Доработки по главной странице, итерация 2
This commit is contained in:
parent
3b2a71bace
commit
6ee8a8f624
Binary file not shown.
|
After Width: | Height: | Size: 213 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 232 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 171 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 188 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
|
|
@ -15,7 +15,6 @@ import { DatePicker } from "@/components/ui/date-picker";
|
||||||
import Icon from "@/components/ui/icon";
|
import Icon from "@/components/ui/icon";
|
||||||
|
|
||||||
export default function Hero() {
|
export default function Hero() {
|
||||||
const [location, setLocation] = useState("");
|
|
||||||
const [guests, setGuests] = useState("");
|
const [guests, setGuests] = useState("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -54,37 +53,15 @@ export default function Hero() {
|
||||||
<div className="flex flex-col md:flex-row gap-4">
|
<div className="flex flex-col md:flex-row gap-4">
|
||||||
{/* Локация */}
|
{/* Локация */}
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<Select
|
<Button variant="outline" className="w-full h-[64px] px-4 justify-start">
|
||||||
value={location}
|
<div className="flex items-center">
|
||||||
onValueChange={setLocation}
|
<Icon
|
||||||
>
|
name="map"
|
||||||
<SelectTrigger className="w-full h-[64px] px-4">
|
className="w-5 h-5 text-brand mr-3"
|
||||||
<div className="flex items-center">
|
/>
|
||||||
<Icon
|
<span className="font-normal">Балаклава</span>
|
||||||
name="map"
|
</div>
|
||||||
className="w-5 h-5 text-brand mr-3"
|
</Button>
|
||||||
/>
|
|
||||||
<SelectValue placeholder="Выберите локацию" />
|
|
||||||
</div>
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="balaklava">
|
|
||||||
Балаклава
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="sevastopol">
|
|
||||||
Севастополь
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="yalta">
|
|
||||||
Ялта
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="sudak">
|
|
||||||
Судак
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="kerch">
|
|
||||||
Керчь
|
|
||||||
</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Дата и время */}
|
{/* Дата и время */}
|
||||||
|
|
@ -143,7 +120,7 @@ export default function Hero() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Кнопка поиска */}
|
{/* Кнопка поиска */}
|
||||||
<Button className="bg-brand hover:bg-brand-hover active:bg-brand-active font-bold text-white h-[64px] w-[176px] px-8">
|
<Button variant="gradient" className="font-bold text-white h-[64px] w-[176px] px-8">
|
||||||
Найти
|
Найти
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export default function VideoSection() {
|
||||||
alt="Яхта SALVADOR на море"
|
alt="Яхта SALVADOR на море"
|
||||||
width={800}
|
width={800}
|
||||||
height={600}
|
height={600}
|
||||||
className="w-full h-auto"
|
className="w-full h-auto cursor-pointer"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ const yachts = [
|
||||||
length: "12 метров",
|
length: "12 метров",
|
||||||
price: "от 12 500 ₽ / час",
|
price: "от 12 500 ₽ / час",
|
||||||
feet: "7 Футов",
|
feet: "7 Футов",
|
||||||
img: "/images/yacht.jpg",
|
img: "/images/yachts/yacht1.jpg",
|
||||||
badge: "Быстрая бронь",
|
badge: "Быстрая бронь",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -25,7 +25,7 @@ const yachts = [
|
||||||
length: "14 метров",
|
length: "14 метров",
|
||||||
price: "от 26 400 ₽ / час",
|
price: "от 26 400 ₽ / час",
|
||||||
feet: "7 Футов",
|
feet: "7 Футов",
|
||||||
img: "/images/yacht.jpg",
|
img: "/images/yachts/yacht2.jpg",
|
||||||
badge: "По запросу",
|
badge: "По запросу",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -33,7 +33,7 @@ const yachts = [
|
||||||
length: "22 метра",
|
length: "22 метра",
|
||||||
price: "от 48 000 ₽ / час",
|
price: "от 48 000 ₽ / час",
|
||||||
feet: "7 Футов",
|
feet: "7 Футов",
|
||||||
img: "/images/yacht.jpg",
|
img: "/images/yachts/yacht3.jpg",
|
||||||
badge: "По запросу",
|
badge: "По запросу",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -41,7 +41,7 @@ const yachts = [
|
||||||
length: "13.6 метров",
|
length: "13.6 метров",
|
||||||
price: "от 17 400 ₽ / час",
|
price: "от 17 400 ₽ / час",
|
||||||
feet: "7 Футов",
|
feet: "7 Футов",
|
||||||
img: "/images/yacht.jpg",
|
img: "/images/yachts/yacht4.jpg",
|
||||||
badge: "По запросу",
|
badge: "По запросу",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -49,7 +49,7 @@ const yachts = [
|
||||||
length: "13 метров",
|
length: "13 метров",
|
||||||
price: "от 14 400 ₽ / час",
|
price: "от 14 400 ₽ / час",
|
||||||
feet: "7 Футов",
|
feet: "7 Футов",
|
||||||
img: "/images/yacht.jpg",
|
img: "/images/yachts/yacht5.jpg",
|
||||||
badge: "По запросу",
|
badge: "По запросу",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -57,7 +57,7 @@ const yachts = [
|
||||||
length: "12 метров",
|
length: "12 метров",
|
||||||
price: "от 12 480 ₽ / час",
|
price: "от 12 480 ₽ / час",
|
||||||
feet: "7 Футов",
|
feet: "7 Футов",
|
||||||
img: "/images/yacht.jpg",
|
img: "/images/yachts/yacht6.jpg",
|
||||||
badge: "По запросу",
|
badge: "По запросу",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -238,13 +238,18 @@ export default function YachtGrid() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Book button */}
|
{/* Book button */}
|
||||||
<Button className="w-full text-white py-4 text-lg font-bold mb-6 rounded-lg shadow-md">
|
<Button
|
||||||
|
variant="gradient"
|
||||||
|
className="font-bold text-white h-[64px] w-full px-8"
|
||||||
|
>
|
||||||
Забронировать
|
Забронировать
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Total price */}
|
{/* Total price */}
|
||||||
<div className="flex justify-between items-center text-l font-bold text-gray-800">
|
<div className="flex justify-between items-center text-l mt-6 font-bold text-gray-800">
|
||||||
<span className="font-normal">Итого:</span>
|
<span className="font-normal">
|
||||||
|
Итого:
|
||||||
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{featuredYacht.totalPrice}
|
{featuredYacht.totalPrice}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -276,14 +281,7 @@ export default function YachtGrid() {
|
||||||
{/* Badge Overlay */}
|
{/* Badge Overlay */}
|
||||||
<div className="absolute top-3 left-3">
|
<div className="absolute top-3 left-3">
|
||||||
<div className="flex items-center justify-center bg-black/40 text-white px-3 py-1 rounded-lg text-sm flex items-center gap-1">
|
<div className="flex items-center justify-center bg-black/40 text-white px-3 py-1 rounded-lg text-sm flex items-center gap-1">
|
||||||
<Icon
|
<Icon size={16} name="restart" />
|
||||||
size={16}
|
|
||||||
name={
|
|
||||||
idx === 0
|
|
||||||
? "logo"
|
|
||||||
: "restart"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<span>{yacht.badge}</span>
|
<span>{yacht.badge}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@
|
||||||
--color-brand: var(--brand);
|
--color-brand: var(--brand);
|
||||||
--color-brand-hover: var(--brand-hover);
|
--color-brand-hover: var(--brand-hover);
|
||||||
--color-brand-active: var(--brand-active);
|
--color-brand-active: var(--brand-active);
|
||||||
|
--color-brand-gradient: var(--brand-gradient);
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
|
|
@ -87,6 +88,7 @@
|
||||||
--brand: oklch(0.5588 0.0992 215.93);
|
--brand: oklch(0.5588 0.0992 215.93);
|
||||||
--brand-hover: oklch(0.4588 0.0992 215.93); /* #006B7A */
|
--brand-hover: oklch(0.4588 0.0992 215.93); /* #006B7A */
|
||||||
--brand-active: oklch(0.3588 0.0992 215.93);
|
--brand-active: oklch(0.3588 0.0992 215.93);
|
||||||
|
--brand-gradient: linear-gradient(90deg, #0072A8 0%, #0598DE 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,65 @@
|
||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import { Slot } from "@radix-ui/react-slot"
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-full text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-full text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default:
|
default:
|
||||||
"bg-primary text-primary-foreground border hover:bg-primary/90",
|
"bg-primary text-primary-foreground border hover:bg-primary/90",
|
||||||
destructive:
|
destructive:
|
||||||
"bg-destructive text-destructive-foreground border hover:bg-destructive/90",
|
"bg-destructive text-destructive-foreground border hover:bg-destructive/90",
|
||||||
outline:
|
outline:
|
||||||
"border border-input bg-background border hover:bg-accent hover:text-accent-foreground",
|
"border border-input bg-background border hover:bg-accent hover:text-accent-foreground",
|
||||||
secondary:
|
secondary:
|
||||||
"bg-secondary text-secondary-foreground border hover:bg-secondary/80",
|
"bg-secondary text-secondary-foreground border hover:bg-secondary/80",
|
||||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
link: "text-primary underline-offset-4 hover:underline",
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
},
|
gradient: "text-white border-0 hover:opacity-90",
|
||||||
size: {
|
},
|
||||||
default: "h-9 px-4 py-2",
|
size: {
|
||||||
sm: "h-8 rounded-full px-3 text-xs",
|
default: "h-9 px-4 py-2",
|
||||||
lg: "h-10 rounded-full px-8",
|
sm: "h-8 rounded-full px-3 text-xs",
|
||||||
icon: "h-9 w-9",
|
lg: "h-10 rounded-full px-8",
|
||||||
},
|
icon: "h-9 w-9",
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
},
|
||||||
variant: "default",
|
defaultVariants: {
|
||||||
size: "default",
|
variant: "default",
|
||||||
},
|
size: "default",
|
||||||
}
|
},
|
||||||
)
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export interface ButtonProps
|
export interface ButtonProps
|
||||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
VariantProps<typeof buttonVariants> {
|
VariantProps<typeof buttonVariants> {
|
||||||
asChild?: boolean
|
asChild?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||||
const Comp = asChild ? Slot : "button"
|
const Comp = asChild ? Slot : "button";
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
className={cn(buttonVariants({ variant, size, className }))}
|
|
||||||
ref={ref}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Button.displayName = "Button"
|
|
||||||
|
|
||||||
export { Button, buttonVariants }
|
// Inline стили для градиента
|
||||||
|
const gradientStyle = variant === "gradient" ? {
|
||||||
|
background: "linear-gradient(90deg, #0072A8 0%, #0598DE 100%)",
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
style={gradientStyle}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Button.displayName = "Button";
|
||||||
|
|
||||||
|
export { Button, buttonVariants };
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
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 { ChevronDownIcon } 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,6 +16,13 @@ import Icon from "./icon";
|
||||||
|
|
||||||
export function DatePicker() {
|
export function DatePicker() {
|
||||||
const [date, setDate] = React.useState<Date>();
|
const [date, setDate] = React.useState<Date>();
|
||||||
|
const [departureTime, setDepartureTime] = React.useState("12:00");
|
||||||
|
const [arrivalTime, setArrivalTime] = React.useState("13:00");
|
||||||
|
|
||||||
|
const handleApply = () => {
|
||||||
|
// Логика применения выбранных даты и времени
|
||||||
|
console.log("Применено:", { date, departureTime, arrivalTime });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
|
|
@ -21,21 +30,93 @@ export function DatePicker() {
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
data-empty={!date}
|
data-empty={!date}
|
||||||
className="data-[empty=true]:text-muted-foreground w-full h-[64px] justify-start text-left font-normal"
|
className="w-full h-[64px] justify-start text-left font-normal"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon name="calendar" className="w-4 h-4 text-brand mr-2" />
|
||||||
name="calendar"
|
|
||||||
className="w-4 h-4 text-brand mr-2"
|
|
||||||
/>
|
|
||||||
{date ? (
|
{date ? (
|
||||||
format(date, "dd.MM.yyyy")
|
format(date, `dd.MM, ${departureTime} - ${arrivalTime}`)
|
||||||
) : (
|
) : (
|
||||||
<span>Выберите дату</span>
|
<span>Выберите дату и время</span>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-auto p-0">
|
<PopoverContent className="w-[360px] p-0 bg-white rounded-[20px] shadow-lg">
|
||||||
<Calendar mode="single" selected={date} onSelect={setDate} />
|
<div className="p-4 w-full">
|
||||||
|
{/* Календарь */}
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={date}
|
||||||
|
onSelect={setDate}
|
||||||
|
className="mb-4 "
|
||||||
|
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",
|
||||||
|
week: "mt-2 flex w-full",
|
||||||
|
today: "bg-gray-100 text-gray-900",
|
||||||
|
outside: "text-gray-300",
|
||||||
|
disabled: "text-gray-400 cursor-not-allowed",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Поля времени */}
|
||||||
|
<div className="flex gap-3 mb-4">
|
||||||
|
<div className="flex-1">
|
||||||
|
<label className="block text-xs text-gray-500 mb-1">
|
||||||
|
Выход
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type="time"
|
||||||
|
value={departureTime}
|
||||||
|
onChange={(e) =>
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
<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 className="flex-1">
|
||||||
|
<label className="block text-xs text-gray-500 mb-1">
|
||||||
|
Заход
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type="time"
|
||||||
|
value={arrivalTime}
|
||||||
|
onChange={(e) =>
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
{/* Кнопка Применить */}
|
||||||
|
<Button
|
||||||
|
onClick={handleApply}
|
||||||
|
variant="gradient"
|
||||||
|
className="font-bold text-white h-[44px] w-full px-8"
|
||||||
|
>
|
||||||
|
Применить
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue