Доработки по главной странице, итерация 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";
|
||||
|
||||
export default function Hero() {
|
||||
const [location, setLocation] = useState("");
|
||||
const [guests, setGuests] = useState("");
|
||||
|
||||
return (
|
||||
|
|
@ -54,37 +53,15 @@ export default function Hero() {
|
|||
<div className="flex flex-col md:flex-row gap-4">
|
||||
{/* Локация */}
|
||||
<div className="flex-1">
|
||||
<Select
|
||||
value={location}
|
||||
onValueChange={setLocation}
|
||||
>
|
||||
<SelectTrigger className="w-full h-[64px] px-4">
|
||||
<div className="flex items-center">
|
||||
<Icon
|
||||
name="map"
|
||||
className="w-5 h-5 text-brand mr-3"
|
||||
/>
|
||||
<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>
|
||||
<Button variant="outline" className="w-full h-[64px] px-4 justify-start">
|
||||
<div className="flex items-center">
|
||||
<Icon
|
||||
name="map"
|
||||
className="w-5 h-5 text-brand mr-3"
|
||||
/>
|
||||
<span className="font-normal">Балаклава</span>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Дата и время */}
|
||||
|
|
@ -143,7 +120,7 @@ export default function Hero() {
|
|||
</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>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export default function VideoSection() {
|
|||
alt="Яхта SALVADOR на море"
|
||||
width={800}
|
||||
height={600}
|
||||
className="w-full h-auto"
|
||||
className="w-full h-auto cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const yachts = [
|
|||
length: "12 метров",
|
||||
price: "от 12 500 ₽ / час",
|
||||
feet: "7 Футов",
|
||||
img: "/images/yacht.jpg",
|
||||
img: "/images/yachts/yacht1.jpg",
|
||||
badge: "Быстрая бронь",
|
||||
},
|
||||
{
|
||||
|
|
@ -25,7 +25,7 @@ const yachts = [
|
|||
length: "14 метров",
|
||||
price: "от 26 400 ₽ / час",
|
||||
feet: "7 Футов",
|
||||
img: "/images/yacht.jpg",
|
||||
img: "/images/yachts/yacht2.jpg",
|
||||
badge: "По запросу",
|
||||
},
|
||||
{
|
||||
|
|
@ -33,7 +33,7 @@ const yachts = [
|
|||
length: "22 метра",
|
||||
price: "от 48 000 ₽ / час",
|
||||
feet: "7 Футов",
|
||||
img: "/images/yacht.jpg",
|
||||
img: "/images/yachts/yacht3.jpg",
|
||||
badge: "По запросу",
|
||||
},
|
||||
{
|
||||
|
|
@ -41,7 +41,7 @@ const yachts = [
|
|||
length: "13.6 метров",
|
||||
price: "от 17 400 ₽ / час",
|
||||
feet: "7 Футов",
|
||||
img: "/images/yacht.jpg",
|
||||
img: "/images/yachts/yacht4.jpg",
|
||||
badge: "По запросу",
|
||||
},
|
||||
{
|
||||
|
|
@ -49,7 +49,7 @@ const yachts = [
|
|||
length: "13 метров",
|
||||
price: "от 14 400 ₽ / час",
|
||||
feet: "7 Футов",
|
||||
img: "/images/yacht.jpg",
|
||||
img: "/images/yachts/yacht5.jpg",
|
||||
badge: "По запросу",
|
||||
},
|
||||
{
|
||||
|
|
@ -57,7 +57,7 @@ const yachts = [
|
|||
length: "12 метров",
|
||||
price: "от 12 480 ₽ / час",
|
||||
feet: "7 Футов",
|
||||
img: "/images/yacht.jpg",
|
||||
img: "/images/yachts/yacht6.jpg",
|
||||
badge: "По запросу",
|
||||
},
|
||||
];
|
||||
|
|
@ -238,13 +238,18 @@ export default function YachtGrid() {
|
|||
</div>
|
||||
|
||||
{/* 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>
|
||||
|
||||
{/* Total price */}
|
||||
<div className="flex justify-between items-center text-l font-bold text-gray-800">
|
||||
<span className="font-normal">Итого:</span>
|
||||
<div className="flex justify-between items-center text-l mt-6 font-bold text-gray-800">
|
||||
<span className="font-normal">
|
||||
Итого:
|
||||
</span>
|
||||
<span>
|
||||
{featuredYacht.totalPrice}
|
||||
</span>
|
||||
|
|
@ -276,14 +281,7 @@ export default function YachtGrid() {
|
|||
{/* Badge Overlay */}
|
||||
<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">
|
||||
<Icon
|
||||
size={16}
|
||||
name={
|
||||
idx === 0
|
||||
? "logo"
|
||||
: "restart"
|
||||
}
|
||||
/>
|
||||
<Icon size={16} name="restart" />
|
||||
<span>{yacht.badge}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
--color-brand: var(--brand);
|
||||
--color-brand-hover: var(--brand-hover);
|
||||
--color-brand-active: var(--brand-active);
|
||||
--color-brand-gradient: var(--brand-gradient);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
|
|
@ -87,6 +88,7 @@
|
|||
--brand: oklch(0.5588 0.0992 215.93);
|
||||
--brand-hover: oklch(0.4588 0.0992 215.93); /* #006B7A */
|
||||
--brand-active: oklch(0.3588 0.0992 215.93);
|
||||
--brand-gradient: linear-gradient(90deg, #0072A8 0%, #0598DE 100%);
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
|
|
|||
|
|
@ -1,57 +1,65 @@
|
|||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import * as React from "react";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
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",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground border hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground border hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background border hover:bg-accent hover:text-accent-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground border hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 rounded-full px-3 text-xs",
|
||||
lg: "h-10 rounded-full px-8",
|
||||
icon: "h-9 w-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
"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: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground border hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground border hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background border hover:bg-accent hover:text-accent-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground border hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
gradient: "text-white border-0 hover:opacity-90",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 rounded-full px-3 text-xs",
|
||||
lg: "h-10 rounded-full px-8",
|
||||
icon: "h-9 w-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Button.displayName = "Button"
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "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 { format } from "date-fns";
|
||||
import { ru } from "date-fns/locale";
|
||||
import { ChevronDownIcon } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
|
|
@ -14,6 +16,13 @@ import Icon from "./icon";
|
|||
|
||||
export function DatePicker() {
|
||||
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 (
|
||||
<Popover>
|
||||
|
|
@ -21,21 +30,93 @@ export function DatePicker() {
|
|||
<Button
|
||||
variant="outline"
|
||||
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
|
||||
name="calendar"
|
||||
className="w-4 h-4 text-brand mr-2"
|
||||
/>
|
||||
<Icon name="calendar" className="w-4 h-4 text-brand mr-2" />
|
||||
{date ? (
|
||||
format(date, "dd.MM.yyyy")
|
||||
format(date, `dd.MM, ${departureTime} - ${arrivalTime}`)
|
||||
) : (
|
||||
<span>Выберите дату</span>
|
||||
<span>Выберите дату и время</span>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar mode="single" selected={date} onSelect={setDate} />
|
||||
<PopoverContent className="w-[360px] p-0 bg-white rounded-[20px] shadow-lg">
|
||||
<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>
|
||||
</Popover>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue