This commit is contained in:
Batyr 2025-05-28 15:27:48 +05:00
parent 47f36f7ed3
commit ed3676a0ec
20 changed files with 504 additions and 99 deletions

View File

@ -23,6 +23,7 @@
"embla-carousel-autoplay": "^8.5.2",
"embla-carousel-react": "^8.5.2",
"i18next": "^24.2.2",
"locomotive-scroll": "^4.1.4",
"lucide-react": "^0.473.0",
"motion": "^12.0.1",
"react": "^18.3.1",
@ -38,12 +39,12 @@
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/locomotive-scroll": "^4.1.4",
"@types/node": "^22.12.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.17.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.16",
"globals": "^15.14.0",

View File

@ -47,6 +47,9 @@ importers:
i18next:
specifier: ^24.2.2
version: 24.2.2(typescript@5.6.3)
locomotive-scroll:
specifier: ^4.1.4
version: 4.1.4
lucide-react:
specifier: ^0.473.0
version: 0.473.0(react@18.3.1)
@ -87,6 +90,9 @@ importers:
'@eslint/js':
specifier: ^9.17.0
version: 9.19.0
'@types/locomotive-scroll':
specifier: ^4.1.4
version: 4.1.4
'@types/node':
specifier: ^22.12.0
version: 22.12.0
@ -102,9 +108,6 @@ importers:
autoprefixer:
specifier: ^10.4.20
version: 10.4.20(postcss@8.5.1)
eslint:
specifier: ^9.17.0
version: 9.19.0(jiti@1.21.7)
eslint-plugin-react-hooks:
specifier: ^5.0.0
version: 5.1.0(eslint@9.19.0(jiti@1.21.7))
@ -881,6 +884,9 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/locomotive-scroll@4.1.4':
resolution: {integrity: sha512-G8GUr/ML/Ssxxl83VYbtFHMFoPNOQh/glzPWi5Z3Jk7Hng26Sl0ckAV11uq1AGKEq40jFMPhyAIuQ6fGPiBmHQ==}
'@types/node@22.12.0':
resolution: {integrity: sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==}
@ -1009,10 +1015,16 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
bezier-easing@2.1.0:
resolution: {integrity: sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==}
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
bindall-standalone@1.0.5:
resolution: {integrity: sha512-HDI7YBWXVJk/eoGz+e4lYQQJnYp1ZHcUvAY71lVptLMhQnDm86vD73AGPw2qIlgYR3P0bjmoAcXiA8qhFejBhA==}
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
@ -1397,6 +1409,9 @@ packages:
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
lethargy@1.0.9:
resolution: {integrity: sha512-nFM8blpCF9rqIL5mRAaTGc78W8oQixVtsD86jbEPvcI13+lDUYJf3R7DZQQL7tCiBpbGpGKMX2gwJFO9hiaOkg==}
levn@0.4.1:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
@ -1412,6 +1427,9 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
locomotive-scroll@4.1.4:
resolution: {integrity: sha512-6i98cFF2SKg6wIPpwVPuo2FG8qL3USsdDeew78TEYZyLoqleMWNfkSDpWA6mPym4dOfTIBXc678VmGlkgx3fTA==}
lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
@ -1743,6 +1761,9 @@ packages:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
smoothscroll-polyfill@0.4.4:
resolution: {integrity: sha512-TK5ZA9U5RqCwMpfoMq/l1mrH0JAR7y7KRvOBx0n2869aLxch+gT9GhN3yUfjiw+d/DiF1mKo14+hd62JyMmoBg==}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@ -1800,6 +1821,9 @@ packages:
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
tiny-emitter@1.2.0:
resolution: {integrity: sha512-rWjF00inHeWtT5UbQYAXoMI4hL6TRMqohuKCsODyPYYmfAxqfMnXLsIeNrbdPEkNxlk++rojVilTnI9IVmEBtA==}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@ -1876,6 +1900,9 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
virtual-scroll@1.5.2:
resolution: {integrity: sha512-7jDHwlKbHUho7CYU/HojE/VKFH8GV9P5fVWP2HCa7dRUOpVvwl93OBOKIIcb2mKd+vqsbVR/0zl0X70+3sUZqA==}
vite@6.0.11:
resolution: {integrity: sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@ -2558,6 +2585,8 @@ snapshots:
'@types/json-schema@7.0.15': {}
'@types/locomotive-scroll@4.1.4': {}
'@types/node@22.12.0':
dependencies:
undici-types: 6.20.0
@ -2717,8 +2746,12 @@ snapshots:
balanced-match@1.0.2: {}
bezier-easing@2.1.0: {}
binary-extensions@2.3.0: {}
bindall-standalone@1.0.5: {}
brace-expansion@1.1.11:
dependencies:
balanced-match: 1.0.2
@ -3101,6 +3134,8 @@ snapshots:
dependencies:
json-buffer: 3.0.1
lethargy@1.0.9: {}
levn@0.4.1:
dependencies:
prelude-ls: 1.2.1
@ -3114,6 +3149,12 @@ snapshots:
dependencies:
p-locate: 5.0.0
locomotive-scroll@4.1.4:
dependencies:
bezier-easing: 2.1.0
smoothscroll-polyfill: 0.4.4
virtual-scroll: 1.5.2
lodash.debounce@4.0.8: {}
lodash.merge@4.6.2: {}
@ -3405,6 +3446,8 @@ snapshots:
signal-exit@4.1.0: {}
smoothscroll-polyfill@0.4.4: {}
source-map-js@1.2.1: {}
string-width@4.2.3:
@ -3486,6 +3529,8 @@ snapshots:
dependencies:
any-promise: 1.3.0
tiny-emitter@1.2.0: {}
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@ -3550,6 +3595,13 @@ snapshots:
util-deprecate@1.0.2: {}
virtual-scroll@1.5.2:
dependencies:
bindall-standalone: 1.0.5
lethargy: 1.0.9
object-assign: 4.1.1
tiny-emitter: 1.2.0
vite@6.0.11(@types/node@22.12.0)(jiti@1.21.7)(yaml@2.7.0):
dependencies:
esbuild: 0.24.2

BIN
public/impressions/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@ -0,0 +1,49 @@
import { useEffect, useRef, type PropsWithChildren } from "react";
import "locomotive-scroll/dist/locomotive-scroll.css";
type SmoothScrollProps = {
className?: string;
};
const SmoothScroll = ({
children,
className,
}: PropsWithChildren<SmoothScrollProps>) => {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
let locomotiveScroll: any;
const initScroll = async () => {
const LocomotiveScroll = (await import("locomotive-scroll")).default;
if (containerRef.current) {
if (locomotiveScroll) locomotiveScroll.destroy();
locomotiveScroll = new LocomotiveScroll({
el: containerRef.current,
smooth: true,
});
setTimeout(() => {
const scrollbars = document.querySelectorAll(".c-scrollbar");
if (scrollbars.length > 1) {
scrollbars[1].remove();
}
}, 100);
}
};
initScroll();
return () => {
locomotiveScroll?.destroy();
};
}, []);
return (
<main ref={containerRef} className={className} data-scroll-container>
{children}
</main>
);
};
export default SmoothScroll;

View File

@ -1,38 +1,132 @@
import { FC } from "react";
import { FC, useEffect, useState } from "react";
import { cn } from "@/lib/utils";
import { Dialog, DialogContent, DialogTrigger } from "../ui/dialog";
import useEmblaCarousel from "embla-carousel-react";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
interface Props {
imgUrl: string;
name: string;
info: string;
biography: string;
className?: string;
achievements: {
ru: string;
en: string;
}[];
image: { path: string };
images: { path: string }[];
}
export const DesignerCard: FC<Props> = ({ className, ...props }) => {
export const DesignerCard: FC<Props> = ({
className,
name,
achievements,
biography,
image,
images,
}) => {
const [emblaRef, emblaApi] = useEmblaCarousel({});
const [canScrollPrev, setCanScrollPrev] = useState(false);
const [canScrollNext, setCanScrollNext] = useState(false);
useEffect(() => {
if (!emblaApi) return;
const onSelect = () => {
setCanScrollPrev(emblaApi.canScrollPrev());
setCanScrollNext(emblaApi.canScrollNext());
};
emblaApi.on("select", onSelect);
onSelect();
return () => {
emblaApi.off("select", onSelect);
};
}, [emblaApi]);
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"
/>
<Dialog>
<DialogTrigger
className={cn(
"relative bg-[url('/impressions/card-bg.png')] bg-no-repeat h-[326px] overflow-hidden",
className
)}
>
<article>
<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 gap-4">
<img
src={image?.path}
alt={name}
className="flex-[0_0_44%] -mt-2.5 object-contain overflow-hidden"
/>
<div className="flex-auto z-20">
<h3>{props.name}</h3>
<p>{props.info}</p>
<div className="flex-auto text-left text-white z-20 pt-6 pl-4 pr-4">
<h3 className="text-xl mb-2">{name}</h3>
<ul className="text-sm list-disc pl-4 flex flex-col gap-2">
{achievements.slice(0, 7).map((item, i) => (
<li key={i}>{item.ru}</li>
))}
</ul>
</div>
</div>
</article>
</DialogTrigger>
<DialogContent className="p-6 flex flex-col gap-4 xl:!w-[1224px] max-h-[80vh]">
<div className="flex gap-4 overflow-y-auto">
<div className="flex-1 h-[345px]">
<img src={image?.path} alt="" className="size-full object-cover" />
</div>
<div className="flex-[0_0_70%]">
<div className="text-2xl mb-4">{name}</div>
<hr />
<div
className="mt-4 flex flex-col gap-3 normal"
dangerouslySetInnerHTML={{ __html: biography }}
/>
</div>
</div>
</div>
</article>
<hr />
<div>
<h3 className="text-xl mb-2.5">Работы дизайнера</h3>
<div ref={emblaRef} className="embla overflow-hidden">
<div className="embla__container items-center flex gap-4">
{canScrollPrev && (
<ChevronLeftIcon
onClick={() => canScrollPrev && emblaApi?.scrollPrev()}
className={cn(
"cursor-pointer",
!canScrollPrev && "opacity-20"
)}
/>
)}
<div className="flex items-center gap-4">
{images.map((item) => (
<img
src={item?.path}
className="size-[140px] object-cover object-top"
/>
))}
</div>
{canScrollNext && (
<ChevronRightIcon
onClick={() => canScrollPrev && emblaApi?.scrollNext()}
className={cn(
"cursor-pointer",
!canScrollNext && "opacity-20"
)}
/>
)}
</div>
</div>
</div>
</DialogContent>
</Dialog>
);
};

View File

View File

@ -0,0 +1,36 @@
import { Container } from "@/components/layout";
import { DesignerCard, Loader } from "../";
import useEmblaCarousel from "embla-carousel-react";
import { useDesigners } from "@/hooks/tanstack/use-designers";
const Designers = () => {
const [emblaRef] = useEmblaCarousel({ align: "center" });
const { data, isPending } = useDesigners(1);
return (
<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">
{isPending ? (
<Loader />
) : (
data?.designers?.map((item, i) => (
<DesignerCard
key={i}
{...item}
className="embla__slide flex-[0_0_714px] mr-4"
/>
))
)}
</div>
</div>
</Container>
</section>
);
};
export default Designers;

View File

@ -0,0 +1,44 @@
import { Container } from "@/components/layout";
import { Button } from "@/components/ui/button";
import { useTranslation } from "react-i18next";
const Hero = () => {
const { t } = useTranslation("main");
const { title, texts, button, button2 } = t("impressions.hero", {
returnObjects: true,
}) as { title: string; texts: string[]; button: string; button2: string };
return (
<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')]">
<Container className="py-20">
<h2 className="text-5xl leading-[120%] mb-10">{title}</h2>
<div className="text-on_surface_v flex flex-col gap-3">
{texts.map((item, i) => (
<p key={i}>{item}</p>
))}
</div>
<div className="flex items-center gap-6 mt-6">
<Button size={"sm"} className="w-full">
{button}
</Button>
<Button size={"sm"} className="w-full">
{button2}
</Button>
</div>
</Container>
</div>
</section>
);
};
export default Hero;

View File

@ -0,0 +1,3 @@
export { default as Hero } from "./hero";
export { default as Videos } from "./videos";
export { default as Designers } from "./designers";

View File

@ -0,0 +1,69 @@
import { Container } from "@/components/layout";
import { useTranslation } from "react-i18next";
const Videos = () => {
const { t } = useTranslation("main");
const title = t("impressions.videosTitle");
return (
<section className="bg-[url('/impressions/videos-block-bg.png')] py-20">
<Container>
<h2 className="mb-10 text-5xl leading-[120%] text-white">{title}</h2>
<div className="grid grid-cols-1 md:grid-cols-[1fr_32.5%] gap-4 text-white overflow-hidden h-auto md:h-[497px]">
{/* FIRST BLOCK */}
<div className="bg-gray_10 p-2 rounded-[8px] border border-gray_09 flex flex-col h-full">
<div className="max-h-[453px]">
<video
src="https://turkmentextile.turkmenexpo.com/app/storage/app/media/video/Textile2025.mp4"
muted
controls
autoPlay
loop
className="w-full size-full mb-2 rounded"
/>
</div>
<div className="flex items-center justify-between mt-auto">
<h3 className="text-sm">
Алена Ахмадуллина | 'Царевна-Лебедь' (74 симв.)
</h3>
<span className="text-xs normal">1/10</span>
</div>
</div>
{/* SECOND BLOCK */}
<div className="bg-gray_10 px-2 pt-2 rounded-[8px] border border-gray_09 flex flex-col h-full min-h-0">
<div className="flex items-center justify-between mb-2">
<h3 className="text-sm">Смотрите также</h3>
<span className="text-xs normal">1/10</span>
</div>
<div className="flex flex-col gap-2 overflow-y-auto flex-grow">
{[...Array(10)].map((_, i) => (
<div
key={i}
className="h-[59px] w-full flex items-center gap-2"
>
<div className="flex-[0_0_27%]">
<img
src="/impressions/test.png"
alt=""
className="object-contain rounded-tl rounded-bl"
/>
</div>
<h3 className="text-sm">
Алена Ахмадуллина | 'Царевна-Лебедь' 'Царевна-Лебедь' |
Алена Ахмадуллина
</h3>
</div>
))}
</div>
</div>
</div>
</Container>
</section>
);
};
export default Videos;

View File

@ -0,0 +1,15 @@
import { getDesigners } from "@/services/service";
import { useLangStore } from "@/store/lang";
import { useQuery } from "@tanstack/react-query";
export const useDesigners = (id: number) => {
const lang = useLangStore((state) => state.lang);
const { data, isPending } = useQuery({
queryKey: ["designers", lang],
queryFn: () => getDesigners(lang, id),
select: ({ data }) => data.data,
});
return { data, isPending };
};

View File

