change
This commit is contained in:
parent
47f36f7ed3
commit
ed3676a0ec
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 136 KiB |
|
|
@ -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;
|
||||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export { default as Hero } from "./hero";
|
||||
export { default as Videos } from "./videos";
|
||||
export { default as Designers } from "./designers";
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 };
|
||||
};
|
||||
|
|
@ -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 уже экранирует значения
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@
|
|||
font-family: "Gilroy-Medium";
|
||||
@apply bg-background;
|
||||
}
|
||||
.c-scrollbar .c-scrollbar_thumb {
|
||||
background-color: #99101d !important;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -47,6 +47,9 @@ export default {
|
|||
outline_v: "#D0D3D8",
|
||||
background: "#F9F9FA",
|
||||
surface_container: "#EEEDF4",
|
||||
|
||||
gray_10: "#433C3D",
|
||||
gray_09: "#595051",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue