diff --git a/README.md b/README.md index cda8df9..3400e52 100644 Binary files a/README.md and b/README.md differ diff --git a/public/fonts/Gilroy-Black.woff2 b/public/fonts/Gilroy-Black.woff2 new file mode 100644 index 0000000..e3272a1 Binary files /dev/null and b/public/fonts/Gilroy-Black.woff2 differ diff --git a/public/impressions-bg.png b/public/impressions-bg.png new file mode 100644 index 0000000..8b58074 Binary files /dev/null and b/public/impressions-bg.png differ diff --git a/public/impressions-cover.png b/public/impressions-cover.png new file mode 100644 index 0000000..d124d41 Binary files /dev/null and b/public/impressions-cover.png differ diff --git a/public/impressions/card-bg-bg.png b/public/impressions/card-bg-bg.png new file mode 100644 index 0000000..e40ab04 Binary files /dev/null and b/public/impressions/card-bg-bg.png differ diff --git a/public/impressions/card-bg.png b/public/impressions/card-bg.png new file mode 100644 index 0000000..ed3e893 Binary files /dev/null and b/public/impressions/card-bg.png differ diff --git a/public/impressions/designer.png b/public/impressions/designer.png new file mode 100644 index 0000000..d6dcc26 Binary files /dev/null and b/public/impressions/designer.png differ diff --git a/public/impressions/gallery-1.png b/public/impressions/gallery-1.png new file mode 100644 index 0000000..be9c592 Binary files /dev/null and b/public/impressions/gallery-1.png differ diff --git a/public/impressions/gallery-2.png b/public/impressions/gallery-2.png new file mode 100644 index 0000000..0d13393 Binary files /dev/null and b/public/impressions/gallery-2.png differ diff --git a/public/impressions/gallery-3.png b/public/impressions/gallery-3.png new file mode 100644 index 0000000..1739775 Binary files /dev/null and b/public/impressions/gallery-3.png differ diff --git a/src/components/shared/chevron.tsx b/src/components/shared/chevron.tsx index 7a99cad..c138957 100644 --- a/src/components/shared/chevron.tsx +++ b/src/components/shared/chevron.tsx @@ -3,13 +3,15 @@ import { FC } from "react"; interface Props { className?: string; color?: string; + w?: string; + h?: string; } -export const Chevron: FC = ({ color = "white" }) => { +export const Chevron: FC = ({ color = "white", w = "20", h = "20" }) => { return ( = ({ className, ...props }) => { + return ( +
+
+ +
+ + +
+

{props.name}

+

{props.info}

+
+
+ + ); +}; diff --git a/src/components/shared/home/home-actions.tsx b/src/components/shared/home/home-actions.tsx new file mode 100644 index 0000000..1cc5d16 --- /dev/null +++ b/src/components/shared/home/home-actions.tsx @@ -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 = ({ 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 ( +
+ +

+ {title} +

+ +
+ {items.map((item) => + !item.dropdown ? ( + + + + ) : ( + + + + + + ) + )} +
+
+
+ ); +}; diff --git a/src/components/shared/home/home-hero.tsx b/src/components/shared/home/home-hero.tsx index 5bb37a0..bc31e58 100644 --- a/src/components/shared/home/home-hero.tsx +++ b/src/components/shared/home/home-hero.tsx @@ -1,17 +1,11 @@ import useEmblaCarousel from "embla-carousel-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 { btns } from "@/data/home/home-hero.data"; -import { useTranslate } from "@/hooks/use-translate"; -import { useLangStore } from "@/store/lang"; import { useTranslation } from "react-i18next"; +import { HomeTimer } from "./"; export const HomeHero: FC = () => { const [embalRef] = useEmblaCarousel(); - const lang = useLangStore((state) => state.lang); const { t } = useTranslation("home"); @@ -23,38 +17,24 @@ export const HomeHero: FC = () => { else if (md) return t("banners.md"); else return t("banners.sm"); } + return ( -
-
-
-
- +
+
+
+
+
+ +
-
+
- - {btns[useTranslate(lang)].data.map(({ title, link, blank }) => ( - - - - ))} - - + + ); }; diff --git a/src/components/shared/home/home-news.tsx b/src/components/shared/home/home-news.tsx index b291613..543b3f3 100644 --- a/src/components/shared/home/home-news.tsx +++ b/src/components/shared/home/home-news.tsx @@ -20,7 +20,7 @@ export const HomeNews: FC = () => { {lang === "en" ? "News" : "Новости"}
- {data?.map((item) => ( + {data?.slice(0, 3).map((item) => ( ))}
diff --git a/src/components/shared/home/home-time.tsx b/src/components/shared/home/home-time.tsx index fa01b64..a87254c 100644 --- a/src/components/shared/home/home-time.tsx +++ b/src/components/shared/home/home-time.tsx @@ -20,12 +20,14 @@ export const HomeTime: FC = ({ className }) => { const { data, isPending } = useExhibitionTime(); const { data: contacts } = useHomeContacts(); + const translate = useTranslate(lang); + if (isPending) return ; return (
-

{times[useTranslate(lang)].title}

+

{times[translate].title}

diff --git a/src/components/shared/home/home-timer.tsx b/src/components/shared/home/home-timer.tsx new file mode 100644 index 0000000..af7714f --- /dev/null +++ b/src/components/shared/home/home-timer.tsx @@ -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 = ({ className }) => { + const { t } = useTranslation("home"); + + const [timeLeft, setTimeLeft] = useState({ + days: "0", + hours: "00", + minutes: "00", + seconds: "00", + }); + const [prevValues, setPrevValues] = useState>({}); + 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 ( +
+

{title}

+ +
+ + + + +
+
+ ); +}; diff --git a/src/components/shared/home/index.ts b/src/components/shared/home/index.ts index f56aa43..193cfcf 100644 --- a/src/components/shared/home/index.ts +++ b/src/components/shared/home/index.ts @@ -4,3 +4,6 @@ export { HomeAbout } from "./home-about"; export { HomeTheme } from "./home-theme"; export { HomeOffers } from "./home-offers"; export { HomeSponsors } from "./home-sponsors"; +export { HomeNews } from "./home-news"; +export { HomeTimer } from "./home-timer"; +export { HomeActions } from "./home-actions"; diff --git a/src/components/shared/index.ts b/src/components/shared/index.ts index ee3a51c..bdfb2b5 100644 --- a/src/components/shared/index.ts +++ b/src/components/shared/index.ts @@ -15,6 +15,8 @@ export { Loader } from "./loader"; export { Tabs } from "./tabs"; export { ParticipantItem } from "./participant-item"; export { MediaModal } from "./media-modal"; +export { DesignerCard } from "./designer-card"; +export { TimerItem } from "./timer-item"; export * from "./about"; export * from "./home"; diff --git a/src/components/shared/menu.tsx b/src/components/shared/menu.tsx index 8e5b172..692e436 100644 --- a/src/components/shared/menu.tsx +++ b/src/components/shared/menu.tsx @@ -1,4 +1,4 @@ -import { FC, useState } from "react"; +import { FC, PropsWithChildren, useState } from "react"; import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; import { Chevron } from "./"; import { Link } from "react-router-dom"; @@ -9,18 +9,19 @@ import { useUiStore } from "@/store/ui"; interface Props { className?: string; - title: string; + title?: string; color?: string; triggerClassName?: string; dropDownContent?: DropDownContent[]; } -export const Menu: FC = ({ +export const Menu: FC> = ({ title, dropDownContent, color, triggerClassName, + children, }) => { const [isOpen, setIsOpen] = useState(false); const setSheet = useUiStore((state) => state.setSheet); @@ -31,7 +32,8 @@ export const Menu: FC = ({ className={cn("flex items-center gap-2", triggerClassName)} > {title} - + {children} + {!children && } diff --git a/src/components/shared/timer-item.tsx b/src/components/shared/timer-item.tsx new file mode 100644 index 0000000..544bf7f --- /dev/null +++ b/src/components/shared/timer-item.tsx @@ -0,0 +1,29 @@ +export const TimerItem = ({ + value, + label, + prevValue, +}: { + value: string; + label: string; + prevValue: string; +}) => ( +
+
+ + {prevValue} + + + {value} + +
+
{label}
+
+); diff --git a/src/index.css b/src/index.css index 5d76321..533800a 100644 --- a/src/index.css +++ b/src/index.css @@ -17,6 +17,11 @@ url(../fonts/Gilroy-Semibold.woff2) format("woff2"); 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 components; @@ -33,6 +38,14 @@ } @layer utilities { + .container { + @apply w-full mx-auto max-w-[1240px] px-4; + } + + .section-y { + @apply py-20; + } + .normal { @apply font-["Gilroy-Regular"] font-normal; } @@ -41,12 +54,16 @@ @apply font-["Gilroy-Semibold"] font-semibold; } + .black { + @apply font-['Gilroy-Black']; + } + .h1 { @apply text-[28px] leading-[130%] font-medium; } .h2 { - @apply md:text-[28px] text-2xl text-[#171717]; + @apply md:text-[28px] text-[32px] text-[#171717]; } .p { diff --git a/src/locales/ru/home.json b/src/locales/ru/home.json index 84b1ae0..b714c45 100644 --- a/src/locales/ru/home.json +++ b/src/locales/ru/home.json @@ -5,6 +5,66 @@ "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": { "title": "Партнёры", "button": "" diff --git a/src/main.tsx b/src/main.tsx index daa4bf4..75cf15e 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -10,6 +10,7 @@ import { BecomeSponsor, Contacts, Home, + Impressions, Media, News, NewsInner, @@ -32,6 +33,7 @@ const router = createBrowserRouter([ { element: , path: "news/:id" }, { element: , path: "participants" }, { element: , path: "media" }, + { element: , path: "impressions" }, ], }, ]); diff --git a/src/pages/home.tsx b/src/pages/home.tsx index 5613fb7..f928416 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -1,17 +1,19 @@ import { HomeAbout, + HomeActions, HomeHero, + HomeNews, HomeOffers, HomeSponsors, HomeTheme, HomeTime, } from "@/components/shared"; -import { HomeNews } from "@/components/shared/home/home-news"; export default function Home() { return (
+ diff --git a/src/pages/impressions.tsx b/src/pages/impressions.tsx new file mode 100644 index 0000000..0384891 --- /dev/null +++ b/src/pages/impressions.tsx @@ -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 ( +
+
+ +

Показы

+ +
+ +
+

+ Дефиле туркменской моды на выставке "ТуркменТекстиль" +

+

+ В рамках международной выставки "Туркмен текстиль" состоятся + эксклюзивные показы коллекций ведущих туркменских дизайнеров и + Домов моды. Гости мероприятия смогут оценить высокое мастерство + исполнения, богатство национальных традиций в современном + прочтении и актуальные тенденции текстильной индустрии + Туркменистана, воплощенные в уникальных моделях одежды и + аксессуаров. +

+
+
+ impression image + + +
+
+
+
+ +
+ +

Дизайнеры одежды

+ +
+
+ {[...Array(9)].map((_, i) => ( + + ))} +
+
+
+
+ +
+
+

+ Тренды туркменского текстиля 2025/2026 +

+

+ Скачайте наш эксклюзивный гид и первыми узнайте о новейших + тенденциях, инновационных материалах и вдохновляющих коллекциях, + которые определят будущее туркменской текстильной индустрии +

+ +
+
+
+ ); +} diff --git a/src/pages/index.ts b/src/pages/index.ts index e91ee2e..ba3510d 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -8,3 +8,4 @@ export { default as NewsInner } from "./news-inner"; export { default as News } from "./news"; export { default as Participants } from "./participants"; export { default as StendForm } from "./stend-form"; +export { default as Impressions } from "./impressions";