Доработки по главной странице, итерация 7
This commit is contained in:
parent
9083c29a26
commit
7b282e4435
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10.5 0C4.7025 0 0 4.7025 0 10.5C0 16.2975 4.7025 21 10.5 21C16.2975 21 21 16.2975 21 10.5C21 4.7025 16.2975 0 10.5 0Z" fill="#0072A8"/>
|
||||||
|
<path d="M14.9408 10.8861C17.5638 10.1129 16.4426 7.5531 14.4975 5.99692C11.2871 3.42913 6.34431 4.29467 4.15568 7.60179C2.55446 10.0205 3.91316 14.0958 7.93359 15.8C11.1489 17.1644 15.0183 16.6755 17.518 13.0723C14.7609 12.8199 11.7443 12.0994 10.479 10.8592C9.31615 9.71843 10.1342 8.08276 11.2275 8.11555" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M15.6835 14.8467C8.77668 15.0415 6.60893 11.3965 6.53935 9.88602C6.43797 7.6879 9.78154 5.44506 14.8784 10.8181" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 808 B |
|
|
@ -3,13 +3,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 { DatePicker } from "@/components/ui/date-picker";
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
import {
|
import {
|
||||||
Carousel,
|
Carousel,
|
||||||
CarouselContent,
|
CarouselContent,
|
||||||
|
|
@ -20,6 +13,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";
|
||||||
|
|
||||||
const yacht = {
|
const yacht = {
|
||||||
name: "Яхта",
|
name: "Яхта",
|
||||||
|
|
@ -58,6 +52,22 @@ export default function FeaturedYacht() {
|
||||||
<div className="flex flex-col lg:flex-row gap-11 px-6 py-10">
|
<div className="flex flex-col lg:flex-row gap-11 px-6 py-10">
|
||||||
{/* Left side - Yacht details and images */}
|
{/* Left side - Yacht details and images */}
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
|
{/* Promoted banner - Mobile only */}
|
||||||
|
<div
|
||||||
|
className="text-white flex items-center justify-center py-2 rounded-full text-center mb-6 relative lg:hidden"
|
||||||
|
style={{
|
||||||
|
backgroundImage:
|
||||||
|
"url(/images/badge-bg.jpg)",
|
||||||
|
backgroundSize: "cover",
|
||||||
|
backgroundPosition: "center",
|
||||||
|
backgroundRepeat: "no-repeat",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="text-xs font-medium relative z-10">
|
||||||
|
Заметнее других — бронируют быстрее
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Header with yacht name and length */}
|
{/* Header with yacht name and length */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<h2 className="text-3xl font-bold">
|
<h2 className="text-3xl font-bold">
|
||||||
|
|
@ -101,18 +111,21 @@ export default function FeaturedYacht() {
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Image
|
<Image
|
||||||
src={thumb}
|
src={thumb}
|
||||||
alt={`${yacht.name} view ${
|
alt={`${
|
||||||
idx + 1
|
yacht.name
|
||||||
}`}
|
} view ${idx + 1}`}
|
||||||
width={80}
|
width={80}
|
||||||
height={60}
|
height={60}
|
||||||
className={`w-20 h-16 object-cover rounded-[8px] cursor-pointer border-2 transition-all ${
|
className={`w-20 h-16 object-cover rounded-[8px] cursor-pointer border-2 transition-all ${
|
||||||
selectedImage === thumb
|
selectedImage ===
|
||||||
|
thumb
|
||||||
? "border-[#008299]"
|
? "border-[#008299]"
|
||||||
: "border-gray-200 hover:border-gray-400"
|
: "border-gray-200 hover:border-gray-400"
|
||||||
}`}
|
}`}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleThumbnailClick(thumb)
|
handleThumbnailClick(
|
||||||
|
thumb
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -126,7 +139,8 @@ export default function FeaturedYacht() {
|
||||||
|
|
||||||
{/* Promoted badge */}
|
{/* Promoted badge */}
|
||||||
{yacht.isPromoted && (
|
{yacht.isPromoted && (
|
||||||
<div className="flex items-center gap-2 text-sm text-gray-600">
|
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||||
|
<Icon size={21} name="ad" />
|
||||||
<span>
|
<span>
|
||||||
Это объявление продвигается.{" "}
|
Это объявление продвигается.{" "}
|
||||||
<span className="underline cursor-pointer">
|
<span className="underline cursor-pointer">
|
||||||
|
|
@ -140,9 +154,9 @@ export default function FeaturedYacht() {
|
||||||
{/* Right side - Booking form */}
|
{/* Right side - Booking form */}
|
||||||
<div className="min-w-[296px] flex flex-col justify-between">
|
<div className="min-w-[296px] flex flex-col justify-between">
|
||||||
<div>
|
<div>
|
||||||
{/* Promoted banner */}
|
{/* Promoted banner - Desktop only */}
|
||||||
<div
|
<div
|
||||||
className="text-white flex items-center justify-center py-2 rounded-full text-center mb-6 relative"
|
className="text-white flex items-center justify-center py-2 rounded-full text-center mb-6 relative hidden lg:flex"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
"url(/images/badge-bg.jpg)",
|
"url(/images/badge-bg.jpg)",
|
||||||
|
|
@ -170,34 +184,13 @@ export default function FeaturedYacht() {
|
||||||
{/* Booking form */}
|
{/* Booking form */}
|
||||||
<div className="space-y-5 mb-8">
|
<div className="space-y-5 mb-8">
|
||||||
<div>
|
<div>
|
||||||
<DatePicker />
|
<DatePicker showIcon={false} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<DatePicker />
|
<DatePicker showIcon={false} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Select>
|
<GuestPicker showIcon={false} />
|
||||||
<SelectTrigger className="w-full h-[64px]">
|
|
||||||
<SelectValue placeholder="1 гость" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="1">
|
|
||||||
1 гость
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="2">
|
|
||||||
2 гостя
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="3">
|
|
||||||
3 гостя
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="4">
|
|
||||||
4 гостя
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="5+">
|
|
||||||
5+ гостей
|
|
||||||
</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ const yachts = [
|
||||||
feet: "7 Футов",
|
feet: "7 Футов",
|
||||||
img: "/images/yachts/yacht1.jpg",
|
img: "/images/yachts/yacht1.jpg",
|
||||||
bestOfferText: "🔥 Лучшее предложение",
|
bestOfferText: "🔥 Лучшее предложение",
|
||||||
|
colorPrice: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Яхта",
|
name: "Яхта",
|
||||||
|
|
@ -92,7 +93,8 @@ export default function YachtGrid() {
|
||||||
<div
|
<div
|
||||||
className="flex w-full items-center justify-center text-white px-4 py-2 text-sm font-medium bg-cover bg-center bg-no-repeat"
|
className="flex w-full items-center justify-center text-white px-4 py-2 text-sm font-medium bg-cover bg-center bg-no-repeat"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: "url('/images/best-yacht-bg.jpg')",
|
backgroundImage:
|
||||||
|
"url('/images/best-yacht-bg.jpg')",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
|
|
@ -127,24 +129,41 @@ export default function YachtGrid() {
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<div className="flex justify-between items-start mb-1">
|
<div className="flex justify-between gap-4">
|
||||||
<h3 className="font-bold text-l">
|
{/* Левая колонка - название и длина */}
|
||||||
{yacht.name}
|
<div className="space-y-2">
|
||||||
</h3>
|
<h3 className="font-bold text-l">
|
||||||
<div className="text-right">
|
{yacht.name}
|
||||||
<p className="text-l">
|
</h3>
|
||||||
{yacht.price}
|
<div className="flex items-center gap-1 text-sm">
|
||||||
</p>
|
<Icon size={16} name="width" />
|
||||||
|
<span>{yacht.length}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between text-gray-600">
|
{/* Правая колонка - цена и футы */}
|
||||||
<div className="flex items-center gap-1">
|
<div className="space-y-2">
|
||||||
<Icon size={16} name="width" />
|
<div className="w-fit">
|
||||||
<span>{yacht.length}</span>
|
{yacht.colorPrice ? (
|
||||||
</div>
|
<p
|
||||||
<div className="flex items-center gap-1">
|
style={{
|
||||||
<Icon size={16} name="anchor" />
|
background:
|
||||||
<span>{yacht.feet}</span>
|
"linear-gradient(90deg, #008299 0%, #7E8FFF 100%)",
|
||||||
|
}}
|
||||||
|
className="text-l font-bold text-white pl-2 pr-3 rounded-t-[5px] rounded-bl-[10px] rounded-br-[30px]"
|
||||||
|
>
|
||||||
|
{yacht.price}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p className="w-fit text-l">
|
||||||
|
{yacht.price}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1 text-sm">
|
||||||
|
<Icon size={16} name="anchor" />
|
||||||
|
<span>{yacht.feet}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,10 @@ import { ChevronUp, ChevronDown } from "lucide-react";
|
||||||
interface GuestPickerProps {
|
interface GuestPickerProps {
|
||||||
onApply?: (adults: number, children: number) => void;
|
onApply?: (adults: number, children: number) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
showIcon?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GuestPicker: React.FC<GuestPickerProps> = ({ onApply }) => {
|
export const GuestPicker: React.FC<GuestPickerProps> = ({ onApply, showIcon = true }) => {
|
||||||
const [adults, setAdults] = useState<number>(0);
|
const [adults, setAdults] = useState<number>(0);
|
||||||
const [children, setChildren] = useState<number>(0);
|
const [children, setChildren] = useState<number>(0);
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||||
|
|
@ -48,11 +49,13 @@ export const GuestPicker: React.FC<GuestPickerProps> = ({ onApply }) => {
|
||||||
className="h-[64px] px-4 w-full justify-between"
|
className="h-[64px] px-4 w-full justify-between"
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Icon
|
{showIcon && (
|
||||||
name="people"
|
<Icon
|
||||||
className="mr-3"
|
name="people"
|
||||||
/>
|
className="mr-3"
|
||||||
<span>{getDisplayText()}</span>
|
/>
|
||||||
|
)}
|
||||||
|
<span className="font-normal">{getDisplayText()}</span>
|
||||||
</div>
|
</div>
|
||||||
{isOpen ? (
|
{isOpen ? (
|
||||||
<ChevronUp className="h-4 w-4" />
|
<ChevronUp className="h-4 w-4" />
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { ru } from "date-fns/locale";
|
import { ru } from "date-fns/locale";
|
||||||
import { ChevronDownIcon } from "lucide-react";
|
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
|
|
@ -14,7 +14,11 @@ import {
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import Icon from "./icon";
|
import Icon from "./icon";
|
||||||
|
|
||||||
export function DatePicker() {
|
interface DatePickerProps {
|
||||||
|
showIcon?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DatePicker({ showIcon = true }: DatePickerProps) {
|
||||||
const [date, setDate] = React.useState<Date>();
|
const [date, setDate] = React.useState<Date>();
|
||||||
const [departureTime, setDepartureTime] = React.useState("12:00");
|
const [departureTime, setDepartureTime] = React.useState("12:00");
|
||||||
const [arrivalTime, setArrivalTime] = React.useState("13:00");
|
const [arrivalTime, setArrivalTime] = React.useState("13:00");
|
||||||
|
|
@ -31,13 +35,29 @@ export function DatePicker() {
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
data-empty={!date}
|
data-empty={!date}
|
||||||
className="w-full h-[64px] justify-start text-left font-normal"
|
className="w-full h-[64px] justify-between text-left font-normal"
|
||||||
>
|
>
|
||||||
<Icon name="calendar" className="w-4 h-4 text-brand mr-2" />
|
<div className="flex items-center">
|
||||||
{date ? (
|
{showIcon && (
|
||||||
format(date, `d MMMM, ${departureTime} - ${arrivalTime}`, { locale: ru })
|
<Icon
|
||||||
|
name="calendar"
|
||||||
|
className="w-4 h-4 text-brand mr-2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{date ? (
|
||||||
|
format(
|
||||||
|
date,
|
||||||
|
`d MMMM, ${departureTime} - ${arrivalTime}`,
|
||||||
|
{ locale: ru }
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<span>Выберите дату и время</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{open ? (
|
||||||
|
<ChevronUpIcon className="w-4 h-4" />
|
||||||
) : (
|
) : (
|
||||||
<span>Выберите дату и время</span>
|
<ChevronDownIcon className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
|
@ -79,37 +99,32 @@ export function DatePicker() {
|
||||||
|
|
||||||
{/* Поля времени */}
|
{/* Поля времени */}
|
||||||
<div className="flex gap-3 mb-4">
|
<div className="flex gap-3 mb-4">
|
||||||
<div className="flex-1">
|
<div className="relative w-full">
|
||||||
<div className="relative">
|
<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">
|
||||||
<input
|
Выход
|
||||||
type="time"
|
</label>
|
||||||
value={departureTime}
|
<ChevronDownIcon className="absolute right-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500 pointer-events-none" />
|
||||||
onChange={(e) =>
|
<input
|
||||||
setDepartureTime(e.target.value)
|
type="time"
|
||||||
}
|
value={departureTime}
|
||||||
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"
|
onChange={(e) =>
|
||||||
/>
|
setDepartureTime(e.target.value)
|
||||||
<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">
|
}
|
||||||
Выход
|
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"
|
||||||
</label>
|
/>
|
||||||
<ChevronDownIcon className="absolute right-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500 pointer-events-none" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
|
||||||
<div className="relative">
|
<div className="relative w-full">
|
||||||
<input
|
<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">
|
||||||
type="time"
|
Заход
|
||||||
value={arrivalTime}
|
</label>
|
||||||
onChange={(e) =>
|
<ChevronDownIcon className="absolute right-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500 pointer-events-none" />
|
||||||
setArrivalTime(e.target.value)
|
<input
|
||||||
}
|
type="time"
|
||||||
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"
|
value={arrivalTime}
|
||||||
/>
|
onChange={(e) => setArrivalTime(e.target.value)}
|
||||||
<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">
|
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"
|
||||||
Заход
|
/>
|
||||||
</label>
|
|
||||||
<ChevronDownIcon className="absolute right-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500 pointer-events-none" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import StarIcon from "../../../public/images/icons/star.svg";
|
||||||
import VkIcon from "../../../public/images/icons/vk.svg";
|
import VkIcon from "../../../public/images/icons/vk.svg";
|
||||||
import DzenIcon from "../../../public/images/icons/dzen.svg";
|
import DzenIcon from "../../../public/images/icons/dzen.svg";
|
||||||
import TgIcon from "../../../public/images/icons/tg.svg";
|
import TgIcon from "../../../public/images/icons/tg.svg";
|
||||||
|
import AdIcon from "../../../public/images/icons/ad.svg";
|
||||||
|
|
||||||
// Объект с иконками для удобного доступа
|
// Объект с иконками для удобного доступа
|
||||||
const icons = {
|
const icons = {
|
||||||
|
|
@ -31,6 +32,7 @@ const icons = {
|
||||||
vk: VkIcon,
|
vk: VkIcon,
|
||||||
dzen: DzenIcon,
|
dzen: DzenIcon,
|
||||||
tg: TgIcon,
|
tg: TgIcon,
|
||||||
|
ad: AdIcon,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IconName =
|
export type IconName =
|
||||||
|
|
@ -46,7 +48,8 @@ export type IconName =
|
||||||
| "star"
|
| "star"
|
||||||
| "vk"
|
| "vk"
|
||||||
| "dzen"
|
| "dzen"
|
||||||
| "tg";
|
| "tg"
|
||||||
|
| "ad";
|
||||||
|
|
||||||
export interface IconProps
|
export interface IconProps
|
||||||
extends Omit<SVGProps<SVGSVGElement>, "name" | "preserveAspectRatio"> {
|
extends Omit<SVGProps<SVGSVGElement>, "name" | "preserveAspectRatio"> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue