changes
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 358 KiB |
|
After Width: | Height: | Size: 323 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
|
@ -3,13 +3,15 @@ import { FC } from "react";
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
w?: string;
|
||||||
|
h?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Chevron: FC<Props> = ({ color = "white" }) => {
|
export const Chevron: FC<Props> = ({ color = "white", w = "20", h = "20" }) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="20"
|
width={w}
|
||||||
height="20"
|
height={h}
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { FC } from "react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
imgUrl: string;
|
||||||
|
name: string;
|
||||||
|
info: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DesignerCard: FC<Props> = ({ className, ...props }) => {
|
||||||
|
return (
|
||||||
|
<article
|
||||||
|
className={cn(
|
||||||
|
"relative bg-[url('/impressions/card-bg.png')] bg-no-repeat h-[326px] overflow-hidden",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/impressions/card-bg-bg.png"
|
||||||
|
className="absolute top-2.5 left-2.5 -z-10 size-full"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<img
|
||||||
|
src={props.imgUrl}
|
||||||
|
alt=""
|
||||||
|
className="flex-[0_0_44%] object-contain overflow-hidden"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex-auto z-20">
|
||||||
|
<h3>{props.name}</h3>
|
||||||
|
<p>{props.info}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { FC } from "react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Container } from "@/components/layout";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Menu } from "../menu";
|
||||||
|
import { Chevron } from "../chevron";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HomeActions: FC<Props> = ({ className }) => {
|
||||||
|
const { t } = useTranslation("home");
|
||||||
|
|
||||||
|
const { title, items } = t("buttons", { returnObjects: true }) as {
|
||||||
|
title: string;
|
||||||
|
items: {
|
||||||
|
text: string;
|
||||||
|
link?: string;
|
||||||
|
blank?: boolean;
|
||||||
|
dropdown?: boolean;
|
||||||
|
items: { text: string; link: string }[];
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(items);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={cn("bg-teritary_container py-10", className)}>
|
||||||
|
<Container>
|
||||||
|
<h2 className="h2 text-center mb-10 !text-on_teritary_container">
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-3 gap-6">
|
||||||
|
{items.map((item) =>
|
||||||
|
!item.dropdown ? (
|
||||||
|
<Link to={item.link ?? ""}>
|
||||||
|
<Button
|
||||||
|
variant={"teritary"}
|
||||||
|
size={"lg"}
|
||||||
|
className="w-full drop-shadow-sm shadow-md text-xl bg-teritary text-on_teritary hover:bg-teritary/90"
|
||||||
|
>
|
||||||
|
{item.text}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Link
|
||||||
|
target={item.blank ? "_blank" : ""}
|
||||||
|
key={title}
|
||||||
|
to={item.link ?? ""}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<Menu
|
||||||
|
dropDownContent={item.items}
|
||||||
|
triggerClassName="w-full"
|
||||||
|
className="!w-full"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
size={"lg"}
|
||||||
|
variant={"teritary"}
|
||||||
|
className="w-full drop-shadow-sm shadow-md text-xl bg-teritary text-on_teritary hover:bg-teritary/90"
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
<Chevron w={"40"} h={"40"} color="white" />
|
||||||
|
</Button>
|
||||||
|
</Menu>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,17 +1,11 @@
|
||||||
import useEmblaCarousel from "embla-carousel-react";
|
import useEmblaCarousel from "embla-carousel-react";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Container } from "@/components/layout";
|
|
||||||
import { useMediaQuery } from "usehooks-ts";
|
import { useMediaQuery } from "usehooks-ts";
|
||||||
import { btns } from "@/data/home/home-hero.data";
|
|
||||||
import { useTranslate } from "@/hooks/use-translate";
|
|
||||||
import { useLangStore } from "@/store/lang";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { HomeTimer } from "./";
|
||||||
|
|
||||||
export const HomeHero: FC = () => {
|
export const HomeHero: FC = () => {
|
||||||
const [embalRef] = useEmblaCarousel();
|
const [embalRef] = useEmblaCarousel();
|
||||||
const lang = useLangStore((state) => state.lang);
|
|
||||||
|
|
||||||
const { t } = useTranslation("home");
|
const { t } = useTranslation("home");
|
||||||
|
|
||||||
|
|
@ -23,38 +17,24 @@ export const HomeHero: FC = () => {
|
||||||
else if (md) return t("banners.md");
|
else if (md) return t("banners.md");
|
||||||
else return t("banners.sm");
|
else return t("banners.sm");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="flex flex-col gap-5">
|
<div>
|
||||||
<div ref={embalRef} className="embla">
|
<section className="flex flex-col gap-5">
|
||||||
<div className="embla__container">
|
<div ref={embalRef} className="embla">
|
||||||
<div className="embla__slide">
|
<div className="embla__container">
|
||||||
<img
|
<div className="embla__slide">
|
||||||
src={getBanner()}
|
<img
|
||||||
alt=""
|
src={getBanner()}
|
||||||
className="size-full object-cover lg:max-h-[600px] lg:min-h-[320px]"
|
alt=""
|
||||||
/>
|
className="size-full object-cover lg:max-h-[600px] lg:min-h-[320px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<Container className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 items-center gap-6 text-2xl">
|
<HomeTimer />
|
||||||
{btns[useTranslate(lang)].data.map(({ title, link, blank }) => (
|
</div>
|
||||||
<Link
|
|
||||||
target={blank ? "_blank" : ""}
|
|
||||||
key={title}
|
|
||||||
to={link}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
size={"lg"}
|
|
||||||
variant={"teritary"}
|
|
||||||
className="w-full drop-shadow-sm shadow-md text-xl bg-teritary text-on_teritary hover:bg-teritary/90"
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</Container>
|
|
||||||
</section>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export const HomeNews: FC = () => {
|
||||||
{lang === "en" ? "News" : "Новости"}
|
{lang === "en" ? "News" : "Новости"}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="grid md:grid-cols-3 grid-cols-1 gap-6">
|
<div className="grid md:grid-cols-3 grid-cols-1 gap-6">
|
||||||
{data?.map((item) => (
|
{data?.slice(0, 3).map((item) => (
|
||||||
<NewsCard {...item} key={item.title} />
|
<NewsCard {...item} key={item.title} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,14 @@ export const HomeTime: FC<Props> = ({ className }) => {
|
||||||
const { data, isPending } = useExhibitionTime();
|
const { data, isPending } = useExhibitionTime();
|
||||||
const { data: contacts } = useHomeContacts();
|
const { data: contacts } = useHomeContacts();
|
||||||
|
|
||||||
|
const translate = useTranslate(lang);
|
||||||
|
|
||||||
if (isPending) return <Loader />;
|
if (isPending) return <Loader />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={cn("bg-surface_high pt-10 pb-20", className)}>
|
<section className={cn("bg-surface_high pt-10 pb-20", className)}>
|
||||||
<Container>
|
<Container>
|
||||||
<h2 className="h2 mb-6">{times[useTranslate(lang)].title}</h2>
|
<h2 className="h2 mb-6">{times[translate].title}</h2>
|
||||||
|
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
<div className="flex flex-col md:flex-row items-center gap-6">
|
<div className="flex flex-col md:flex-row items-center gap-6">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
import { FC, useEffect, useState } from "react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { TimerItem } from "../";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HomeTimer: FC<Props> = ({ className }) => {
|
||||||
|
const { t } = useTranslation("home");
|
||||||
|
|
||||||
|
const [timeLeft, setTimeLeft] = useState({
|
||||||
|
days: "0",
|
||||||
|
hours: "00",
|
||||||
|
minutes: "00",
|
||||||
|
seconds: "00",
|
||||||
|
});
|
||||||
|
const [prevValues, setPrevValues] = useState<Record<string, string>>({});
|
||||||
|
const targetDate = new Date("2025-06-11T08:00:00").getTime();
|
||||||
|
|
||||||
|
// Функция для вычисления оставшегося времени
|
||||||
|
const calculateTimeLeft = () => {
|
||||||
|
const now = new Date().getTime();
|
||||||
|
const diff = targetDate - now;
|
||||||
|
|
||||||
|
if (diff <= 0) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
days: Math.floor(diff / (1000 * 60 * 60 * 24)),
|
||||||
|
hours: Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)),
|
||||||
|
minutes: Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)),
|
||||||
|
seconds: Math.floor((diff % (1000 * 60)) / 1000),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Первоначальный расчет
|
||||||
|
const initialTime = calculateTimeLeft();
|
||||||
|
if (initialTime) {
|
||||||
|
setTimeLeft({
|
||||||
|
days: initialTime.days.toString(),
|
||||||
|
hours: initialTime.hours.toString().padStart(2, "0"),
|
||||||
|
minutes: initialTime.minutes.toString().padStart(2, "0"),
|
||||||
|
seconds: initialTime.seconds.toString().padStart(2, "0"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const timerInterval = setInterval(() => {
|
||||||
|
const newTime = calculateTimeLeft();
|
||||||
|
if (!newTime) {
|
||||||
|
clearInterval(timerInterval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем предыдущие значения перед установкой новых
|
||||||
|
setPrevValues({
|
||||||
|
days: timeLeft.days,
|
||||||
|
hours: timeLeft.hours,
|
||||||
|
minutes: timeLeft.minutes,
|
||||||
|
seconds: timeLeft.seconds,
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeLeft({
|
||||||
|
days: newTime.days.toString(),
|
||||||
|
hours: newTime.hours.toString().padStart(2, "0"),
|
||||||
|
minutes: newTime.minutes.toString().padStart(2, "0"),
|
||||||
|
seconds: newTime.seconds.toString().padStart(2, "0"),
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(timerInterval);
|
||||||
|
}, [targetDate]); // Убрали зависимость от timeLeft
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
days: daysLabel,
|
||||||
|
hours: hoursLabel,
|
||||||
|
minutes: minutesLabel,
|
||||||
|
seconds: secondsLabel,
|
||||||
|
} = t("timer", {
|
||||||
|
returnObjects: true,
|
||||||
|
}) as {
|
||||||
|
title: string;
|
||||||
|
days: string;
|
||||||
|
hours: string;
|
||||||
|
minutes: string;
|
||||||
|
seconds: string;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<section className={cn("container py-10", className)}>
|
||||||
|
<h2 className="h2 text-center mb-10">{title}</h2>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-4 gap-6 place-items-center">
|
||||||
|
<TimerItem
|
||||||
|
value={timeLeft.days}
|
||||||
|
prevValue={prevValues.days || timeLeft.days}
|
||||||
|
label={daysLabel}
|
||||||
|
/>
|
||||||
|
<TimerItem
|
||||||
|
value={timeLeft.hours}
|
||||||
|
prevValue={prevValues.hours || timeLeft.hours}
|
||||||
|
label={hoursLabel}
|
||||||
|
/>
|
||||||
|
<TimerItem
|
||||||
|
value={timeLeft.minutes}
|
||||||
|
prevValue={prevValues.minutes || timeLeft.minutes}
|
||||||
|
label={minutesLabel}
|
||||||
|
/>
|
||||||
|
<TimerItem
|
||||||
|
value={timeLeft.seconds}
|
||||||
|
prevValue={prevValues.seconds || timeLeft.seconds}
|
||||||
|
label={secondsLabel}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -4,3 +4,6 @@ export { HomeAbout } from "./home-about";
|
||||||
export { HomeTheme } from "./home-theme";
|
export { HomeTheme } from "./home-theme";
|
||||||
export { HomeOffers } from "./home-offers";
|
export { HomeOffers } from "./home-offers";
|
||||||
export { HomeSponsors } from "./home-sponsors";
|
export { HomeSponsors } from "./home-sponsors";
|
||||||
|
export { HomeNews } from "./home-news";
|
||||||
|
export { HomeTimer } from "./home-timer";
|
||||||
|
export { HomeActions } from "./home-actions";
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ export { Loader } from "./loader";
|
||||||
export { Tabs } from "./tabs";
|
export { Tabs } from "./tabs";
|
||||||
export { ParticipantItem } from "./participant-item";
|
export { ParticipantItem } from "./participant-item";
|
||||||
export { MediaModal } from "./media-modal";
|
export { MediaModal } from "./media-modal";
|
||||||
|
export { DesignerCard } from "./designer-card";
|
||||||
|
export { TimerItem } from "./timer-item";
|
||||||
|
|
||||||
export * from "./about";
|
export * from "./about";
|
||||||
export * from "./home";
|
export * from "./home";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { FC, useState } from "react";
|
import { FC, PropsWithChildren, useState } from "react";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
|
||||||
import { Chevron } from "./";
|
import { Chevron } from "./";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
@ -9,18 +9,19 @@ import { useUiStore } from "@/store/ui";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
title: string;
|
title?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
triggerClassName?: string;
|
triggerClassName?: string;
|
||||||
|
|
||||||
dropDownContent?: DropDownContent[];
|
dropDownContent?: DropDownContent[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Menu: FC<Props> = ({
|
export const Menu: FC<PropsWithChildren<Props>> = ({
|
||||||
title,
|
title,
|
||||||
dropDownContent,
|
dropDownContent,
|
||||||
color,
|
color,
|
||||||
triggerClassName,
|
triggerClassName,
|
||||||
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const setSheet = useUiStore((state) => state.setSheet);
|
const setSheet = useUiStore((state) => state.setSheet);
|
||||||
|
|
@ -31,7 +32,8 @@ export const Menu: FC<Props> = ({
|
||||||
className={cn("flex items-center gap-2", triggerClassName)}
|
className={cn("flex items-center gap-2", triggerClassName)}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
<Chevron color={color} />
|
{children}
|
||||||
|
{!children && <Chevron color={color} />}
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
|
||||||
<PopoverContent className="w-fit px-0 py-2 cursor-pointer bg-surface_container">
|
<PopoverContent className="w-fit px-0 py-2 cursor-pointer bg-surface_container">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
export const TimerItem = ({
|
||||||
|
value,
|
||||||
|
label,
|
||||||
|
prevValue,
|
||||||
|
}: {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
prevValue: string;
|
||||||
|
}) => (
|
||||||
|
<div className="timer-item flex flex-col items-center">
|
||||||
|
<div className="relative h-14 w-16 overflow-hidden font-bold">
|
||||||
|
<span
|
||||||
|
className={`absolute left-1/2 -translate-x-1/2 black bg-gradient-to-t from-[#F38835] to-[#F1700D] text-transparent bg-clip-text text-5xl leading-none transition-all duration-300 ${
|
||||||
|
prevValue !== value ? " opacity-0" : " opacity-100"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{prevValue}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`absolute left-1/2 -translate-x-1/2 black bg-gradient-to-t from-[#F38835] to-[#F1700D] text-transparent bg-clip-text text-5xl leading-none transition-all duration-300 ${
|
||||||
|
prevValue !== value ? "opacity-100" : "opacity-0"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="label normal uppercase text-xl">{label}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
@ -17,6 +17,11 @@
|
||||||
url(../fonts/Gilroy-Semibold.woff2) format("woff2");
|
url(../fonts/Gilroy-Semibold.woff2) format("woff2");
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Gilroy-Black";
|
||||||
|
src: local("Gilroy-Black"), url(../fonts/Gilroy-Black.woff2) format("woff2");
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
|
|
@ -33,6 +38,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
|
.container {
|
||||||
|
@apply w-full mx-auto max-w-[1240px] px-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-y {
|
||||||
|
@apply py-20;
|
||||||
|
}
|
||||||
|
|
||||||
.normal {
|
.normal {
|
||||||
@apply font-["Gilroy-Regular"] font-normal;
|
@apply font-["Gilroy-Regular"] font-normal;
|
||||||
}
|
}
|
||||||
|
|
@ -41,12 +54,16 @@
|
||||||
@apply font-["Gilroy-Semibold"] font-semibold;
|
@apply font-["Gilroy-Semibold"] font-semibold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.black {
|
||||||
|
@apply font-['Gilroy-Black'];
|
||||||
|
}
|
||||||
|
|
||||||
.h1 {
|
.h1 {
|
||||||
@apply text-[28px] leading-[130%] font-medium;
|
@apply text-[28px] leading-[130%] font-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h2 {
|
.h2 {
|
||||||
@apply md:text-[28px] text-2xl text-[#171717];
|
@apply md:text-[28px] text-[32px] text-[#171717];
|
||||||
}
|
}
|
||||||
|
|
||||||
.p {
|
.p {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,66 @@
|
||||||
"sm": "https://turkmentextile.turkmenexpo.com/app/storage/app/media/surat/ru/s.jpg"
|
"sm": "https://turkmentextile.turkmenexpo.com/app/storage/app/media/surat/ru/s.jpg"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"timer": {
|
||||||
|
"title": "Событие начнется через",
|
||||||
|
|
||||||
|
"days": "дней",
|
||||||
|
"hours": "часов",
|
||||||
|
"minutes": "минут",
|
||||||
|
"seconds": "секунд"
|
||||||
|
},
|
||||||
|
|
||||||
|
"buttons": {
|
||||||
|
"title": "Быстрые действия",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"text": "План выставки",
|
||||||
|
"link": "https://turkmentextile.turkmenexpo.com/app/storage/app/media/Floor%20plan/floor%20plan.pdf",
|
||||||
|
"blank": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Забронировать стенд",
|
||||||
|
"link": "/stend-form"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Список участников",
|
||||||
|
"link": "/participants"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "B2B | B2G встречи",
|
||||||
|
"link": "/B2B-B2G"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Модные показы",
|
||||||
|
"dropdown": true,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"text": "Зарубежных дизайнеров",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Туркменских дизайнеров",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Мастер-классы",
|
||||||
|
"dropdown": true,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"text": "Для дизайнеров",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Для посетителей ",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
"partners": {
|
"partners": {
|
||||||
"title": "Партнёры",
|
"title": "Партнёры",
|
||||||
"button": ""
|
"button": ""
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
BecomeSponsor,
|
BecomeSponsor,
|
||||||
Contacts,
|
Contacts,
|
||||||
Home,
|
Home,
|
||||||
|
Impressions,
|
||||||
Media,
|
Media,
|
||||||
News,
|
News,
|
||||||
NewsInner,
|
NewsInner,
|
||||||
|
|
@ -32,6 +33,7 @@ const router = createBrowserRouter([
|
||||||
{ element: <NewsInner />, path: "news/:id" },
|
{ element: <NewsInner />, path: "news/:id" },
|
||||||
{ element: <Participants />, path: "participants" },
|
{ element: <Participants />, path: "participants" },
|
||||||
{ element: <Media />, path: "media" },
|
{ element: <Media />, path: "media" },
|
||||||
|
{ element: <Impressions />, path: "impressions" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
import {
|
import {
|
||||||
HomeAbout,
|
HomeAbout,
|
||||||
|
HomeActions,
|
||||||
HomeHero,
|
HomeHero,
|
||||||
|
HomeNews,
|
||||||
HomeOffers,
|
HomeOffers,
|
||||||
HomeSponsors,
|
HomeSponsors,
|
||||||
HomeTheme,
|
HomeTheme,
|
||||||
HomeTime,
|
HomeTime,
|
||||||
} from "@/components/shared";
|
} from "@/components/shared";
|
||||||
import { HomeNews } from "@/components/shared/home/home-news";
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-20">
|
<div className="flex flex-col gap-20">
|
||||||
<HomeHero />
|
<HomeHero />
|
||||||
|
<HomeActions />
|
||||||
<HomeAbout />
|
<HomeAbout />
|
||||||
<HomeSponsors />
|
<HomeSponsors />
|
||||||
<HomeOffers />
|
<HomeOffers />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
import { Container } from "@/components/layout";
|
||||||
|
import { DesignerCard } from "@/components/shared";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useScrollTop } from "@/hooks/use-scroll-top";
|
||||||
|
import useEmblaCarousel from "embla-carousel-react";
|
||||||
|
|
||||||
|
export default function Impressions() {
|
||||||
|
useScrollTop();
|
||||||
|
|
||||||
|
const [emblaRef] = useEmblaCarousel({ align: "center" });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="overflow-x-hidden">
|
||||||
|
<section className="">
|
||||||
|
<img
|
||||||
|
src="/impressions-cover.png"
|
||||||
|
alt=""
|
||||||
|
className="w-full object-contain max-h-[610px]"
|
||||||
|
/>
|
||||||
|
<h1 className="absolute opacity-0 top-0 left-0">Показы</h1>
|
||||||
|
|
||||||
|
<div className="bg-[url('/impressions-bg.png')] py-[200px] ">
|
||||||
|
<Container className="grid grid-cols-2 gap-6 h-[570px]">
|
||||||
|
<div className="flex flex-col gap-6 justify-center">
|
||||||
|
<h2 className="text-5xl leading-[120%]">
|
||||||
|
Дефиле туркменской моды на выставке "ТуркменТекстиль"
|
||||||
|
</h2>
|
||||||
|
<p className="text-on_surface_v normal text-base">
|
||||||
|
В рамках международной выставки "Туркмен текстиль" состоятся
|
||||||
|
эксклюзивные показы коллекций ведущих туркменских дизайнеров и
|
||||||
|
Домов моды. Гости мероприятия смогут оценить высокое мастерство
|
||||||
|
исполнения, богатство национальных традиций в современном
|
||||||
|
прочтении и актуальные тенденции текстильной индустрии
|
||||||
|
Туркменистана, воплощенные в уникальных моделях одежды и
|
||||||
|
аксессуаров.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<img
|
||||||
|
src="/impressions/gallery-1.png"
|
||||||
|
alt="impression image"
|
||||||
|
className="absolute top-0 right-0 z-10 shadow-xl drop-shadow-sm"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src="/impressions/gallery-2.png"
|
||||||
|
className="absolute left-0 top-1/4 shadow-xl drop-shadow-sm"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src="/impressions/gallery-3.png"
|
||||||
|
alt=""
|
||||||
|
className="absolute bottom-0 right-0 z-10 shadow-xl drop-shadow-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="py-[200px]">
|
||||||
|
<Container className="flex flex-col gap-10">
|
||||||
|
<h2 className="text-5xl">Дизайнеры одежды</h2>
|
||||||
|
|
||||||
|
<div ref={emblaRef} className="embla">
|
||||||
|
<div className="flex embla__container">
|
||||||
|
{[...Array(9)].map((_, i) => (
|
||||||
|
<DesignerCard
|
||||||
|
key={i}
|
||||||
|
name="Batyr"
|
||||||
|
info="test tesat tesxt test"
|
||||||
|
imgUrl="/impressions/designer.png"
|
||||||
|
className="embla__slide flex-[0_0_714px] mr-4"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="bg-[url('/CTA.png')] py-20 ">
|
||||||
|
<div className="flex flex-col gap-6 text-center max-w-[808px] mx-auto">
|
||||||
|
<h3 className="text-white text-3xl">
|
||||||
|
Тренды туркменского текстиля 2025/2026
|
||||||
|
</h3>
|
||||||
|
<p className="text-lg text-primary_03 normal">
|
||||||
|
Скачайте наш эксклюзивный гид и первыми узнайте о новейших
|
||||||
|
тенденциях, инновационных материалах и вдохновляющих коллекциях,
|
||||||
|
которые определят будущее туркменской текстильной индустрии
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
className="bg-reverse_primary w-fit mx-auto hover:bg-reverse_primary/90 text-on_secondary_container"
|
||||||
|
size={"sm"}
|
||||||
|
>
|
||||||
|
Скачать гид
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -8,3 +8,4 @@ export { default as NewsInner } from "./news-inner";
|
||||||
export { default as News } from "./news";
|
export { default as News } from "./news";
|
||||||
export { default as Participants } from "./participants";
|
export { default as Participants } from "./participants";
|
||||||
export { default as StendForm } from "./stend-form";
|
export { default as StendForm } from "./stend-form";
|
||||||
|
export { default as Impressions } from "./impressions";
|
||||||
|
|
|
||||||