@ -13,6 +13,9 @@ import homeRu from "./locales/ru/home.json";
import aboutEn from "./locales/en/about.json";
import aboutRu from "./locales/ru/about.json";
import mainEn from "./locales/en/main.json";
import mainRu from "./locales/ru/main.json";
import contactsEn from "./locales/en/contacts.json";
import contactsRu from "./locales/ru/contacts.json";
@ -28,6 +31,7 @@ i18n.use(initReactI18next).init({
home: homeEn,
about: aboutEn,
contacts: contactsEn,
main: mainEn,
},
ru: {
nav: navRu,
@ -35,11 +39,12 @@ i18n.use(initReactI18next).init({
home: homeRu,
about: aboutRu,
contacts: contactsRu,
main: mainRu,
},
},
lng: initialLanguage, // Язык по умолчанию
fallbackLng: "en",
ns: ["home", "about", "header", "contacts", "nav"], // список namespaces
ns: ["home", "about", "header", "contacts", "nav", "main"], // список namespaces
defaultNS: "home", // namespace по умолчанию (можно менять в компонентах)
interpolation: {
escapeValue: false, // React уже экранирует значения

View File

@ -35,6 +35,9 @@
font-family: "Gilroy-Medium";
@apply bg-background;
}
.c-scrollbar .c-scrollbar_thumb {
background-color: #99101d !important;
}
}
@layer utilities {

19
src/locales/en/main.json Normal file
View File

@ -0,0 +1,19 @@
{
"impressions": {
"hero": {
"title": "TurkmenTextile Expo-2025 Перекресток стиля на Шелковом Пути!",
"texts": [
"От самого сердца Великого Шелкового пути — к сияющим вершинам мировой моды!",
"TurkmenTextile Expo-2025 приглашает вас в увлекательное путешествие, где встречаются вековые традиции, самые актуальные тренды и безграничный талант дизайнеров со всего мира. Это гармония, созданная вдохновением и мастерством.",
"Не упустите возможность стать частью этого грандиозного события! Присоединяйтесь к нам с 11 по 13 июня в Торгово-Промышленной Палате Туркменистана, Ашхабад."
],
"button": "Добавить в календарь (.ics файл)",
"button2": "Скачать программу"
},
"designers": {
"title": "Познакомьтесь с мастерами стиля: известные дизайнеры на TurkmenTextile Expo-2025!!",
"text": "С гордостью представляем талантливых дизайнеров из Туркменистана, чьи амбициозные проекты отличаются высочайшим качеством исполнения. Аккуратность пошива и идеально точный крой — это лишь часть того, что делает их коллекции по-настоящему выдающимися. Их смелый взгляд на будущее, органично переплетенный с богатым культурным наследием, создает уникальные произведения, готовые покорить самую широкую аудиторию и продемонстрировать мировой уровень мастерства туркменских модельеров."
},
"videosTitle": "Погрузитесь в триумф дизайнеров на подиуме TurkmenTextile Expo- 2025"
}
}

19
src/locales/ru/main.json Normal file
View File

@ -0,0 +1,19 @@
{
"impressions": {
"hero": {
"title": "TurkmenTextile Expo-2025 Перекресток стиля на Шелковом Пути!",
"texts": [
"От самого сердца Великого Шелкового пути — к сияющим вершинам мировой моды!",
"TurkmenTextile Expo-2025 приглашает вас в увлекательное путешествие, где встречаются вековые традиции, самые актуальные тренды и безграничный талант дизайнеров со всего мира. Это гармония, созданная вдохновением и мастерством.",
"Не упустите возможность стать частью этого грандиозного события! Присоединяйтесь к нам с 11 по 13 июня в Торгово-Промышленной Палате Туркменистана, Ашхабад."
],
"button": "Добавить в календарь (.ics файл)",
"button2": "Скачать программу"
},
"designers": {
"title": "Познакомьтесь с мастерами стиля: известные дизайнеры на TurkmenTextile Expo-2025!!",
"text": "С гордостью представляем талантливых дизайнеров из Туркменистана, чьи амбициозные проекты отличаются высочайшим качеством исполнения. Аккуратность пошива и идеально точный крой — это лишь часть того, что делает их коллекции по-настоящему выдающимися. Их смелый взгляд на будущее, органично переплетенный с богатым культурным наследием, создает уникальные произведения, готовые покорить самую широкую аудиторию и продемонстрировать мировой уровень мастерства туркменских модельеров."
},
"videosTitle": "Погрузитесь в триумф дизайнеров на подиуме TurkmenTextile Expo- 2025"
}
}

View File

@ -1,80 +1,15 @@
import { Container } from "@/components/layout";
import { DesignerCard } from "@/components/shared";
import { Designers, Hero, Videos } from "@/components/shared/impressions";
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>
<Hero />
<Videos />
<Designers />
<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">

View File

@ -14,6 +14,7 @@ import { NewsInnerType, NewsType } from "./types/news.type";
import { ParticipantsType } from "./types/participants.type";
import { PhotoTypes } from "./types/photo.type";
import { VideoTypes } from "./types/videos.type";
import { DesignersType } from "./types/designers.type";
const axios_url = axios.create({
baseURL: "https://turkmentextile.turkmenexpo.com/app/api/v1/",
@ -160,6 +161,7 @@ export const getVideos = async (id: number) => {
return data;
};
export const getSponsors = async (lang: LangState["lang"]) => {
const data = axios_url<PartnersType>("sponsors_and_partners", {
headers: {
@ -169,3 +171,13 @@ export const getSponsors = async (lang: LangState["lang"]) => {
return data;
};
export const getDesigners = async (lang: LangState["lang"], id: number) => {
const data = axios_url<DesignersType>("designers/category/" + id, {
headers: {
"Accept-Language": lang,
},
});
return data;
};

View File

@ -0,0 +1,46 @@
export interface DesignerImage {
id: number;
disk_name: string;
file_name: string;
file_size: number;
content_type: string;
title: string | null;
description: string | null;
field: string;
sort_order: number;
created_at: string;
updated_at: string;
path: string;
extension: string;
}
export interface Achievement {
ru: string;
en: string;
}
export interface Designer {
id: number;
name: string;
biography: string;
created_at: string;
updated_at: string;
category_designer_id: number;
achievements: Achievement[];
order_id: number | null;
image: DesignerImage;
images: DesignerImage[];
}
export interface Datum {
id: number;
name: string;
created_at: string;
updated_at: string;
designers: Designer[];
}
export interface DesignersType {
status: string;
data: Datum;
}

View File

@ -47,6 +47,9 @@ export default {
outline_v: "#D0D3D8",
background: "#F9F9FA",
surface_container: "#EEEDF4",
gray_10: "#433C3D",
gray_09: "#595051",
},
},
},