feat: migrate i18n to next-intl with locale-based URL routing
Replace manual cookie-based language system with next-intl v4. All routes now use /ru and /en URL prefixes, translation strings live in JSON files, and components use useTranslations/getTranslations instead of useLang/Redux.
This commit is contained in:
parent
7940c7b2f4
commit
f53753487f
|
|
@ -33,3 +33,6 @@ yarn-error.log*
|
|||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
.claude
|
||||
.agents
|
||||
|
|
@ -1,16 +1,15 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getAbout } from "@/services/about";
|
||||
import { cookies } from "next/headers";
|
||||
import { getLocale, getTranslations } from "next-intl/server";
|
||||
|
||||
export default async function AboutPage() {
|
||||
const lang = cookies().get("lang")?.value ?? "ru";
|
||||
const lang = await getLocale();
|
||||
const t = await getTranslations("about");
|
||||
|
||||
const data = await getAbout(lang);
|
||||
|
||||
const aboutText = lang === "en" ? "About us" : "Коротко о нас";
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar second={aboutText} title={aboutText}>
|
||||
<LayoutWithSidebar second={t("title")} title={t("title")}>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data,
|
||||
|
|
@ -5,26 +5,28 @@ import Image from "next/image";
|
|||
|
||||
import { GreenBtn } from "@/components/ui/Buttons";
|
||||
|
||||
import { useAppDispatch, useAppSelector } from "@/redux/hooks";
|
||||
import { selectHeader, setShowInput } from "@/redux/slices/headerSlice";
|
||||
import { useAppDispatch } from "@/redux/hooks";
|
||||
import { setShowInput } from "@/redux/slices/headerSlice";
|
||||
import { baseAPI } from "@/lib/API";
|
||||
import { NewsPageType } from "@/lib/types/NewsPage.type";
|
||||
import Link from "next/link";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
import { BreadCrumbs } from "@/components/ui/bread-crumbs";
|
||||
import { Title } from "@/components/ui/title";
|
||||
import Loader from "@/components/ui/Loader";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
|
||||
export default function SingleNewsPage({ params }: { params: { id: string } }) {
|
||||
const dispatch = useAppDispatch();
|
||||
const locale = useLocale();
|
||||
const t = useTranslations("news");
|
||||
|
||||
const { activeLang } = useAppSelector(selectHeader);
|
||||
const [newsItemData, setNewsItemData] = React.useState<NewsPageType>();
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch(`${baseAPI}news/${params.id}`, {
|
||||
headers: {
|
||||
"Accept-Language": activeLang.localization,
|
||||
"Accept-Language": locale,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -43,11 +45,11 @@ export default function SingleNewsPage({ params }: { params: { id: string } }) {
|
|||
React.useEffect(() => {
|
||||
fetchData();
|
||||
dispatch(setShowInput(false));
|
||||
}, [activeLang.localization]);
|
||||
}, [locale]);
|
||||
|
||||
return (
|
||||
<div className="section-mb w-full">
|
||||
<BreadCrumbs second="Новости" path="/news" third="Статья" />
|
||||
<BreadCrumbs second={t("title")} path="/news" third={t("article")} />
|
||||
{newsItemData ? (
|
||||
<div className="mb-5">
|
||||
<Title text={newsItemData.data.title} />
|
||||
|
|
@ -84,7 +86,7 @@ export default function SingleNewsPage({ params }: { params: { id: string } }) {
|
|||
</div>
|
||||
|
||||
<Link href="/news" className="flex justify-center">
|
||||
<GreenBtn text="Все новости" px />
|
||||
<GreenBtn text={t("allNews")} px />
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useLang } from "@/utils/useLang";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { selectHeader } from "@/redux/slices/headerSlice";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { NewsData } from "@/lib/types/NewsData.type";
|
||||
import { baseAPI } from "@/lib/API";
|
||||
import Loader from "@/components/ui/Loader";
|
||||
|
|
@ -15,9 +13,10 @@ import { Pagination } from "@/components/ui/Pagination";
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
|
||||
const News = () => {
|
||||
const { activeLang } = useAppSelector(selectHeader);
|
||||
const locale = useLocale();
|
||||
const t = useTranslations("news");
|
||||
const tCommon = useTranslations("common");
|
||||
|
||||
const menu = ["Новости", "СМИ о нас"];
|
||||
const [current, setCurrent] = React.useState<number>(1);
|
||||
const [perPage, setPerPage] = React.useState<number>(6);
|
||||
const [totalNews, setTotalNews] = React.useState<number>(0);
|
||||
|
|
@ -36,7 +35,7 @@ const News = () => {
|
|||
`${baseAPI}news?page=${current}&per_page=${perPage}`,
|
||||
{
|
||||
headers: {
|
||||
"Accept-Language": activeLang.localization,
|
||||
"Accept-Language": locale,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
@ -57,7 +56,7 @@ const News = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
fetchNews();
|
||||
}, [current, perPage, activeLang.localization]);
|
||||
}, [current, perPage, locale]);
|
||||
|
||||
const handleOnClickButton = () => {
|
||||
setPerPage((prev) => prev + 6);
|
||||
|
|
@ -70,23 +69,13 @@ const News = () => {
|
|||
return (
|
||||
<div>
|
||||
<LayoutWithSidebar
|
||||
title={useLang("News", "Новости", activeLang.localization)}
|
||||
second={useLang("News", "Новости", activeLang.localization)}
|
||||
title={t("title")}
|
||||
second={t("title")}
|
||||
cursor={false}
|
||||
>
|
||||
<div>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col">
|
||||
<div className="hidden sm:flex justify-between items-center pb-[5px] ">
|
||||
<div className="pointer-events-none opacity-0 flex items-center gap-5">
|
||||
{menu.map((item, i) => (
|
||||
<p key={i} className="cursor-pointer leading-[130%]">
|
||||
{item}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
"mb-[48px] lg:mb-[108px]",
|
||||
|
|
@ -112,7 +101,7 @@ const News = () => {
|
|||
<div className="hidden sm:flex flex-col gap-6 w-full max-w-[180px] mx-auto justify-center items-center">
|
||||
{newsData && totalNews > perPage && perPage >= totalNews && (
|
||||
<div onClick={handleOnClickButton}>
|
||||
<BorderBtn px text={"Показать ещё"} />
|
||||
<BorderBtn px text={tCommon("showMore")} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function AdvertisingPage() {
|
||||
const { data } = await getServices();
|
||||
const lang = await getLocale();
|
||||
const { data } = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function AdvertisingPage() {
|
||||
const data = await getServices();
|
||||
const lang = await getLocale();
|
||||
const data = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -12,7 +14,7 @@ export default async function AdvertisingPage() {
|
|||
<div
|
||||
className="select-inner"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data ? data.data[1].content : "",
|
||||
__html: data?.data ? data.data[1].content : "",
|
||||
}}
|
||||
/>
|
||||
</LayoutWithSidebar>
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function BusinessTours() {
|
||||
const { data } = await getServices();
|
||||
const lang = await getLocale();
|
||||
const { data } = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function CertificationPage() {
|
||||
const data = await getServices();
|
||||
const lang = await getLocale();
|
||||
const data = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -12,7 +14,7 @@ export default async function CertificationPage() {
|
|||
<div
|
||||
className="select-inner"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data ? data.data[3].content : "",
|
||||
__html: data?.data ? data.data[3].content : "",
|
||||
}}
|
||||
/>
|
||||
</LayoutWithSidebar>
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function ForwardingPage() {
|
||||
const data = await getServices();
|
||||
const lang = await getLocale();
|
||||
const data = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -12,7 +14,7 @@ export default async function ForwardingPage() {
|
|||
<div
|
||||
className="select-inner"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data ? data.data[4].content : "",
|
||||
__html: data?.data ? data.data[4].content : "",
|
||||
}}
|
||||
/>
|
||||
</LayoutWithSidebar>
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function HybridsPage() {
|
||||
const data = await getServices();
|
||||
const lang = await getLocale();
|
||||
const data = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -12,7 +14,7 @@ export default async function HybridsPage() {
|
|||
<div
|
||||
className="select-inner"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data ? data.data[2].content : "",
|
||||
__html: data?.data ? data.data[2].content : "",
|
||||
}}
|
||||
/>
|
||||
</LayoutWithSidebar>
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function MeetingsPage() {
|
||||
const data = await getServices();
|
||||
const lang = await getLocale();
|
||||
const data = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -12,7 +14,7 @@ export default async function MeetingsPage() {
|
|||
<div
|
||||
className="select-inner"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data ? data.data[9].content : "",
|
||||
__html: data?.data ? data.data[9].content : "",
|
||||
}}
|
||||
/>
|
||||
</LayoutWithSidebar>
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function MissionsPage() {
|
||||
const data = await getServices();
|
||||
const lang = await getLocale();
|
||||
const data = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -12,7 +14,7 @@ export default async function MissionsPage() {
|
|||
<div
|
||||
className="select-inner"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data ? data.data[10].content : "",
|
||||
__html: data?.data ? data.data[10].content : "",
|
||||
}}
|
||||
/>
|
||||
</LayoutWithSidebar>
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function OrganizationPage() {
|
||||
const data = await getServices();
|
||||
const lang = await getLocale();
|
||||
const data = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function OutsourcingPage() {
|
||||
const data = await getServices();
|
||||
const lang = await getLocale();
|
||||
const data = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -12,7 +14,7 @@ export default async function OutsourcingPage() {
|
|||
<div
|
||||
className="select-inner"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data ? data.data[5].content : "",
|
||||
__html: data?.data ? data.data[5].content : "",
|
||||
}}
|
||||
/>
|
||||
</LayoutWithSidebar>
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { getServices } from "@/services/services";
|
||||
import { getLocale } from "next-intl/server";
|
||||
|
||||
export default async function PersonalTrainingPage() {
|
||||
const data = await getServices();
|
||||
const lang = await getLocale();
|
||||
const data = await getServices(lang);
|
||||
|
||||
return (
|
||||
<LayoutWithSidebar
|
||||
|
|
@ -12,7 +14,7 @@ export default async function PersonalTrainingPage() {
|
|||
<div
|
||||
className="select-inner"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data ? data.data[6].content : "",
|
||||
__html: data?.data ? data.data[6].content : "",
|
||||
}}
|
||||
/>
|
||||
</LayoutWithSidebar>
|
||||
|
|
@ -2,20 +2,19 @@
|
|||
|
||||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { baseAPI } from "@/lib/API";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { selectHeader } from "@/redux/slices/headerSlice";
|
||||
import { useLang } from "@/utils/useLang";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const Visitors = () => {
|
||||
const [visitorsData, setVisitorsData] = useState<string>();
|
||||
const { activeLang } = useAppSelector(selectHeader);
|
||||
const locale = useLocale();
|
||||
const t = useTranslations("visitors");
|
||||
|
||||
const fetchVisitors = async () => {
|
||||
try {
|
||||
const res = await fetch(`${baseAPI}settings/visitors_page`, {
|
||||
headers: {
|
||||
"Accept-Language": activeLang.localization,
|
||||
"Accept-Language": locale,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -33,17 +32,13 @@ const Visitors = () => {
|
|||
|
||||
useEffect(() => {
|
||||
fetchVisitors();
|
||||
}, []);
|
||||
}, [locale]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LayoutWithSidebar
|
||||
title={useLang(
|
||||
"Information for visitors",
|
||||
"Информация для посетителей",
|
||||
activeLang.localization
|
||||
)}
|
||||
second={useLang("For visitors", "Посетителям", activeLang.localization)}
|
||||
title={t("information")}
|
||||
second={t("title")}
|
||||
>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: visitorsData ? visitorsData : "" }}
|
||||
|
|
@ -2,20 +2,19 @@
|
|||
|
||||
import { LayoutWithSidebar } from "@/components/page/LayoutWithSidebar";
|
||||
import { baseAPI } from "@/lib/API";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { selectHeader } from "@/redux/slices/headerSlice";
|
||||
import { useLang } from "@/utils/useLang";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const RulesForVisitors = () => {
|
||||
const [visitorsData, setVisitorsData] = useState<string>();
|
||||
const { activeLang } = useAppSelector(selectHeader);
|
||||
const locale = useLocale();
|
||||
const t = useTranslations("visitors");
|
||||
|
||||
const fetchVisitors = async () => {
|
||||
try {
|
||||
const res = await fetch(`${baseAPI}settings/visiting_rules`, {
|
||||
headers: {
|
||||
"Accept-Language": activeLang.localization,
|
||||
"Accept-Language": locale,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -33,23 +32,15 @@ const RulesForVisitors = () => {
|
|||
|
||||
useEffect(() => {
|
||||
fetchVisitors();
|
||||
}, []);
|
||||
}, [locale]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LayoutWithSidebar
|
||||
title={useLang(
|
||||
"Entrance rules",
|
||||
"Порядок регистрации посетителей",
|
||||
activeLang.localization
|
||||
)}
|
||||
second={useLang("Visitors", "Посетителям", activeLang.localization)}
|
||||
title={t("rules")}
|
||||
second={t("title")}
|
||||
path="/visitors"
|
||||
third={useLang(
|
||||
"Entrance rules",
|
||||
"Порядок регистрации посетителей",
|
||||
activeLang.localization
|
||||
)}
|
||||
third={t("rules")}
|
||||
>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: visitorsData ? visitorsData : "" }}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { EventPageButtons } from "@/components/shared/event-page-buttons";
|
||||
import { getEventPage } from "@/services/calendar";
|
||||
import { cookies } from "next/headers";
|
||||
import { getLocale, getTranslations } from "next-intl/server";
|
||||
import Image from "next/image";
|
||||
|
||||
export default async function EventPage({
|
||||
|
|
@ -10,7 +10,8 @@ export default async function EventPage({
|
|||
}) {
|
||||
const { id } = params;
|
||||
|
||||
const lang = cookies().get("lang")?.value ?? "ru";
|
||||
const lang = await getLocale();
|
||||
const t = await getTranslations("events");
|
||||
|
||||
const data = await getEventPage(id, lang);
|
||||
|
||||
|
|
@ -24,7 +25,7 @@ export default async function EventPage({
|
|||
<div className="flex flex-col gap-20 w-full">
|
||||
<div className="flex flex-col gap-8 event-block ">
|
||||
<div className="flex justify-between items-center">
|
||||
<h4>{lang === "en" ? "Date" : "Дата"}</h4>
|
||||
<h4>{t("date")}</h4>
|
||||
<h5>{data?.date}</h5>
|
||||
</div>
|
||||
|
||||
|
|
@ -32,7 +33,7 @@ export default async function EventPage({
|
|||
{data?.location && (
|
||||
<>
|
||||
<div className="flex justify-between items-center">
|
||||
<h4>{lang === "en" ? "Venue" : "Место"}</h4>
|
||||
<h4>{t("venue")}</h4>
|
||||
<h5>{data?.location}</h5>
|
||||
</div>
|
||||
<hr />
|
||||
|
|
@ -42,7 +43,7 @@ export default async function EventPage({
|
|||
{data?.organizers.length > 0 && (
|
||||
<>
|
||||
<div className="flex justify-between items-center">
|
||||
<h4>{lang === "en" ? "Organiser" : "Организатор"}</h4>
|
||||
<h4>{t("organiser")}</h4>
|
||||
<h5>{data?.organizers[0]?.name}</h5>
|
||||
</div>
|
||||
</>
|
||||
|
|
@ -53,7 +54,7 @@ export default async function EventPage({
|
|||
<hr />
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
<h4>{lang === "en" ? "Co-organiser" : "Со-организатор"}</h4>
|
||||
<h4>{t("coOrganiser")}</h4>
|
||||
|
||||
<h5>{data?.coorganizers[0]?.name}</h5>
|
||||
</div>
|
||||
|
|
@ -81,7 +82,7 @@ export default async function EventPage({
|
|||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col lg:flex-row gap-6">
|
||||
<h4 className="text_24 lg:flex-[0_0_392px]">
|
||||
{lang === "en" ? "Description" : "Описание"}
|
||||
{t("description")}
|
||||
</h4>
|
||||
<p className="flex-1 text_16">{data?.description}</p>
|
||||
</div>
|
||||
|
|
@ -92,7 +93,7 @@ export default async function EventPage({
|
|||
|
||||
<div className="flex flex-col lg:flex-row gap-6">
|
||||
<h4 className="text_24 lg:flex-[0_0_392px]">
|
||||
{lang === "en" ? "Theme of event" : "Тематика мероприятия"}
|
||||
{t("theme")}
|
||||
</h4>
|
||||
<div
|
||||
className="flex-1 text_16"
|
||||
|
|
@ -3,26 +3,26 @@ import { BreadCrumbs } from "@/components/ui/bread-crumbs";
|
|||
import { Title } from "@/components/ui/title";
|
||||
import { getCalendar } from "@/services/calendar";
|
||||
import { Metadata } from "next";
|
||||
import { cookies } from "next/headers";
|
||||
import { getLocale, getTranslations } from "next-intl/server";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "TurkmenExpo | Calendar",
|
||||
};
|
||||
|
||||
export default async function CalendarPage() {
|
||||
const lang = cookies().get("lang")?.value ?? "ru";
|
||||
const lang = await getLocale();
|
||||
const t = await getTranslations("calendar");
|
||||
|
||||
const data = await getCalendar(lang);
|
||||
|
||||
const title = lang === "en" ? "Calendar of events" : "Календарь мероприятий";
|
||||
return (
|
||||
<div className="section-mb pt-10">
|
||||
<div className="container flex flex-col items-start pt-5 gap-10 md:gap-12">
|
||||
<div>
|
||||
<div className="mb-[24px]">
|
||||
<BreadCrumbs second={title} />
|
||||
<BreadCrumbs second={t("title")} />
|
||||
</div>
|
||||
<Title text={title} />
|
||||
<Title text={t("title")} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-6 w-full">
|
||||
{data.data.map((item, i) => (
|
||||
|
|
@ -2,32 +2,34 @@ import React from "react";
|
|||
|
||||
import { BreadCrumbs } from "@/components/ui/bread-crumbs";
|
||||
import { ContactsForm } from "@/components/shared/contacts-form";
|
||||
import { cookies } from "next/headers";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
|
||||
export default async function ContactsPage() {
|
||||
const lang = cookies().get("lang")?.value;
|
||||
const t = await getTranslations("contacts");
|
||||
|
||||
return (
|
||||
<main className="bg-blueBg h-full w-full pt-12">
|
||||
<div className="container flex flex-col items-start">
|
||||
<div className="mt-5">
|
||||
<BreadCrumbs second={lang === "ru" ? "Контакты" : "Contacts"} />
|
||||
<BreadCrumbs second={t("title")} />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 w-full">
|
||||
<ContactsForm />
|
||||
|
||||
<div className="p-6 bg-bg_surface_container rounded-[8px]">
|
||||
<h2 className="h2 mb-10 xl:mb-8 text-3xl font-normal">Контакты</h2>
|
||||
<h2 className="h2 mb-10 xl:mb-8 text-3xl font-normal">
|
||||
{t("title")}
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-col gap-20">
|
||||
<div className="flex items-center gap-6">
|
||||
<img src="/assets/icons/contacts/address.svg" alt="address" />
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl mb-2">Адрес:</h3>
|
||||
<h3 className="text-xl mb-2">{t("address")}</h3>
|
||||
<address className="text-base normal not-italic">
|
||||
744000, г. Ашхабад, просп. Битарап Туркменистан, 183
|
||||
{t("addressValue")}
|
||||
</address>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -36,9 +38,7 @@ export default async function ContactsPage() {
|
|||
|
||||
<div>
|
||||
<h3 className="text-xl mb-2"></h3>
|
||||
<h4 className="text-base normal">
|
||||
+99371871814; 99363719588
|
||||
</h4>
|
||||
<h4 className="text-base normal">{t("phoneNumbers")}</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -4,11 +4,11 @@ import { Partners } from "@/components/shared/Partners";
|
|||
import { Reviews } from "@/components/shared/Reviews";
|
||||
import { Slider } from "@/components/shared/Slider";
|
||||
import Loader from "@/components/ui/Loader";
|
||||
import { cookies } from "next/headers";
|
||||
import { getLocale } from "next-intl/server";
|
||||
import { Suspense } from "react";
|
||||
|
||||
export default async function HomePage() {
|
||||
const lang = cookies().get("lang")?.value ?? "ru";
|
||||
const lang = await getLocale();
|
||||
|
||||
return (
|
||||
<div className="bg-blueBg flex flex-col gap-[60px] md:gap-20 pb-20 mt-[70px]">
|
||||
|
|
@ -19,7 +19,6 @@ export default async function HomePage() {
|
|||
</Suspense>
|
||||
<News />
|
||||
<Reviews />
|
||||
{/* <Video /> */}
|
||||
<Partners />
|
||||
</div>
|
||||
);
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import { Roboto } from "next/font/google";
|
||||
import clsx from "clsx";
|
||||
import { NextIntlClientProvider } from "next-intl";
|
||||
import { getMessages, setRequestLocale } from "next-intl/server";
|
||||
import { notFound } from "next/navigation";
|
||||
import { locales } from "@/i18n/config";
|
||||
import StoreProvider from "../StoreProvider";
|
||||
import "../globals.css";
|
||||
|
||||
const roboto = Roboto({
|
||||
subsets: ["latin"],
|
||||
weight: ["300", "400", "500", "700"],
|
||||
variable: "--font-roboto",
|
||||
});
|
||||
|
||||
export function generateStaticParams() {
|
||||
return locales.map((locale) => ({ locale }));
|
||||
}
|
||||
|
||||
export default async function LocaleLayout({
|
||||
children,
|
||||
params,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
params: { locale: string };
|
||||
}) {
|
||||
const { locale } = params;
|
||||
|
||||
if (!locales.includes(locale as any)) notFound();
|
||||
|
||||
setRequestLocale(locale);
|
||||
const messages = await getMessages();
|
||||
|
||||
return (
|
||||
<html lang={locale}>
|
||||
<StoreProvider>
|
||||
<body className={clsx("antialiased font-roboto", roboto.variable)}>
|
||||
<NextIntlClientProvider locale={locale} messages={messages}>
|
||||
{children}
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</StoreProvider>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,15 +1,4 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Roboto } from "next/font/google";
|
||||
import StoreProvider from "./StoreProvider";
|
||||
|
||||
import "./globals.css";
|
||||
import clsx from "clsx";
|
||||
|
||||
const roboto = Roboto({
|
||||
subsets: ["latin"],
|
||||
weight: ["300", "400", "500", "700"],
|
||||
variable: "--font-roboto",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "TurkmenExpo",
|
||||
|
|
@ -29,13 +18,5 @@ export default function RootLayout({
|
|||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html>
|
||||
<StoreProvider>
|
||||
<body className={clsx("antialiased font-roboto", roboto.variable)}>
|
||||
{children}
|
||||
</body>
|
||||
</StoreProvider>
|
||||
</html>
|
||||
);
|
||||
return children;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
// middleware.ts
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
// 1. Проверяем существующую куку
|
||||
let lang = request.cookies.get("lang")?.value;
|
||||
|
||||
// 2. Если куки нет, определяем язык из браузера
|
||||
if (!lang) {
|
||||
lang =
|
||||
request.headers
|
||||
.get("Accept-Language")
|
||||
?.split(",")[0] // Берём первый язык из списка
|
||||
.split("-")[0] // Убираем регион (en-US → en)
|
||||
.toLowerCase() || "en"; // Fallback на английский
|
||||
}
|
||||
|
||||
// 3. Сохраняем язык в куки
|
||||
const response = NextResponse.next();
|
||||
if (!request.cookies.has("lang")) {
|
||||
response.cookies.set("lang", lang, {
|
||||
maxAge: 60 * 60 * 24 * 365, // 1 год
|
||||
httpOnly: true,
|
||||
});
|
||||
}
|
||||
|
||||
// 4. Добавляем язык в заголовки ВСЕХ исходящих запросов
|
||||
response.headers.set("Accept-Language", lang);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
import React from "react";
|
||||
import { BreadCrumbs } from "../ui/bread-crumbs";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Title } from "../ui/title";
|
||||
import { usePathname } from "@/i18n/navigation";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
|
|
@ -26,20 +26,14 @@ export const LayoutWithSidebar = ({
|
|||
cursor = false,
|
||||
}: Props) => {
|
||||
const pathname = usePathname();
|
||||
const lang = useAppSelector(
|
||||
(state) => state.headerSlice.activeLang.localization
|
||||
);
|
||||
const t = useTranslations("services");
|
||||
|
||||
return (
|
||||
<div className="flex bg-white px-6 py-4 rounded-lg flex-col md:gap-6 gap-10 section-mb w-full">
|
||||
<div>
|
||||
<BreadCrumbs
|
||||
second={
|
||||
pathname.includes("/services")
|
||||
? lang === "en"
|
||||
? "Services"
|
||||
: "Сервисы"
|
||||
: second ?? ""
|
||||
pathname.includes("/services") ? t("breadcrumb") : second ?? ""
|
||||
}
|
||||
path={path}
|
||||
path2={path2}
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
import { topMenu } from "@/lib/database/pathnames";
|
||||
|
||||
export const Pathnames = ({ sort }: { sort: string }) => {
|
||||
return (
|
||||
<div className="text-[12px] text-gray4 flex items-center mob:mb-6 mb-5">
|
||||
<Link href={"/"}>{"Главная "}</Link>
|
||||
{topMenu
|
||||
.filter((item) => item.path === sort)
|
||||
.map((obj, i) =>
|
||||
obj.links.map((item) => <p key={i}>{item.default}</p>)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
|
||||
interface Props {
|
||||
img: string;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getEvents } from "@/services/home";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
|
||||
const events = [
|
||||
{
|
||||
|
|
@ -22,44 +22,18 @@ const events = [
|
|||
];
|
||||
|
||||
export const Events = async ({ lang }: { lang: string }) => {
|
||||
// const data = await getEvents(lang);
|
||||
|
||||
const btnText =
|
||||
lang === "en" ? "Show more" : lang === "ru" ? "Показать еще" : "Show more";
|
||||
|
||||
const title = lang === "en" ? "Exhibitions" : "Выставки";
|
||||
|
||||
const linkText = lang === "en" ? "Go to website" : "Перейти на сайт";
|
||||
const t = await getTranslations("home");
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="container md:block">
|
||||
<div className="sm:mb-10">
|
||||
<h2 className="text-center font-semibold text-3xl mb-8">
|
||||
{title} <span className="text-PRIMARY">Turkmen</span>
|
||||
{t("exhibitions")} <span className="text-PRIMARY">Turkmen</span>
|
||||
<span className="text-red">Expo 2025</span>
|
||||
</h2>
|
||||
{/* <Title
|
||||
text={
|
||||
lang === "en"
|
||||
? "Upcoming exhibitions and events"
|
||||
: lang === "ru"
|
||||
? "Ближайшие выставки и мероприятия"
|
||||
: "Upcoming exhibitions and events"
|
||||
}
|
||||
/> */}
|
||||
</div>
|
||||
{/* <div className="w-full flex flex-col items-center gap-8"> */}
|
||||
<div className="w-full flex flex-col items-center gap-8 lg:flex-row">
|
||||
{/* {data.data.slice(0, 2).map((item, i) => (
|
||||
<EventCard
|
||||
coorganizers={item.coorganizers}
|
||||
dark
|
||||
key={i}
|
||||
{...item}
|
||||
/>
|
||||
))}
|
||||
<LinkButton href="/calendar">{btnText}</LinkButton> */}
|
||||
{events.map((item, i) => (
|
||||
<article
|
||||
key={i}
|
||||
|
|
@ -87,15 +61,13 @@ export const Events = async ({ lang }: { lang: string }) => {
|
|||
target="_blank"
|
||||
className="text-PRIMARY underline"
|
||||
>
|
||||
{linkText}
|
||||
{t("goToWebsite")}
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <EventsMobile data={data.data.slice(0, 5)} /> */}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import { footerInfo, headerMenu2 } from "@/lib/database/pathnames";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import clsx from "clsx";
|
||||
import { headerMenu2 } from "@/lib/database/pathnames";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
|
||||
export const socials = [
|
||||
{ href: "https://www.linkedin.com/company/turkmen-expo", icon: "linkedin" },
|
||||
|
|
@ -24,9 +23,7 @@ export const socials = [
|
|||
];
|
||||
|
||||
export const Footer = () => {
|
||||
const localization = useAppSelector(
|
||||
(state) => state.headerSlice.activeLang.localization
|
||||
);
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<footer className="bg-PRIMARY text-white pt-6 pb-5 mob:py-[40px]">
|
||||
|
|
@ -36,40 +33,30 @@ export const Footer = () => {
|
|||
<img className="h-12" src="/assets/icons/white-logo.svg" />
|
||||
|
||||
<div className="flex flex-col mt-8 lg:flex-row items-center font-medium gap-12">
|
||||
{headerMenu2
|
||||
.filter((item) => (localization === "en" ? item.en : !item.en))
|
||||
.map((item, i) => (
|
||||
<Link key={i} href={item.link} className="cursor-pointer">
|
||||
{item.title}
|
||||
</Link>
|
||||
))}
|
||||
{headerMenu2.map((item, i) => (
|
||||
<Link key={i} href={item.link} className="cursor-pointer">
|
||||
{t(item.titleKey)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col lg:flex-row lg:justify-between justify-center text-center lg:text-left lg:items-end items-center gap-6">
|
||||
<div className="flex flex-col justify-end w-full">
|
||||
{localization === "ru" ? (
|
||||
<div className="flex flex-col md:gap-y-[10px] gap-2">
|
||||
{footerInfo.slice(0, 4).map((item, i) => (
|
||||
<p
|
||||
className={clsx("text-[12px] leading-[130%]", {
|
||||
"ml-8": i === 2,
|
||||
})}
|
||||
key={i}
|
||||
>
|
||||
{item}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
) : localization === "en" ? (
|
||||
<div className="flex flex-col md:gap-y-[10px] gap-0">
|
||||
{footerInfo.slice(3, 7).map((item, i) => (
|
||||
<p className="text-[12px] leading-[130%]" key={i}>
|
||||
{item}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex flex-col md:gap-y-[10px] gap-2">
|
||||
<p className="text-[12px] leading-[130%]">
|
||||
{t("footer.address")}
|
||||
</p>
|
||||
<p className="text-[12px] leading-[130%]">
|
||||
{t("footer.phone")}
|
||||
</p>
|
||||
<p className="text-[12px] leading-[130%] ml-8">
|
||||
{t("footer.phone2")}
|
||||
</p>
|
||||
<p className="text-[12px] leading-[130%]">
|
||||
{t("footer.email")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-6">
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"use client";
|
||||
|
||||
import React, { Suspense, useEffect, useState } from "react";
|
||||
import React, { Suspense, useState } from "react";
|
||||
import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { AnimatePresence } from "framer-motion";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import logo from "@/public/assets/icons/logo.svg";
|
||||
import search from "@/public/assets/icons/header/search.svg";
|
||||
|
|
@ -17,14 +17,14 @@ import { BurgerMenu } from "../ui/burger-menu";
|
|||
import { useAppDispatch, useAppSelector } from "@/redux/hooks";
|
||||
import { selectHeader, setShowInput } from "@/redux/slices/headerSlice";
|
||||
import { selectBurger, setBurgerOpen } from "@/redux/slices/burgerSlice";
|
||||
import { useStorage } from "@/hooks/useStorage";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
|
||||
export const Header = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { showInput } = useAppSelector(selectHeader);
|
||||
const { burgerOpen } = useAppSelector(selectBurger);
|
||||
const { activeLang } = useAppSelector(selectHeader);
|
||||
const [activeLink, setActiveLink] = useState("");
|
||||
const t = useTranslations();
|
||||
|
||||
const toggleMenu = () => {
|
||||
dispatch(setBurgerOpen(!burgerOpen));
|
||||
|
|
@ -36,12 +36,6 @@ export const Header = () => {
|
|||
dispatch(setBurgerOpen(false));
|
||||
};
|
||||
|
||||
const { setItem } = useStorage("language");
|
||||
|
||||
useEffect(() => {
|
||||
setItem(activeLang);
|
||||
}, [activeLang]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnimatePresence>
|
||||
|
|
@ -52,23 +46,17 @@ export const Header = () => {
|
|||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Mobile */}
|
||||
|
||||
{/* <div className="h-[74px] tab:hidden"> */}
|
||||
<div className="h-[70px]">
|
||||
<header
|
||||
className={clsx(
|
||||
"bg-[#EEF1F0] border-b z-30 border-[#DFE2E1] tab:hidden fixed top-0 left-0 right-0 flex items-center justify-between px-4 py-6",
|
||||
{
|
||||
// 'fixed w-full top-0': burgerOpen,
|
||||
}
|
||||
"bg-[#EEF1F0] border-b z-30 border-[#DFE2E1] tab:hidden fixed top-0 left-0 right-0 flex items-center justify-between px-4 py-6"
|
||||
)}
|
||||
>
|
||||
<Image
|
||||
src={searchMob}
|
||||
height={32}
|
||||
width={32}
|
||||
alt="поиск"
|
||||
alt="search"
|
||||
className="cursor-pointer"
|
||||
onClick={onSearch}
|
||||
/>
|
||||
|
|
@ -84,7 +72,7 @@ export const Header = () => {
|
|||
src={logo}
|
||||
height={24}
|
||||
width={160}
|
||||
alt="лого"
|
||||
alt="logo"
|
||||
className="cursor-pointer"
|
||||
/>
|
||||
</Link>
|
||||
|
|
@ -121,11 +109,7 @@ export const Header = () => {
|
|||
|
||||
<AnimatePresence>{burgerOpen && <BurgerMenu />}</AnimatePresence>
|
||||
</header>
|
||||
{/* </div> */}
|
||||
|
||||
{/* Desktop */}
|
||||
|
||||
{/* <div className="hidden h-[74px] tab:block"> */}
|
||||
<header className="hidden border-b border-[#DFE2E1] fixed top-0 left-0 right-0 z-30 tab:flex flex-col">
|
||||
<div className="bg-[#EEF1F0] text-black">
|
||||
<div className="container py-[17px] flex items-center justify-between">
|
||||
|
|
@ -139,7 +123,7 @@ export const Header = () => {
|
|||
</Suspense>
|
||||
<Image
|
||||
src={search}
|
||||
alt="поиск"
|
||||
alt="search"
|
||||
onClick={() => dispatch(setShowInput(true))}
|
||||
className="cursor-pointer"
|
||||
/>
|
||||
|
|
@ -147,25 +131,20 @@ export const Header = () => {
|
|||
</div>
|
||||
|
||||
<nav className="flex items-center gap-x-5 font-medium">
|
||||
{headerMenu2
|
||||
.filter((item) =>
|
||||
activeLang.localization === "en" ? item.en : !item.en
|
||||
)
|
||||
.map((item, i) => (
|
||||
<Link
|
||||
key={i}
|
||||
href={item.link}
|
||||
onClick={() => setActiveLink(item.link)}
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
))}
|
||||
{headerMenu2.map((item, i) => (
|
||||
<Link
|
||||
key={i}
|
||||
href={item.link}
|
||||
onClick={() => setActiveLink(item.link)}
|
||||
>
|
||||
{t(item.titleKey)}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
{/* </div> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ import { Navigation } from "swiper/modules";
|
|||
import { NewsCard } from "./news-card";
|
||||
import { NavBtn } from "../home/ui/NavBtn";
|
||||
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { selectHeader } from "@/redux/slices/headerSlice";
|
||||
import { NewsData } from "@/lib/types/NewsData.type";
|
||||
|
||||
import "swiper/css/bundle";
|
||||
|
|
@ -17,17 +15,18 @@ import "swiper/css";
|
|||
import "swiper/css/navigation";
|
||||
import "swiper/css/pagination";
|
||||
import "swiper/css/scrollbar";
|
||||
import Link from "next/link";
|
||||
import { useLang } from "@/utils/useLang";
|
||||
import { useTranslations, useLocale } from "next-intl";
|
||||
import { GreenBtn } from "../ui/Buttons";
|
||||
import { Title } from "../ui/title";
|
||||
import Loader from "@/components/ui/Loader";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
|
||||
export const News = () => {
|
||||
const [newsData, setNewsData] = useState<NewsData>();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const { activeLang } = useAppSelector(selectHeader);
|
||||
const t = useTranslations("home");
|
||||
const locale = useLocale();
|
||||
|
||||
const fetchNews = async () => {
|
||||
try {
|
||||
|
|
@ -35,7 +34,7 @@ export const News = () => {
|
|||
const response = await fetch(`https://turkmenexpo.com/app/api/v1/news`, {
|
||||
next: { revalidate: 1800 },
|
||||
headers: {
|
||||
"Accept-Language": activeLang.localization,
|
||||
"Accept-Language": locale,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -54,7 +53,7 @@ export const News = () => {
|
|||
|
||||
useEffect(() => {
|
||||
fetchNews();
|
||||
}, [activeLang.localization]);
|
||||
}, [locale]);
|
||||
|
||||
if (loading) {
|
||||
<Loader className="h-[300px] w-full mx-auto" />;
|
||||
|
|
@ -64,7 +63,7 @@ export const News = () => {
|
|||
<section>
|
||||
<div className="container w-full">
|
||||
<header className="flex items-center mb-5 sm:mb-[43px] justify-between">
|
||||
<Title text={useLang("News", "Новости", activeLang.localization)} />
|
||||
<Title text={t("news")} />
|
||||
<div className="hidden sm:flex items-center gap-5">
|
||||
<NavBtn left />
|
||||
<NavBtn />
|
||||
|
|
@ -105,9 +104,7 @@ export const News = () => {
|
|||
</div>
|
||||
|
||||
<Link href={"/news"} className="hidden sm:flex justify-center">
|
||||
<GreenBtn
|
||||
text={useLang("All news", "Все новости", activeLang.localization)}
|
||||
/>
|
||||
<GreenBtn text={t("allNews")} />
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -5,22 +5,21 @@ import Image from "next/image";
|
|||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
|
||||
import { Autoplay, Pagination } from "swiper/modules";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { selectHeader } from "@/redux/slices/headerSlice";
|
||||
import { PartnersType } from "@/lib/types/PartnersData.type";
|
||||
import { baseAPI } from "@/lib/API";
|
||||
import { useLang } from "@/utils/useLang";
|
||||
import { useTranslations, useLocale } from "next-intl";
|
||||
import { Title } from "../ui/title";
|
||||
|
||||
export const Partners = () => {
|
||||
const { activeLang } = useAppSelector(selectHeader);
|
||||
const locale = useLocale();
|
||||
const t = useTranslations("home");
|
||||
const [partnersData, setPartnersData] = useState<PartnersType>();
|
||||
|
||||
const fetchPartners = async () => {
|
||||
try {
|
||||
const res = await fetch(`${baseAPI}partners`, {
|
||||
headers: {
|
||||
"Accept-Language": activeLang.localization,
|
||||
"Accept-Language": locale,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -34,14 +33,12 @@ export const Partners = () => {
|
|||
|
||||
useEffect(() => {
|
||||
fetchPartners();
|
||||
}, [activeLang.localization]);
|
||||
}, [locale]);
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<div className="mb-[40px]">
|
||||
<Title
|
||||
text={useLang("Partners", "Партнёры", activeLang.localization)}
|
||||
/>
|
||||
<Title text={t("partners")} />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
|
|
|
|||
|
|
@ -7,31 +7,21 @@ import "swiper/css";
|
|||
import "swiper/css/pagination";
|
||||
import { useFetch } from "@/hooks/useFetch";
|
||||
import { ReviewsType } from "@/lib/types/Reviews.type";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { selectHeader } from "@/redux/slices/headerSlice";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Title } from "../ui/title";
|
||||
import Image from "next/image";
|
||||
|
||||
export const Reviews = () => {
|
||||
const { data } = useFetch<ReviewsType>("reviews");
|
||||
|
||||
const {
|
||||
activeLang: { localization },
|
||||
} = useAppSelector(selectHeader);
|
||||
|
||||
const title = localization === "en" ? "Feedback" : "Обратная связь";
|
||||
const t = useTranslations("home");
|
||||
|
||||
return (
|
||||
<section className="container my-20 pb-20 overflow-hidden">
|
||||
<Title text={title} className="!text-center mb-10" />
|
||||
<Title text={t("feedback")} className="!text-center mb-10" />
|
||||
|
||||
<div className="max-w-[1200px] mx-auto px-4 md:px-0">
|
||||
<Swiper
|
||||
modules={[Autoplay, Pagination]}
|
||||
// autoplay={{
|
||||
// delay: 6000,
|
||||
// disableOnInteraction: false,
|
||||
// }}
|
||||
style={{ overflow: "visible" }}
|
||||
freeMode
|
||||
speed={1000}
|
||||
|
|
@ -87,7 +77,6 @@ export const Reviews = () => {
|
|||
))}
|
||||
</Swiper>
|
||||
|
||||
{/* Кастомная пагинация */}
|
||||
<div className="custom-pagination flex justify-center items-center mt-60"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { useMediaQuery } from "usehooks-ts";
|
|||
import { Autoplay, Pagination } from "swiper/modules";
|
||||
import Loader from "../ui/Loader";
|
||||
import { baseAPI } from "@/lib/API";
|
||||
import Link from "next/link";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
import clsx from "clsx";
|
||||
|
||||
export const Slider = ({ lang }: { lang: string }) => {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,36 @@
|
|||
"use client";
|
||||
|
||||
import { FC, useState } from "react";
|
||||
import { Form, useForm } from "react-hook-form";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import clsx from "clsx";
|
||||
import { postContacts } from "@/services/contacts";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(2, "Имя необходимо"),
|
||||
email: z.string().email("Email необходим"),
|
||||
phone: z.string().min(8, "Номер телефона необходим"),
|
||||
company: z.string().optional(),
|
||||
msg: z.string().min(5, "Сообщение необходимо"),
|
||||
});
|
||||
|
||||
export type FormType = z.infer<typeof formSchema>;
|
||||
export type FormType = {
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
company?: string;
|
||||
msg: string;
|
||||
};
|
||||
|
||||
export const ContactsForm: FC<Props> = ({ className }) => {
|
||||
const [status, setStatus] = useState(false);
|
||||
const t = useTranslations();
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(2, t("validation.nameRequired")),
|
||||
email: z.string().email(t("validation.emailRequired")),
|
||||
phone: z.string().min(8, t("validation.phoneRequired")),
|
||||
company: z.string().optional(),
|
||||
msg: z.string().min(5, t("validation.messageRequired")),
|
||||
});
|
||||
|
||||
const { register, handleSubmit, formState, reset } = useForm({
|
||||
resolver: zodResolver(formSchema),
|
||||
|
|
@ -50,13 +58,13 @@ export const ContactsForm: FC<Props> = ({ className }) => {
|
|||
<div className={clsx("bg-PRIMARY rounded-[8px] py-8 px-6", className)}>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<h2 className="h2 !text-white text-3xl font-medium lg:mb-8 mb-6">
|
||||
Связаться с нами
|
||||
{t("contacts.contactUs")}
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col relative">
|
||||
<label htmlFor="name" className="label">
|
||||
Имя
|
||||
{t("contacts.name")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -70,7 +78,7 @@ export const ContactsForm: FC<Props> = ({ className }) => {
|
|||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 items-center">
|
||||
<div className="flex flex-col relative">
|
||||
<label htmlFor="email" className="label">
|
||||
E-mail
|
||||
{t("contacts.email")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -83,7 +91,7 @@ export const ContactsForm: FC<Props> = ({ className }) => {
|
|||
|
||||
<div className="flex flex-col relative">
|
||||
<label htmlFor="phone" className="label">
|
||||
Номер
|
||||
{t("contacts.phone")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -97,7 +105,7 @@ export const ContactsForm: FC<Props> = ({ className }) => {
|
|||
|
||||
<div className="flex flex-col relative">
|
||||
<label htmlFor="company" className="label">
|
||||
Название компании
|
||||
{t("contacts.companyName")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -110,7 +118,7 @@ export const ContactsForm: FC<Props> = ({ className }) => {
|
|||
|
||||
<div className="flex flex-col relative">
|
||||
<label htmlFor="msg" className="label">
|
||||
Сообщение
|
||||
{t("contacts.message")}
|
||||
</label>
|
||||
<textarea
|
||||
rows={3}
|
||||
|
|
@ -124,7 +132,7 @@ export const ContactsForm: FC<Props> = ({ className }) => {
|
|||
disabled={formState.isSubmitting || status}
|
||||
className="bg-[#A4FFF3] text-sm text-ON_PRIMARY_CONTAINER h-10 rounded-[2px]"
|
||||
>
|
||||
{!status ? "Отправить" : "Форма отправлена"}
|
||||
{!status ? t("common.send") : t("common.formSubmitted")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@ import {
|
|||
Organizer,
|
||||
Timing,
|
||||
} from "@/lib/types/Calendar.type";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { useLang } from "@/utils/useLang";
|
||||
import Link from "next/link";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
|
||||
interface Props {
|
||||
id: number;
|
||||
|
|
@ -54,9 +53,7 @@ export const EventCard = ({
|
|||
return formattedDate;
|
||||
};
|
||||
|
||||
const lang = useAppSelector(
|
||||
(state) => state.headerSlice.activeLang.localization
|
||||
);
|
||||
const t = useTranslations("events");
|
||||
|
||||
return (
|
||||
<Link className="w-full cursor-default" href={`/calendar/${id}`}>
|
||||
|
|
@ -97,7 +94,6 @@ export const EventCard = ({
|
|||
className="lg:size-[228px] size-[100px] object-cover rounded-l-[4px]"
|
||||
/>
|
||||
|
||||
{/* CENTER */}
|
||||
<div className="flex flex-col gap-[10px] md:gap-4 max-w-[683px] py-6 w-full">
|
||||
<p className="text-[12px] text-[#6B7674] font-normal leading-none">
|
||||
{category}
|
||||
|
|
@ -110,13 +106,10 @@ export const EventCard = ({
|
|||
>
|
||||
{title}
|
||||
</h3>
|
||||
{/* <p className={clsx('text-base line-clamp-2 leading-[130%] md:leading-[150%]', {})}>
|
||||
{description}
|
||||
</p> */}
|
||||
</div>
|
||||
<div className="flex flex-col gap-[10px]">
|
||||
<p className="text-[#6B7674] uppercase font-normal leading-none text-[10px]">
|
||||
{useLang("Organiser:", "Организатор:", lang)}
|
||||
{t("organiserLabel")}
|
||||
</p>
|
||||
<p className="text-[#6B7674] font-normal text-[13px] leading-[125%]">
|
||||
{organizers ? organizers[0]?.name : null}
|
||||
|
|
@ -125,7 +118,7 @@ export const EventCard = ({
|
|||
{coorganizers && coorganizers?.length > 0 && (
|
||||
<div className="flex flex-col gap-[10px]">
|
||||
<p className="text-[#6B7674] uppercase font-normal leading-none text-[10px]">
|
||||
{useLang("Co-organiser:", "Со-организатор:", lang)}
|
||||
{t("coOrganiserLabel")}
|
||||
</p>
|
||||
<p className="text-[#6B7674] font-normal text-[13px] leading-[125%]">
|
||||
{coorganizers ? coorganizers[0]?.name : null}
|
||||
|
|
@ -133,7 +126,6 @@ export const EventCard = ({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* CENTER */}
|
||||
|
||||
<div className="hidden md:flex text-2xl flex-col text-white leading- none h-[228px] min-w-[245px] items-center justify-center bg-[#31A898] px-12 text-center">
|
||||
<p className="">{formatDate(starts_at)}</p>{" "}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import Link from "next/link";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
import { FC } from "react";
|
||||
|
||||
interface Props {
|
||||
|
|
@ -18,9 +18,7 @@ export const EventPageButtons: FC<Props> = ({
|
|||
visit,
|
||||
details,
|
||||
}) => {
|
||||
const lang = useAppSelector(
|
||||
(state) => state.headerSlice.activeLang.localization
|
||||
);
|
||||
const t = useTranslations("events");
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-6">
|
||||
|
|
@ -28,19 +26,19 @@ export const EventPageButtons: FC<Props> = ({
|
|||
<>
|
||||
<Link href={register || ""} className="w-full" target="_blank">
|
||||
<button className="full-btn bg-PRIMARY py-2.5 text-white">
|
||||
{lang === "en" ? "Registration" : "Зарегистрироваться"}
|
||||
{t("registration")}
|
||||
</button>
|
||||
</Link>
|
||||
<Link href={visit || ""} className="w-full" target="_blank">
|
||||
<button className="full-btn bg-SECONDARY_CONTAINER py-2.5 text-ON_SECONDARY_CONTAINER">
|
||||
{lang === "en" ? "Visit site" : "Посетить сайт"}
|
||||
{t("visitSite")}
|
||||
</button>
|
||||
</Link>
|
||||
</>
|
||||
) : (
|
||||
<Link href={details || ""} className="w-full" target="_blank">
|
||||
<button className="full-btn w-full bg-PRIMARY py-2.5 text-white">
|
||||
{lang === "en" ? "More" : "Подробнее"}
|
||||
{t("more")}
|
||||
</button>
|
||||
</Link>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@
|
|||
|
||||
import "swiper/css";
|
||||
import "swiper/css/pagination";
|
||||
// import "./styles/events.css";
|
||||
|
||||
import { useLang } from "@/utils/useLang";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { FC } from "react";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import { EventCard } from "./event-card";
|
||||
import { CalendarType } from "@/lib/types/Calendar.type";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
|
||||
interface Props {
|
||||
data: CalendarType["data"];
|
||||
|
|
@ -17,17 +15,12 @@ interface Props {
|
|||
}
|
||||
|
||||
export const EventsMobile: FC<Props> = ({ data }) => {
|
||||
const lang = useAppSelector(
|
||||
(state) => state.headerSlice.activeLang.localization
|
||||
);
|
||||
const t = useTranslations("home");
|
||||
|
||||
return (
|
||||
<div className="md:hidden container">
|
||||
<h2 className="text-[26px] mb-5 sm:mb-10 font-semibold leading-[115%]">
|
||||
{useLang(
|
||||
"Upcoming exhibitions and events",
|
||||
"Ближайшие выставки и мероприятия",
|
||||
lang
|
||||
)}
|
||||
{t("upcomingExhibitions")}
|
||||
</h2>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex items-center gap-y-[10px]">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { Link } from '@/i18n/navigation';
|
||||
|
||||
interface Props {
|
||||
img: string;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
"use client";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import { useAppDispatch, useAppSelector } from "@/redux/hooks";
|
||||
import close from "@/public/assets/icons/home/close-input.svg";
|
||||
|
||||
import { selectInput, setInputStatus } from "@/redux/slices/inputSlice";
|
||||
import { v4 } from "uuid";
|
||||
|
|
@ -12,20 +10,14 @@ import { selectHeader, setShowInput } from "@/redux/slices/headerSlice";
|
|||
import clsx from "clsx";
|
||||
import { selectBurger } from "@/redux/slices/burgerSlice";
|
||||
import { baseAPI } from "@/lib/API";
|
||||
import Link from "next/link";
|
||||
import { SearchTypes } from "@/lib/types/Search.type";
|
||||
import { useMediaQuery } from "usehooks-ts";
|
||||
|
||||
export const inputRadio = [
|
||||
{ name: "Везде", id: "all" },
|
||||
{ name: "В событиях", id: "events" },
|
||||
{ name: "В новостях", id: "news" },
|
||||
];
|
||||
import { useTranslations, useLocale } from "next-intl";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
|
||||
export const SearchInput = ({ mob = false }: { mob?: boolean }) => {
|
||||
const localization = useAppSelector(
|
||||
(state) => state.headerSlice.activeLang.localization
|
||||
);
|
||||
const locale = useLocale();
|
||||
const t = useTranslations("search");
|
||||
const wrapper = document.querySelector(".wrapper");
|
||||
|
||||
const tab = useMediaQuery("(min-width: 980px)");
|
||||
|
|
@ -38,12 +30,17 @@ export const SearchInput = ({ mob = false }: { mob?: boolean }) => {
|
|||
|
||||
const { showInput } = useAppSelector(selectHeader);
|
||||
|
||||
const inputRadio = [
|
||||
{ name: t("all"), id: "all" },
|
||||
{ name: t("inEvents"), id: "events" },
|
||||
{ name: t("inNews"), id: "news" },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
wrapper?.classList.remove("overflow-hidden");
|
||||
wrapper?.classList.add("overflow-hidden");
|
||||
|
||||
setStatus(inputRadio[0].id);
|
||||
inputRadio;
|
||||
|
||||
return () => {
|
||||
wrapper?.classList.remove("overflow-hidden");
|
||||
|
|
@ -56,7 +53,7 @@ export const SearchInput = ({ mob = false }: { mob?: boolean }) => {
|
|||
try {
|
||||
const res = await fetch(`${baseAPI}search?search=${value}`, {
|
||||
headers: {
|
||||
"Accept-Language": localization,
|
||||
"Accept-Language": locale,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
|
@ -135,7 +132,7 @@ export const SearchInput = ({ mob = false }: { mob?: boolean }) => {
|
|||
}
|
||||
value={value}
|
||||
type="text"
|
||||
placeholder="Что найти?"
|
||||
placeholder={t("placeholder")}
|
||||
className="p-3 w-full leading-[150%] placeholder:leading-[150%] placeholder:text-gray focus:outline-none rounded-sm bg-transparent border-[1px] border-[#BCC4CC]"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -167,7 +164,7 @@ export const SearchInput = ({ mob = false }: { mob?: boolean }) => {
|
|||
</div>
|
||||
) : (
|
||||
<div className="bg-green py-[17px] px-[70px] rounded-sm">
|
||||
Найти
|
||||
{t("findButton")}
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
|
|
@ -177,11 +174,12 @@ export const SearchInput = ({ mob = false }: { mob?: boolean }) => {
|
|||
searchData.data.posts.length ? (
|
||||
<>
|
||||
<div className="mb-12 font-light">
|
||||
По запросу « <span className="font-bold">{searchValue}</span>{" "}
|
||||
» нашлось
|
||||
{t("byRequest")}
|
||||
<span className="font-bold">{searchValue}</span>
|
||||
{t("found")}{" "}
|
||||
{searchData.data.expo_events.length +
|
||||
searchData.data.posts.length}{" "}
|
||||
результатов
|
||||
{t("results")}
|
||||
</div>
|
||||
<div className="flex flex-col gap-9 w-full tab:mb-0 mb-20">
|
||||
{inputStatus === "all" || inputStatus === "events"
|
||||
|
|
@ -191,7 +189,7 @@ export const SearchInput = ({ mob = false }: { mob?: boolean }) => {
|
|||
{item.title}
|
||||
</h3>
|
||||
<Link href={`/calendar/${item.id}`}>
|
||||
Перейти на страницу
|
||||
{t("goToPage")}
|
||||
</Link>
|
||||
<hr className="mt-9 border-OUTLINE_VAR" />
|
||||
</div>
|
||||
|
|
@ -204,7 +202,7 @@ export const SearchInput = ({ mob = false }: { mob?: boolean }) => {
|
|||
{item.title}
|
||||
</h3>
|
||||
<Link href={`/news/${item.id}`}>
|
||||
Перейти на страницу
|
||||
{t("goToPage")}
|
||||
</Link>
|
||||
<hr className="mt-9 border-OUTLINE_VAR" />
|
||||
</div>
|
||||
|
|
@ -213,7 +211,7 @@ export const SearchInput = ({ mob = false }: { mob?: boolean }) => {
|
|||
</div>
|
||||
</>
|
||||
) : (
|
||||
<h3>По вашему запросу ничего не найдено</h3>
|
||||
<h3>{t("noResults")}</h3>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,13 +4,10 @@ import React from "react";
|
|||
import Image from "next/image";
|
||||
|
||||
import { contactCardData, roadCardData } from "@/lib/database/homeInfoData";
|
||||
import { useLang } from "@/utils/useLang";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export const Services = () => {
|
||||
const localization = useAppSelector(
|
||||
(state) => state.headerSlice.activeLang.localization
|
||||
);
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -18,7 +15,7 @@ export const Services = () => {
|
|||
>
|
||||
<div className="flex flex-col items-start gap-x-5 w-full">
|
||||
<h3 className="text-[21px] leading-[100%] font-bold text-bgWhite md:text-[#B0E6A1] uppercase md:mb-10 mb-5">
|
||||
{useLang("Routes", "как добраться", localization)}
|
||||
{t("services.routes")}
|
||||
</h3>
|
||||
<div className="flex flex-col gap-y-5 w-full">
|
||||
{roadCardData.map((item, i) => (
|
||||
|
|
@ -29,7 +26,7 @@ export const Services = () => {
|
|||
<div className="flex items-center gap-10 md:gap-5">
|
||||
<Image className="img-auto" src={item.icon} alt="icon" />
|
||||
<p className="text-[16px] leading-[120%] md:leading-[100%]">
|
||||
{localization === "ru" ? item.text : item.enText}
|
||||
{t(item.textKey)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -38,7 +35,7 @@ export const Services = () => {
|
|||
</div>
|
||||
<div className="flex flex-col items-start md:mt-0 mt-10 gap-x-5 w-full">
|
||||
<h3 className="text-[21px] leading-[100%] text-bgWhite md:text-[#FFD288] uppercase font-bold md:mb-10 mb-5">
|
||||
{useLang("Contacts", "контакты", localization)}
|
||||
{t("contacts.title")}
|
||||
</h3>
|
||||
<div className="flex flex-col gap-y-5 w-full">
|
||||
{contactCardData.map((item, i) => (
|
||||
|
|
@ -49,7 +46,7 @@ export const Services = () => {
|
|||
<div className="flex items-center md:gap-5 gap-10">
|
||||
<Image src={item.icon} alt="icon" />
|
||||
<p className="text-[16px] leading-[120%] md:leading-[100%]">
|
||||
{localization === "ru" ? item.text : item.enText}
|
||||
{t(item.textKey)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { useMediaQuery } from "usehooks-ts";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { useSliderBanner } from "@/hooks/use-slider";
|
||||
import Loader from "../ui/Loader";
|
||||
import { useLocale } from "next-intl";
|
||||
|
||||
export const SliderClient = () => {
|
||||
const isTab = useMediaQuery("(min-width: 1024px)");
|
||||
|
|
@ -14,16 +14,14 @@ export const SliderClient = () => {
|
|||
const [data, setData] = useState<any>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const bannerType = useSliderBanner(isTab, isMd);
|
||||
const lang = useAppSelector(
|
||||
(state) => state.headerSlice.activeLang.localization
|
||||
);
|
||||
const locale = useLocale();
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const res = await fetch(`/api/banners?bannerType=${bannerType}`, {
|
||||
headers: {
|
||||
"Accept-Language": lang,
|
||||
"Accept-Language": locale,
|
||||
},
|
||||
});
|
||||
const json = await res.json();
|
||||
|
|
@ -36,7 +34,7 @@ export const SliderClient = () => {
|
|||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [bannerType, lang]);
|
||||
}, [bannerType, locale]);
|
||||
|
||||
if (loading) return <Loader className="h-[600px] min-h-[320px]" />;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,51 +6,27 @@ import clsx from "clsx";
|
|||
import { motion, AnimatePresence } from "framer-motion";
|
||||
|
||||
import triangle from "@/public/assets/icons/drop-icon.svg";
|
||||
import { useAppSelector, useAppDispatch } from "@/redux/hooks";
|
||||
import { activeLangType, selectHeader } from "@/redux/slices/headerSlice";
|
||||
import { setActiveLang } from "@/redux/slices/headerSlice";
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useLocale } from "next-intl";
|
||||
import { useRouter, usePathname } from "@/i18n/navigation";
|
||||
import { locales } from "@/i18n/config";
|
||||
|
||||
export const lang = [
|
||||
{
|
||||
title: "Русский",
|
||||
localization: "ru",
|
||||
},
|
||||
// {
|
||||
// title: 'Tm',
|
||||
// localization: 'tm',
|
||||
// },
|
||||
{
|
||||
title: "English",
|
||||
localization: "en",
|
||||
},
|
||||
];
|
||||
const langNames: Record<string, string> = {
|
||||
ru: "Русский",
|
||||
en: "English",
|
||||
};
|
||||
|
||||
export const LangMenu = () => {
|
||||
const { activeLang } = useAppSelector(selectHeader);
|
||||
const { refresh } = useRouter();
|
||||
const locale = useLocale();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const [active, setActive] = useState(false);
|
||||
const dispatch = useAppDispatch();
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const setLang = (lang: activeLangType) => {
|
||||
// Устанавливаем cookie через document.cookie
|
||||
document.cookie = `lang=${lang.localization}; path=/; max-age=31536000`;
|
||||
dispatch(setActiveLang(lang));
|
||||
refresh();
|
||||
|
||||
const switchLocale = (newLocale: string) => {
|
||||
router.replace(pathname, { locale: newLocale });
|
||||
setActive(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const cookieLang = document.cookie
|
||||
.split("; ")
|
||||
.find((row) => row.startsWith("lang="))
|
||||
?.split("=")[1];
|
||||
|
||||
if (cookieLang) {
|
||||
const foundLang = lang.find((l) => l.localization === cookieLang);
|
||||
if (foundLang) dispatch(setActiveLang(foundLang));
|
||||
}
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
|
||||
|
|
@ -72,7 +48,7 @@ export const LangMenu = () => {
|
|||
}}
|
||||
>
|
||||
<div className="flex items-center px-[12px]">
|
||||
<p>{activeLang.title}</p>
|
||||
<p>{langNames[locale]}</p>
|
||||
<Image
|
||||
src={triangle}
|
||||
alt="arrow"
|
||||
|
|
@ -98,21 +74,15 @@ export const LangMenu = () => {
|
|||
}}
|
||||
className="absolute rounded-[4px] w-[112px] overflow-hidden custom-shadow z-10 flex flex-col top-[27px] bg-[#EEF1F0] transition-all duration-300"
|
||||
>
|
||||
{lang
|
||||
.filter((item) => item.title !== activeLang.title)
|
||||
.map((item, i) => (
|
||||
{locales
|
||||
.filter((l) => l !== locale)
|
||||
.map((l, i) => (
|
||||
<div
|
||||
key={i}
|
||||
onClick={() => setLang(item)}
|
||||
className={clsx(
|
||||
"p-3 pr-[22px] py-4 text-extraSm transition-all",
|
||||
{
|
||||
"hover:bg-ON_SECONDARY_CONTAINER/[8%]":
|
||||
item.title === item.title,
|
||||
}
|
||||
)}
|
||||
onClick={() => switchLocale(l)}
|
||||
className="p-3 pr-[22px] py-4 text-extraSm transition-all hover:bg-ON_SECONDARY_CONTAINER/[8%]"
|
||||
>
|
||||
{item.title}
|
||||
{langNames[l]}
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import React, { Dispatch, SetStateAction } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface IProps {
|
||||
lastPage?: number;
|
||||
|
|
@ -11,6 +12,8 @@ interface IProps {
|
|||
}
|
||||
|
||||
export const Pagination = ({ current, setCurrent, lastPage = 3 }: IProps) => {
|
||||
const t = useTranslations("common");
|
||||
|
||||
const onNext = () => {
|
||||
setCurrent(current + 1);
|
||||
};
|
||||
|
|
@ -39,7 +42,9 @@ export const Pagination = ({ current, setCurrent, lastPage = 3 }: IProps) => {
|
|||
<div className="border-[1px] border-OUTLINE_VAR rounded-sm px-3 py-[9px]">
|
||||
{current}
|
||||
</div>
|
||||
<p>из {lastPage}</p>
|
||||
<p>
|
||||
{t("of")} {lastPage}
|
||||
</p>
|
||||
<button onClick={onNext} disabled={current >= lastPage} type="button">
|
||||
<svg
|
||||
width="32"
|
||||
|
|
|
|||
|
|
@ -1,19 +1,15 @@
|
|||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import clsx from "clsx";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import { sidebarData } from "@/lib/database/pathnames";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { Link, usePathname } from "@/i18n/navigation";
|
||||
|
||||
export const Sidebar = () => {
|
||||
const pathname = usePathname();
|
||||
|
||||
const lang = useAppSelector(
|
||||
(state) => state.headerSlice.activeLang.localization
|
||||
);
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col bg-white rounded-lg px-6 py-4 items-start gap-y-[12px] pb-5 mt-12 sticky top-0 left-0 overflow-hidden">
|
||||
|
|
@ -31,7 +27,7 @@ export const Sidebar = () => {
|
|||
<p
|
||||
className={clsx("mb-[16px] text-[16px] font-bold leading-[1.5]")}
|
||||
>
|
||||
{lang === "ru" ? item.pathname : item.pathnameEn}
|
||||
{t(item.pathnameKey)}
|
||||
</p>
|
||||
<div className="flex flex-col items-start gap-y-[8px]">
|
||||
<div className="flex flex-col gap-[10px] pl-4 pr-10">
|
||||
|
|
@ -47,7 +43,7 @@ export const Sidebar = () => {
|
|||
)}
|
||||
key={i}
|
||||
>
|
||||
{lang === "ru" ? obj.title : obj.titleEn}
|
||||
{t(obj.titleKey)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { selectHeader } from "@/redux/slices/headerSlice";
|
||||
import { useLang } from "@/utils/useLang";
|
||||
import Link from "next/link";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
|
||||
export const BreadCrumbs = ({
|
||||
second,
|
||||
|
|
@ -18,12 +16,12 @@ export const BreadCrumbs = ({
|
|||
path2?: string;
|
||||
cursor?: boolean;
|
||||
}) => {
|
||||
const { activeLang } = useAppSelector(selectHeader);
|
||||
const t = useTranslations("common");
|
||||
|
||||
return (
|
||||
<div className="text-[12px] text-[#6B7674] flex items-center mob:mb-6 mb-5">
|
||||
<Link href={"/"}>
|
||||
{useLang("Home", "Главная", activeLang.localization)}
|
||||
{t("home")}
|
||||
</Link>
|
||||
|
||||
<p className="px-1">/</p>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import { burgerMenuData } from "@/lib/database/pathnames";
|
||||
import { useAppDispatch, useAppSelector } from "@/redux/hooks";
|
||||
import Link from "next/link";
|
||||
import { useAppDispatch } from "@/redux/hooks";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import { v4 } from "uuid";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import arrow from "@/public/assets/icons/header/burger-arrow.svg";
|
||||
import {
|
||||
|
|
@ -22,6 +23,7 @@ export const BurgerDrop = ({
|
|||
setDrop: (name: boolean) => void;
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const t = useTranslations();
|
||||
|
||||
return burgerMenuData
|
||||
.filter((obj) => !obj.only)
|
||||
|
|
@ -38,7 +40,7 @@ export const BurgerDrop = ({
|
|||
className="cursor-pointer flex items-center gap-[10px] mb-[10px]"
|
||||
>
|
||||
<Image src={arrow} alt="стрелка" className="rotate-180" />
|
||||
<h3 className="leading-[135%] text-[18px]">{item.title}</h3>
|
||||
<h3 className="leading-[135%] text-[18px]">{t(item.titleKey)}</h3>
|
||||
</div>
|
||||
<hr className="border-bgWhite mb-5" />
|
||||
|
||||
|
|
@ -53,7 +55,7 @@ export const BurgerDrop = ({
|
|||
setDrop(false);
|
||||
}}
|
||||
>
|
||||
{subj.title}
|
||||
{t(subj.titleKey)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,88 +1,43 @@
|
|||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { v4 } from "uuid";
|
||||
import { motion } from "framer-motion";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
|
||||
import { useAppDispatch, useAppSelector } from "@/redux/hooks";
|
||||
import { useAppDispatch } from "@/redux/hooks";
|
||||
import { setBurgerOpen } from "@/redux/slices/burgerSlice";
|
||||
import { activeLangType, setActiveLang } from "@/redux/slices/headerSlice";
|
||||
import clsx from "clsx";
|
||||
import { burgerMenu, burgerMenu2 } from "@/lib/database/header";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { lang } from "./LangMenu";
|
||||
import { useRouter, usePathname } from "@/i18n/navigation";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
import { locales } from "@/i18n/config";
|
||||
|
||||
interface flagTypes {
|
||||
// title: 'Ру' | 'En' | 'Tm';
|
||||
title: "Ру" | "En";
|
||||
localization: "ru" | "en";
|
||||
// localization: 'ru' | 'en' | 'tm';
|
||||
}
|
||||
|
||||
const burgerLangs: flagTypes[] = [
|
||||
// {
|
||||
// title: 'Tm',
|
||||
// localization: 'tm',
|
||||
// },
|
||||
{
|
||||
title: "Ру",
|
||||
localization: "ru",
|
||||
},
|
||||
{
|
||||
title: "En",
|
||||
localization: "en",
|
||||
},
|
||||
const burgerLangs = [
|
||||
{ title: "Ру", localization: "ru" as const },
|
||||
{ title: "En", localization: "en" as const },
|
||||
];
|
||||
|
||||
export const BurgerMenu = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const wrapper = document.querySelector(".wrapper");
|
||||
const { refresh } = useRouter();
|
||||
|
||||
const localization = useAppSelector(
|
||||
(state) => state.headerSlice.activeLang.localization
|
||||
);
|
||||
const locale = useLocale();
|
||||
const t = useTranslations();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
const [activeMenu, setActiveMenu] = useState<string>("");
|
||||
const [activeMenu2, setActiveMenu2] = useState<string>("");
|
||||
|
||||
const setActiveTitle = () => {
|
||||
if (activeMenu.includes("/ser"))
|
||||
return (
|
||||
(localization === "ru" && "Услуги") ||
|
||||
(localization === "en" && "Services")
|
||||
);
|
||||
if (activeMenu.includes("/ser")) return t("nav.services");
|
||||
};
|
||||
|
||||
const setActiveTitle2 = () => {
|
||||
if (activeMenu2.includes("/company"))
|
||||
return (
|
||||
(localization === "ru" && "О компании") ||
|
||||
(localization === "en" && "About company")
|
||||
);
|
||||
};
|
||||
|
||||
const setLang = (lang: activeLangType) => {
|
||||
// Устанавливаем cookie через document.cookie
|
||||
document.cookie = `lang=${lang.localization}; path=/; max-age=31536000`;
|
||||
dispatch(setActiveLang(lang));
|
||||
refresh();
|
||||
if (activeMenu2.includes("/company")) return t("nav.about");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const cookieLang = document.cookie
|
||||
.split("; ")
|
||||
.find((row) => row.startsWith("lang="))
|
||||
?.split("=")[1];
|
||||
|
||||
if (cookieLang) {
|
||||
const foundLang = lang.find((l) => l.localization === cookieLang);
|
||||
if (foundLang) dispatch(setActiveLang(foundLang));
|
||||
}
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
const wrapper = document.querySelector(".wrapper");
|
||||
wrapper?.classList.remove("overflow-hidden");
|
||||
wrapper?.classList.add("overflow-hidden");
|
||||
|
||||
|
|
@ -145,7 +100,7 @@ export const BurgerMenu = () => {
|
|||
onClick={() => dispatch(setBurgerOpen(false))}
|
||||
href={obj.link}
|
||||
>
|
||||
{localization === "en" ? obj.titleEn : obj.title}
|
||||
{t(obj.titleKey)}
|
||||
</Link>
|
||||
))
|
||||
)}
|
||||
|
|
@ -163,8 +118,7 @@ export const BurgerMenu = () => {
|
|||
}}
|
||||
href={item.link}
|
||||
>
|
||||
{(localization === "en" && item.titleEn) ||
|
||||
(localization === "ru" && item.title)}
|
||||
{t(item.titleKey)}
|
||||
</Link>
|
||||
) : (
|
||||
<div
|
||||
|
|
@ -174,8 +128,7 @@ export const BurgerMenu = () => {
|
|||
setActiveMenu(item.link);
|
||||
}}
|
||||
>
|
||||
{(localization === "en" && item.titleEn) ||
|
||||
(localization === "ru" && item.title)}
|
||||
{t(item.titleKey)}
|
||||
{item.drop && (
|
||||
<svg
|
||||
width="24"
|
||||
|
|
@ -234,8 +187,7 @@ export const BurgerMenu = () => {
|
|||
dispatch(setBurgerOpen(false));
|
||||
}}
|
||||
>
|
||||
{(localization === "en" && item.titleEn) ||
|
||||
(localization === "ru" && item.title)}
|
||||
{t(item.titleKey)}
|
||||
</Link>
|
||||
))
|
||||
)}
|
||||
|
|
@ -246,9 +198,8 @@ export const BurgerMenu = () => {
|
|||
<div
|
||||
key={i}
|
||||
onClick={() => {
|
||||
dispatch(setActiveLang(item));
|
||||
router.replace(pathname, { locale: item.localization });
|
||||
dispatch(setBurgerOpen(false));
|
||||
setLang(item);
|
||||
}}
|
||||
className="flex cursor-pointer items-center gap-[10px]"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { baseAPI } from "@/lib/API";
|
||||
import { useAppSelector } from "@/redux/hooks";
|
||||
import { selectHeader } from "@/redux/slices/headerSlice";
|
||||
import { useLocale } from "next-intl";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
interface FetchState<T> {
|
||||
|
|
@ -21,9 +20,7 @@ export const useFetch = <T>(
|
|||
error: null,
|
||||
});
|
||||
|
||||
const {
|
||||
activeLang: { localization },
|
||||
} = useAppSelector(selectHeader);
|
||||
const locale = useLocale();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
|
|
@ -32,7 +29,7 @@ export const useFetch = <T>(
|
|||
|
||||
const response = await fetch(baseAPI + url, {
|
||||
headers: {
|
||||
"Accept-Language": localization,
|
||||
"Accept-Language": locale,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -57,13 +54,7 @@ export const useFetch = <T>(
|
|||
};
|
||||
|
||||
fetchData();
|
||||
}, [url, localization]); // Добавьте options в зависимости, если они динамические
|
||||
}, [url, locale]);
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
// Пример использования
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
export const locales = ["ru", "en"] as const;
|
||||
export type Locale = (typeof locales)[number];
|
||||
export const defaultLocale: Locale = "ru";
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { createNavigation } from "next-intl/navigation";
|
||||
import { locales, defaultLocale } from "./config";
|
||||
|
||||
export const { Link, redirect, usePathname, useRouter } = createNavigation({
|
||||
locales,
|
||||
defaultLocale,
|
||||
});
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { getRequestConfig } from "next-intl/server";
|
||||
import { locales, type Locale } from "./config";
|
||||
|
||||
export default getRequestConfig(async ({ requestLocale }) => {
|
||||
let locale = await requestLocale;
|
||||
|
||||
if (!locale || !locales.includes(locale as Locale)) {
|
||||
locale = "ru";
|
||||
}
|
||||
|
||||
return {
|
||||
locale,
|
||||
messages: (await import(`../messages/${locale}.json`)).default,
|
||||
};
|
||||
});
|
||||
|
|
@ -1,123 +1,82 @@
|
|||
export const burgerMenu = [
|
||||
interface BurgerMenuItem {
|
||||
titleKey: string;
|
||||
link: string;
|
||||
services?: boolean;
|
||||
drop?: boolean;
|
||||
dropDown?: {
|
||||
titleKey: string;
|
||||
link: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export const burgerMenu: BurgerMenuItem[] = [
|
||||
{
|
||||
title: "Календарь мероприятий",
|
||||
titleEn: "Calendar of events",
|
||||
titleKey: "nav.calendar",
|
||||
link: "/calendar",
|
||||
},
|
||||
{
|
||||
services: true,
|
||||
title: "Услуги",
|
||||
titleKey: "nav.services",
|
||||
drop: true,
|
||||
titleEn: "Services",
|
||||
link: "/services/organization",
|
||||
|
||||
dropDown: [
|
||||
{
|
||||
title: "Организация выставок",
|
||||
titleEn: "Organization of exhibitions",
|
||||
link: "/services/organization",
|
||||
},
|
||||
{
|
||||
title: "B2B Встречи и Матчмейкинг",
|
||||
titleEn: "B2B Meetings & Business Matchmaking",
|
||||
link: "/services/meetings",
|
||||
},
|
||||
{
|
||||
title: "Бизнес-Миссии",
|
||||
titleEn: "Business Missions",
|
||||
link: "/services/missions",
|
||||
},
|
||||
|
||||
{
|
||||
title: "Рекламная деятельность",
|
||||
titleEn: "Advertising activity",
|
||||
link: "/services/advertising",
|
||||
},
|
||||
{
|
||||
title: "Организация гибридных мероприятий",
|
||||
titleEn: "Organization of hybrid events",
|
||||
link: "/services/hybrid-organizations",
|
||||
},
|
||||
{
|
||||
title: "Сертификация",
|
||||
titleEn: "Certification",
|
||||
link: "/services/certification",
|
||||
},
|
||||
{
|
||||
title: "Экспедирование грузов и таможенное оформление",
|
||||
titleEn: "Freight forwarding and customs clearance",
|
||||
link: "/services/forwarding",
|
||||
},
|
||||
{
|
||||
title: "Аутсорсинг бух.учета и аудита",
|
||||
titleEn: "Outsourcing of accounting and auditing",
|
||||
link: "/services/outsourcing",
|
||||
},
|
||||
{
|
||||
title: "Обучения персонала",
|
||||
titleEn: "Personnel-training",
|
||||
link: "/services/personnel-training",
|
||||
},
|
||||
{
|
||||
title: "Бизнес - туры",
|
||||
titleEn: "Business - tours",
|
||||
link: "/services/business-tours",
|
||||
},
|
||||
{ titleKey: "sidebar.organizeExhibitions", link: "/services/organization" },
|
||||
{ titleKey: "sidebar.b2bMeetings", link: "/services/meetings" },
|
||||
{ titleKey: "sidebar.businessMissions", link: "/services/missions" },
|
||||
{ titleKey: "burger.advertisingActivity", link: "/services/advertising" },
|
||||
{ titleKey: "sidebar.hybridEvents", link: "/services/hybrid-organizations" },
|
||||
{ titleKey: "sidebar.certification", link: "/services/certification" },
|
||||
{ titleKey: "burger.freightForwarding", link: "/services/forwarding" },
|
||||
{ titleKey: "burger.accountingOutsourcing", link: "/services/outsourcing" },
|
||||
{ titleKey: "burger.personnelTraining", link: "/services/personnel-training" },
|
||||
{ titleKey: "sidebar.businessTours", link: "/services/business-tours" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "О компании",
|
||||
titleEn: "Calendar of events",
|
||||
titleKey: "nav.about",
|
||||
link: "/company/aboutus",
|
||||
},
|
||||
{
|
||||
title: "Новости",
|
||||
titleEn: "News",
|
||||
titleKey: "nav.news",
|
||||
link: "/news",
|
||||
},
|
||||
{
|
||||
title: "Контакты",
|
||||
titleEn: "Contacts",
|
||||
titleKey: "nav.contacts",
|
||||
link: "/contacts",
|
||||
},
|
||||
|
||||
// { en: true, title: "Exhibition", link: "/exhibition-about" },
|
||||
// { en: true, title: "Participants", link: "/participants-info" },
|
||||
// { en: true, title: "For visitors", link: "/visitors-info" },
|
||||
];
|
||||
|
||||
export const burgerMenu2 = [
|
||||
interface BurgerMenu2Item {
|
||||
titleKey: string;
|
||||
link: string;
|
||||
company?: boolean;
|
||||
drop?: boolean;
|
||||
dropDown?: {
|
||||
titleKey: string;
|
||||
link: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export const burgerMenu2: BurgerMenu2Item[] = [
|
||||
{
|
||||
company: true,
|
||||
title: "О компании",
|
||||
|
||||
titleEn: "About company",
|
||||
titleKey: "nav.about",
|
||||
link: "/company/aboutus",
|
||||
drop: true,
|
||||
dropDown: [
|
||||
{
|
||||
title: "О нас",
|
||||
titleEn: "About us",
|
||||
link: "/company/aboutus",
|
||||
},
|
||||
{ titleKey: "burger.aboutUs", link: "/company/aboutus" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Новости",
|
||||
|
||||
titleEn: "News",
|
||||
titleKey: "nav.news",
|
||||
link: "/news",
|
||||
},
|
||||
{
|
||||
title: "FAQ",
|
||||
|
||||
titleEn: "FAQ",
|
||||
titleKey: "nav.faq",
|
||||
link: "/faq",
|
||||
},
|
||||
{
|
||||
title: "Контакты",
|
||||
|
||||
titleEn: "Контакты",
|
||||
titleKey: "nav.contacts",
|
||||
link: "/contacts",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,56 +1,36 @@
|
|||
import car from '@/public/assets/icons/service/car.svg';
|
||||
import bus from '@/public/assets/icons/service/bus.svg';
|
||||
import track from '@/public/assets/icons/service/track.svg';
|
||||
import car from "@/public/assets/icons/service/car.svg";
|
||||
import bus from "@/public/assets/icons/service/bus.svg";
|
||||
import track from "@/public/assets/icons/service/track.svg";
|
||||
|
||||
import { RoadCardProps } from '../types';
|
||||
import { StaticImageData } from "next/image";
|
||||
|
||||
export interface RoadCardProps {
|
||||
icon: StaticImageData;
|
||||
textKey: string;
|
||||
}
|
||||
|
||||
export const roadCardData: RoadCardProps[] = [
|
||||
{
|
||||
icon: car,
|
||||
text: 'На машине',
|
||||
|
||||
enText: 'Car',
|
||||
},
|
||||
{
|
||||
icon: bus,
|
||||
text: 'На автобусе',
|
||||
|
||||
enText: 'Public transport',
|
||||
},
|
||||
{
|
||||
icon: track,
|
||||
text: 'Freight transport',
|
||||
|
||||
enText: 'Car',
|
||||
},
|
||||
{ icon: car, textKey: "services.byCar" },
|
||||
{ icon: bus, textKey: "services.byBus" },
|
||||
{ icon: track, textKey: "services.freight" },
|
||||
];
|
||||
|
||||
import call from '@/public/assets/icons/service/call.svg';
|
||||
import megaphone from '@/public/assets/icons/service/megaphone.svg';
|
||||
import bag from '@/public/assets/icons/service/bag.svg';
|
||||
import call from "@/public/assets/icons/service/call.svg";
|
||||
import megaphone from "@/public/assets/icons/service/megaphone.svg";
|
||||
import bag from "@/public/assets/icons/service/bag.svg";
|
||||
|
||||
export const contactCardData: RoadCardProps[] = [
|
||||
{
|
||||
icon: call,
|
||||
text: 'Справочный центр',
|
||||
|
||||
enText: 'Call centre',
|
||||
},
|
||||
{
|
||||
icon: bag,
|
||||
text: 'Услуги',
|
||||
|
||||
enText: 'Servise bureau',
|
||||
},
|
||||
{
|
||||
icon: megaphone,
|
||||
text: 'Реклама',
|
||||
|
||||
enText: 'Advertising',
|
||||
},
|
||||
{ icon: call, textKey: "services.callCentre" },
|
||||
{ icon: bag, textKey: "services.serviceBureau" },
|
||||
{ icon: megaphone, textKey: "services.advertising" },
|
||||
];
|
||||
|
||||
import partner from '@/public/assets/icons/home/partner.svg';
|
||||
import { StaticImageData } from 'next/image';
|
||||
import partner from "@/public/assets/icons/home/partner.svg";
|
||||
|
||||
export const partnersData: StaticImageData[] = [partner, partner, partner, partner, partner];
|
||||
export const partnersData: StaticImageData[] = [
|
||||
partner,
|
||||
partner,
|
||||
partner,
|
||||
partner,
|
||||
partner,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,148 +1,110 @@
|
|||
interface MenuItemType {
|
||||
titleKey: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
interface MenuType {
|
||||
pathname: string;
|
||||
pathnameEn: string;
|
||||
pathnameKey: string;
|
||||
company?: boolean;
|
||||
members?: boolean;
|
||||
visitors?: boolean;
|
||||
news?: boolean;
|
||||
services?: boolean;
|
||||
info: {
|
||||
titleEn: string;
|
||||
title: string;
|
||||
link: string;
|
||||
}[];
|
||||
info: MenuItemType[];
|
||||
}
|
||||
|
||||
export const sidebarData: MenuType[] = [
|
||||
{
|
||||
company: true,
|
||||
pathname: 'О компании',
|
||||
pathnameEn: 'About company',
|
||||
pathnameKey: "sidebar.company",
|
||||
info: [
|
||||
{ title: 'Коротко о нас', titleEn: 'About us', link: '/company/aboutus' },
|
||||
// { title: 'Выставочная деятельность', link: '' },
|
||||
// { title: 'История и награды', link: '' },
|
||||
// { title: 'Партнеры', link: '' },
|
||||
// { title: 'Работы в компании', link: '' },
|
||||
// { title: 'Наши издания', link: '' },
|
||||
{ titleKey: "sidebar.aboutUs", link: "/company/aboutus" },
|
||||
],
|
||||
},
|
||||
{
|
||||
members: true,
|
||||
pathname: 'Участникам',
|
||||
pathnameEn: 'Participants',
|
||||
|
||||
pathnameKey: "sidebar.participants",
|
||||
info: [
|
||||
{
|
||||
title: 'Информация для участников',
|
||||
titleEn: 'Information for participants',
|
||||
link: '/members',
|
||||
},
|
||||
{
|
||||
title: 'Онлайн заявка для участников',
|
||||
titleEn: 'Online application',
|
||||
link: '/members/bid',
|
||||
},
|
||||
{
|
||||
title: 'Правила для участников',
|
||||
titleEn: 'Rules for participants',
|
||||
link: '/members/members-rules',
|
||||
},
|
||||
{ titleKey: "sidebar.participantsInfo", link: "/members" },
|
||||
{ titleKey: "sidebar.participantsApplication", link: "/members/bid" },
|
||||
{ titleKey: "sidebar.participantsRules", link: "/members/members-rules" },
|
||||
],
|
||||
},
|
||||
{
|
||||
news: true,
|
||||
pathname: 'Новости',
|
||||
pathnameEn: 'News',
|
||||
|
||||
info: [
|
||||
{ title: 'Новости', titleEn: 'News', link: '/news' },
|
||||
// { title: 'Пресс-релизы', link: '' },
|
||||
],
|
||||
pathnameKey: "sidebar.news",
|
||||
info: [{ titleKey: "sidebar.news", link: "/news" }],
|
||||
},
|
||||
{
|
||||
visitors: true,
|
||||
pathname: 'Посетителям',
|
||||
pathnameEn: 'Visitors',
|
||||
|
||||
pathnameKey: "sidebar.visitors",
|
||||
info: [
|
||||
{
|
||||
title: 'Информация для посетителей',
|
||||
titleEn: 'Information for visitors',
|
||||
link: '/visitors',
|
||||
},
|
||||
{
|
||||
title: 'Порядок регистрации посетителей',
|
||||
titleEn: 'Entrance rules',
|
||||
link: '/visitors/rules-for-visitors',
|
||||
},
|
||||
{ titleKey: "sidebar.visitorsInfo", link: "/visitors" },
|
||||
{ titleKey: "sidebar.visitorsRules", link: "/visitors/rules-for-visitors" },
|
||||
],
|
||||
},
|
||||
{
|
||||
services: true,
|
||||
pathname: 'Услуги',
|
||||
pathnameEn: 'Services',
|
||||
|
||||
pathnameKey: "sidebar.services",
|
||||
info: [
|
||||
{
|
||||
title: 'Организация выставок',
|
||||
titleEn: 'Organization of exhibitions',
|
||||
link: '/services/organization',
|
||||
},
|
||||
{
|
||||
title: 'B2B Встречи и Матчмейкинг',
|
||||
titleEn: 'B2B Meetings & Business Matchmaking',
|
||||
link: '/services/meetings',
|
||||
},
|
||||
{
|
||||
title: 'Бизнес-Миссии',
|
||||
titleEn: 'Business Missions',
|
||||
link: '/services/missions',
|
||||
},
|
||||
{
|
||||
title: 'Маркетинговые услуги',
|
||||
titleEn: 'Advertising activity',
|
||||
link: '/services/advertising',
|
||||
},
|
||||
{
|
||||
title: 'Организация гибридных мероприятий',
|
||||
titleEn: 'Organization of hybrid events',
|
||||
link: '/services/hybrid-organizations',
|
||||
},
|
||||
{
|
||||
title: 'Сертификация',
|
||||
titleEn: 'Certification',
|
||||
link: '/services/certification',
|
||||
},
|
||||
{
|
||||
title: 'Организация перевозки и таможенное оформление',
|
||||
titleEn: 'Freight forwarding and customs clearance',
|
||||
link: '/services/forwarding',
|
||||
},
|
||||
{
|
||||
title: 'Аутсорсинг бухгалтерского учета и аудита',
|
||||
titleEn: 'Outsourcing of accounting and auditing',
|
||||
link: '/services/outsourcing',
|
||||
},
|
||||
{
|
||||
title: 'Тренинг для персонала',
|
||||
titleEn: 'Personnel-training',
|
||||
link: '/services/personnel-training',
|
||||
},
|
||||
{
|
||||
title: 'Бизнес - туры',
|
||||
titleEn: 'Business - tours',
|
||||
link: '/services/business-tours',
|
||||
},
|
||||
{
|
||||
title: 'Дополнительные услуги',
|
||||
titleEn: 'Additional servies',
|
||||
link: '/services/additional',
|
||||
},
|
||||
{ titleKey: "sidebar.organizeExhibitions", link: "/services/organization" },
|
||||
{ titleKey: "sidebar.b2bMeetings", link: "/services/meetings" },
|
||||
{ titleKey: "sidebar.businessMissions", link: "/services/missions" },
|
||||
{ titleKey: "sidebar.marketingServices", link: "/services/advertising" },
|
||||
{ titleKey: "sidebar.hybridEvents", link: "/services/hybrid-organizations" },
|
||||
{ titleKey: "sidebar.certification", link: "/services/certification" },
|
||||
{ titleKey: "sidebar.forwarding", link: "/services/forwarding" },
|
||||
{ titleKey: "sidebar.outsourcing", link: "/services/outsourcing" },
|
||||
{ titleKey: "sidebar.personalTraining", link: "/services/personnel-training" },
|
||||
{ titleKey: "sidebar.businessTours", link: "/services/business-tours" },
|
||||
{ titleKey: "sidebar.additionalServices", link: "/services/additional" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
interface HeaderType {
|
||||
titleKey: string;
|
||||
link: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export const headerMenu: HeaderType[] = [
|
||||
{ titleKey: "nav.about", link: "/company/aboutus", id: 1 },
|
||||
{ titleKey: "nav.news", link: "/news", id: 2 },
|
||||
{ titleKey: "nav.faq", link: "/faq", id: 3 },
|
||||
{ titleKey: "nav.contacts", link: "/contacts", id: 4 },
|
||||
];
|
||||
|
||||
export const headerMenu2: HeaderType[] = [
|
||||
{ titleKey: "nav.calendar", link: "/calendar", id: 1 },
|
||||
{ titleKey: "nav.services", link: "/services/organization", id: 2 },
|
||||
{ titleKey: "nav.about", link: "/company/aboutus", id: 3 },
|
||||
{ titleKey: "nav.news", link: "/news", id: 4 },
|
||||
{ titleKey: "nav.contacts", link: "/contacts", id: 5 },
|
||||
];
|
||||
|
||||
interface FooterMenuType {
|
||||
titleKey: string;
|
||||
link: string;
|
||||
one?: boolean;
|
||||
}
|
||||
|
||||
export const footerMenu: FooterMenuType[] = [
|
||||
{ titleKey: "nav.calendar", link: "/calendar", one: true },
|
||||
{ titleKey: "nav.participants", link: "/members" },
|
||||
{ titleKey: "nav.visitors", link: "" },
|
||||
{ titleKey: "nav.services", link: "/services/organization" },
|
||||
];
|
||||
|
||||
export const footerMenu2: FooterMenuType[] = [
|
||||
{ titleKey: "footer.territory", link: "" },
|
||||
{ titleKey: "nav.about", link: "/company/aboutus" },
|
||||
{ titleKey: "footer.pressCenter", link: "" },
|
||||
{ titleKey: "nav.faq", link: "/faq" },
|
||||
{ titleKey: "nav.contacts", link: "/contacts" },
|
||||
{ titleKey: "footer.callCentre", link: "" },
|
||||
];
|
||||
|
||||
interface BurgerDataTypes {
|
||||
pathname: string;
|
||||
company?: boolean;
|
||||
|
|
@ -155,278 +117,71 @@ interface BurgerDataTypes {
|
|||
visitors?: boolean;
|
||||
contacts?: boolean;
|
||||
first?: boolean;
|
||||
title: string;
|
||||
titleKey: string;
|
||||
drop?: string;
|
||||
info?: {
|
||||
title: string;
|
||||
titleKey: string;
|
||||
link: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export const burgerMenuData: BurgerDataTypes[] = [
|
||||
{
|
||||
pathname: '/calendar',
|
||||
pathname: "/calendar",
|
||||
calendar: true,
|
||||
only: true,
|
||||
title: 'Календарь мероприятий',
|
||||
titleKey: "nav.calendar",
|
||||
first: true,
|
||||
},
|
||||
{
|
||||
members: true,
|
||||
title: 'Участникам',
|
||||
pathname: '/members',
|
||||
drop: 'members',
|
||||
titleKey: "nav.participants",
|
||||
pathname: "/members",
|
||||
drop: "members",
|
||||
first: true,
|
||||
info: [
|
||||
{ title: 'Информация для участников', link: '/members' },
|
||||
{ title: 'Онлайн заявка для участников', link: '/members/bid' },
|
||||
{ titleKey: "sidebar.participantsInfo", link: "/members" },
|
||||
{ titleKey: "sidebar.participantsApplication", link: "/members/bid" },
|
||||
],
|
||||
},
|
||||
{
|
||||
pathname: '/visitors',
|
||||
pathname: "/visitors",
|
||||
visitors: true,
|
||||
only: true,
|
||||
title: 'Посетителям',
|
||||
titleKey: "nav.visitors",
|
||||
first: true,
|
||||
},
|
||||
{
|
||||
pathname: '/services',
|
||||
pathname: "/services",
|
||||
services: true,
|
||||
only: true,
|
||||
title: 'Услуги',
|
||||
titleKey: "nav.services",
|
||||
first: true,
|
||||
},
|
||||
|
||||
{
|
||||
pathname: '/faq',
|
||||
pathname: "/faq",
|
||||
faq: true,
|
||||
only: true,
|
||||
title: 'FAQ',
|
||||
titleKey: "nav.faq",
|
||||
},
|
||||
{
|
||||
pathname: '/contacts',
|
||||
pathname: "/contacts",
|
||||
contacts: true,
|
||||
only: true,
|
||||
title: 'Контакты',
|
||||
titleKey: "nav.contacts",
|
||||
},
|
||||
{
|
||||
company: true,
|
||||
title: 'О компании',
|
||||
pathname: '/company/aboutus',
|
||||
drop: 'company',
|
||||
info: [
|
||||
{ title: 'Коротко о нас', link: '/company/aboutus' },
|
||||
// { title: "Выставочная деятельность", link: "" },
|
||||
// { title: "История и награды", link: "" },
|
||||
// { title: "Партнеры", link: "" },
|
||||
// { title: "Работы в компании", link: "" },
|
||||
// { title: "Наши издания", link: "" },
|
||||
],
|
||||
titleKey: "nav.about",
|
||||
pathname: "/company/aboutus",
|
||||
drop: "company",
|
||||
info: [{ titleKey: "sidebar.aboutUs", link: "/company/aboutus" }],
|
||||
},
|
||||
{
|
||||
news: true,
|
||||
title: 'Новости',
|
||||
pathname: '/news',
|
||||
drop: 'news',
|
||||
info: [
|
||||
{ title: 'Новости', link: '/news' },
|
||||
// { title: "Пресс-релизы", link: "/news" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
interface HeaderType {
|
||||
title: string;
|
||||
link: string;
|
||||
id: number;
|
||||
one?: boolean;
|
||||
en?: boolean;
|
||||
}
|
||||
|
||||
export const headerMenu: HeaderType[] = [
|
||||
{ title: 'О компании', link: '/company/aboutus', id: 1 },
|
||||
{ title: 'Новости', link: '/news', id: 2 },
|
||||
{ title: 'FAQ', link: '/faq', id: 3 },
|
||||
{ title: 'Контакты', link: '/contacts', id: 4 },
|
||||
|
||||
{ en: true, title: 'About company', link: '/company/aboutus', id: 1 },
|
||||
{ en: true, title: 'News', link: '/news', id: 2 },
|
||||
{ en: true, title: 'FAQ', link: '/faq', id: 3 },
|
||||
{ en: true, title: 'Contacts', link: '/contacts', id: 4 },
|
||||
];
|
||||
|
||||
export const headerMenu2: HeaderType[] = [
|
||||
{ title: 'Календарь мероприятий', link: '/calendar', id: 1 },
|
||||
{ title: 'Услуги', link: '/services/organization', id: 2 },
|
||||
{ title: 'О компании', link: '/company/aboutus', id: 3 },
|
||||
{ title: 'Новости', link: '/news', id: 4 },
|
||||
{ title: 'Контакты', link: '/contacts', id: 5 },
|
||||
|
||||
// { title: 'Участникам', link: '/members', id: 2 },
|
||||
// { title: 'Посетителям', link: '/visitors', id: 3 },
|
||||
|
||||
{ en: true, title: 'Calendar of events', link: '/calendar', id: 1 },
|
||||
{ en: true, title: 'Services', link: '/services/organization', id: 2 },
|
||||
{ en: true, title: 'About company', link: '/company/aboutus', id: 3 },
|
||||
{ en: true, title: 'News', link: '/news', id: 4 },
|
||||
{ en: true, title: 'Contacts', link: '/contacts', id: 5 },
|
||||
|
||||
// { en: true, title: 'Participants', link: '/members', id: 2 },
|
||||
// { en: true, title: 'Visitors', link: '/visitors', id: 3 },
|
||||
];
|
||||
|
||||
interface FooterType {
|
||||
title: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
export const footerMenu = [
|
||||
{ title: 'Календарь мероприятий', link: '/calendar', one: true },
|
||||
{ title: 'Участникам', link: '/members' },
|
||||
{ title: 'Посетителям', link: '' },
|
||||
{ title: 'Услуги', link: '/services/organization' },
|
||||
];
|
||||
|
||||
export const footerMenu2: FooterType[] = [
|
||||
{ title: 'Территория комплекса', link: '' },
|
||||
{ title: 'О компании', link: '/company/aboutus' },
|
||||
{ title: 'Пресс-центр', link: '' },
|
||||
{ title: 'FAQ', link: '/faq' },
|
||||
{ title: 'Контакты', link: '/contacts' },
|
||||
{ title: 'Справочный центр', link: '' },
|
||||
];
|
||||
|
||||
export const footerInfo: string[] = [
|
||||
'Адрес: 744000, г. Ашхабад, просп. Битарап Туркменистан, 183',
|
||||
'Тел.: +99362006200',
|
||||
'+99312454111',
|
||||
'E-mail: contact@turkmenexpo.com',
|
||||
'Address: Ashgabat, 183 Bitarap Turkmenistan',
|
||||
'ave.,744000',
|
||||
'E-mail: contact@turkmenexpo.com',
|
||||
];
|
||||
|
||||
export const topMenu = [
|
||||
{
|
||||
path: 'about',
|
||||
links: [{ active: 'Главная', default: '/ О компании / Коротко нас' }],
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
links: [
|
||||
{
|
||||
active: 'Главная',
|
||||
active2: ' / Участникам',
|
||||
default: ' / Информация для участников',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'members/bid',
|
||||
links: [
|
||||
{
|
||||
active: 'Главная',
|
||||
default: ' / Участникам / Онлайн заявка для участников',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'events',
|
||||
links: [
|
||||
{
|
||||
active: 'Главная ',
|
||||
default: '/ Календарь мероприятий',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'faq',
|
||||
links: [
|
||||
{
|
||||
active: 'Главная',
|
||||
default: ' / FAQ',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'contacts',
|
||||
links: [
|
||||
{
|
||||
active: 'Главная',
|
||||
default: ' / Контакты',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'calendar',
|
||||
links: [
|
||||
{
|
||||
active: 'Главная',
|
||||
default: ' / Календарь мероприятий',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'news',
|
||||
links: [
|
||||
{
|
||||
active: 'Главная',
|
||||
default: ' / Новости',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const burgerLinks = [
|
||||
{
|
||||
title: 'О компании',
|
||||
about: true,
|
||||
links: [
|
||||
{ name: 'Коротко о нас', link: '/about/company' },
|
||||
{ name: 'Выставочная деятельность', link: '/about/company' },
|
||||
{ name: 'История и награды', link: '/about/company' },
|
||||
{ name: 'Партнеры', link: '/about/company' },
|
||||
{ name: 'Работа в компании', link: '/about/company' },
|
||||
{ name: 'Наши издания', link: '/about/company' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Новости',
|
||||
news: true,
|
||||
links: [
|
||||
{ name: 'Новости', link: '/news' },
|
||||
{ name: 'Пресс центр', link: '/news' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Участникам',
|
||||
members: true,
|
||||
links: [
|
||||
{ name: 'Участникам', link: '/members' },
|
||||
{ name: 'Онлайн заявка', link: '/members/bid' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Контакты',
|
||||
contacts: true,
|
||||
links: [
|
||||
{ name: 'Новости', link: '/news' },
|
||||
{ name: 'Пресс центр', link: '/news' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'FAQ',
|
||||
faq: true,
|
||||
links: [
|
||||
{ name: 'faq', link: '/news' },
|
||||
{ name: 'Пресс центр', link: '/news' },
|
||||
],
|
||||
},
|
||||
{
|
||||
news: true,
|
||||
links: [
|
||||
{ name: 'Новости', link: '/news' },
|
||||
{ name: 'Пресс центр', link: '/news' },
|
||||
],
|
||||
titleKey: "nav.news",
|
||||
pathname: "/news",
|
||||
drop: "news",
|
||||
info: [{ titleKey: "sidebar.news", link: "/news" }],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
import { cookies } from "next/headers";
|
||||
|
||||
export const getLang = () => {
|
||||
const cookieStore = cookies();
|
||||
const langCookie = cookieStore.get("lang")?.value || "ru";
|
||||
|
||||
// Возвращаем полный объект языка
|
||||
return {
|
||||
title: langCookie === "en" ? "English" : "Русский",
|
||||
localization: langCookie,
|
||||
};
|
||||
};
|
||||
|
|
@ -22,6 +22,5 @@ export type NewsCardProps = {
|
|||
|
||||
export type RoadCardProps = {
|
||||
icon: any;
|
||||
text: string;
|
||||
enText: string;
|
||||
textKey: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,144 @@
|
|||
{
|
||||
"common": {
|
||||
"home": "Home",
|
||||
"showMore": "Show more",
|
||||
"send": "Send",
|
||||
"formSubmitted": "Form submitted",
|
||||
"of": "of"
|
||||
},
|
||||
"nav": {
|
||||
"calendar": "Calendar of events",
|
||||
"services": "Services",
|
||||
"about": "About company",
|
||||
"news": "News",
|
||||
"contacts": "Contacts",
|
||||
"faq": "FAQ",
|
||||
"participants": "Participants",
|
||||
"visitors": "Visitors"
|
||||
},
|
||||
"sidebar": {
|
||||
"company": "About company",
|
||||
"aboutUs": "About us",
|
||||
"participants": "Participants",
|
||||
"participantsInfo": "Information for participants",
|
||||
"participantsApplication": "Online application",
|
||||
"participantsRules": "Rules for participants",
|
||||
"news": "News",
|
||||
"visitors": "Visitors",
|
||||
"visitorsInfo": "Information for visitors",
|
||||
"visitorsRules": "Entrance rules",
|
||||
"services": "Services",
|
||||
"organizeExhibitions": "Organization of exhibitions",
|
||||
"b2bMeetings": "B2B Meetings & Business Matchmaking",
|
||||
"businessMissions": "Business Missions",
|
||||
"marketingServices": "Advertising activity",
|
||||
"hybridEvents": "Organization of hybrid events",
|
||||
"certification": "Certification",
|
||||
"forwarding": "Freight forwarding and customs clearance",
|
||||
"outsourcing": "Outsourcing of accounting and auditing",
|
||||
"personalTraining": "Personnel-training",
|
||||
"businessTours": "Business - tours",
|
||||
"additionalServices": "Additional services"
|
||||
},
|
||||
"burger": {
|
||||
"services": "Services",
|
||||
"about": "About company",
|
||||
"aboutUs": "About us",
|
||||
"advertisingActivity": "Advertising activity",
|
||||
"freightForwarding": "Freight forwarding and customs clearance",
|
||||
"accountingOutsourcing": "Outsourcing of accounting and auditing",
|
||||
"personnelTraining": "Personnel-training",
|
||||
"calendar": "Calendar of events",
|
||||
"participantsInfo": "Information for participants",
|
||||
"participantsApplication": "Online application"
|
||||
},
|
||||
"home": {
|
||||
"exhibitions": "Exhibitions",
|
||||
"goToWebsite": "Go to website",
|
||||
"news": "News",
|
||||
"allNews": "All news",
|
||||
"upcomingExhibitions": "Upcoming exhibitions and events",
|
||||
"partners": "Partners",
|
||||
"feedback": "Feedback"
|
||||
},
|
||||
"events": {
|
||||
"organiserLabel": "Organiser:",
|
||||
"coOrganiserLabel": "Co-organiser:",
|
||||
"registration": "Registration",
|
||||
"visitSite": "Visit site",
|
||||
"more": "More",
|
||||
"showMore": "Show more",
|
||||
"date": "Date",
|
||||
"venue": "Venue",
|
||||
"organiser": "Organiser",
|
||||
"coOrganiser": "Co-organiser",
|
||||
"description": "Description",
|
||||
"theme": "Theme of event"
|
||||
},
|
||||
"contacts": {
|
||||
"title": "Contacts",
|
||||
"contactUs": "Contact us",
|
||||
"name": "Name",
|
||||
"email": "E-mail",
|
||||
"phone": "Phone",
|
||||
"companyName": "Company name",
|
||||
"message": "Message",
|
||||
"address": "Address:",
|
||||
"addressValue": "744000, Ashgabat, 183 Bitarap Turkmenistan ave.",
|
||||
"phoneNumbers": "+99371871814; 99363719588"
|
||||
},
|
||||
"footer": {
|
||||
"address": "Address: Ashgabat, 183 Bitarap Turkmenistan ave., 744000",
|
||||
"phone": "Phone: +99362006200",
|
||||
"phone2": "+99312454111",
|
||||
"email": "E-mail: contact@turkmenexpo.com",
|
||||
"territory": "Complex territory",
|
||||
"pressCenter": "Press center",
|
||||
"callCentre": "Call centre"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "What to find?",
|
||||
"all": "Everywhere",
|
||||
"inEvents": "In events",
|
||||
"inNews": "In news",
|
||||
"findButton": "Find",
|
||||
"byRequest": "By request \"",
|
||||
"found": "\" found",
|
||||
"results": "results",
|
||||
"goToPage": "Go to page",
|
||||
"noResults": "Nothing found for your request"
|
||||
},
|
||||
"services": {
|
||||
"byCar": "Car",
|
||||
"byBus": "Public transport",
|
||||
"freight": "Freight transport",
|
||||
"callCentre": "Call centre",
|
||||
"serviceBureau": "Service bureau",
|
||||
"advertising": "Advertising",
|
||||
"breadcrumb": "Services",
|
||||
"routes": "Routes"
|
||||
},
|
||||
"validation": {
|
||||
"nameRequired": "Name is required",
|
||||
"emailRequired": "Email is required",
|
||||
"phoneRequired": "Phone number is required",
|
||||
"messageRequired": "Message is required"
|
||||
},
|
||||
"visitors": {
|
||||
"title": "For visitors",
|
||||
"information": "Information for visitors",
|
||||
"rules": "Entrance rules"
|
||||
},
|
||||
"about": {
|
||||
"title": "About us"
|
||||
},
|
||||
"news": {
|
||||
"title": "News",
|
||||
"mediaAboutUs": "Media about us",
|
||||
"allNews": "All news",
|
||||
"article": "Article"
|
||||
},
|
||||
"calendar": {
|
||||
"title": "Calendar of events"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
{
|
||||
"common": {
|
||||
"home": "Главная",
|
||||
"showMore": "Показать ещё",
|
||||
"send": "Отправить",
|
||||
"formSubmitted": "Форма отправлена",
|
||||
"of": "из"
|
||||
},
|
||||
"nav": {
|
||||
"calendar": "Календарь мероприятий",
|
||||
"services": "Услуги",
|
||||
"about": "О компании",
|
||||
"news": "Новости",
|
||||
"contacts": "Контакты",
|
||||
"faq": "FAQ",
|
||||
"participants": "Участникам",
|
||||
"visitors": "Посетителям"
|
||||
},
|
||||
"sidebar": {
|
||||
"company": "О компании",
|
||||
"aboutUs": "Коротко о нас",
|
||||
"participants": "Участникам",
|
||||
"participantsInfo": "Информация для участников",
|
||||
"participantsApplication": "Онлайн заявка для участников",
|
||||
"participantsRules": "Правила для участников",
|
||||
"news": "Новости",
|
||||
"visitors": "Посетителям",
|
||||
"visitorsInfo": "Информация для посетителей",
|
||||
"visitorsRules": "Порядок регистрации посетителей",
|
||||
"services": "Услуги",
|
||||
"organizeExhibitions": "Организация выставок",
|
||||
"b2bMeetings": "B2B Встречи и Матчмейкинг",
|
||||
"businessMissions": "Бизнес-Миссии",
|
||||
"marketingServices": "Маркетинговые услуги",
|
||||
"hybridEvents": "Организация гибридных мероприятий",
|
||||
"certification": "Сертификация",
|
||||
"forwarding": "Организация перевозки и таможенное оформление",
|
||||
"outsourcing": "Аутсорсинг бухгалтерского учета и аудита",
|
||||
"personalTraining": "Тренинг для персонала",
|
||||
"businessTours": "Бизнес - туры",
|
||||
"additionalServices": "Дополнительные услуги"
|
||||
},
|
||||
"burger": {
|
||||
"services": "Услуги",
|
||||
"about": "О компании",
|
||||
"aboutUs": "О нас",
|
||||
"advertisingActivity": "Рекламная деятельность",
|
||||
"freightForwarding": "Экспедирование грузов и таможенное оформление",
|
||||
"accountingOutsourcing": "Аутсорсинг бух.учета и аудита",
|
||||
"personnelTraining": "Обучения персонала",
|
||||
"calendar": "Календарь мероприятий",
|
||||
"participantsInfo": "Информация для участников",
|
||||
"participantsApplication": "Онлайн заявка для участников"
|
||||
},
|
||||
"home": {
|
||||
"exhibitions": "Выставки",
|
||||
"goToWebsite": "Перейти на сайт",
|
||||
"news": "Новости",
|
||||
"allNews": "Все новости",
|
||||
"upcomingExhibitions": "Ближайшие выставки и мероприятия",
|
||||
"partners": "Партнёры",
|
||||
"feedback": "Обратная связь"
|
||||
},
|
||||
"events": {
|
||||
"organiserLabel": "Организатор:",
|
||||
"coOrganiserLabel": "Со-организатор:",
|
||||
"registration": "Зарегистрироваться",
|
||||
"visitSite": "Посетить сайт",
|
||||
"more": "Подробнее",
|
||||
"showMore": "Показать еще",
|
||||
"date": "Дата",
|
||||
"venue": "Место",
|
||||
"organiser": "Организатор",
|
||||
"coOrganiser": "Со-организатор",
|
||||
"description": "Описание",
|
||||
"theme": "Тематика мероприятия"
|
||||
},
|
||||
"contacts": {
|
||||
"title": "Контакты",
|
||||
"contactUs": "Связаться с нами",
|
||||
"name": "Имя",
|
||||
"email": "E-mail",
|
||||
"phone": "Номер",
|
||||
"companyName": "Название компании",
|
||||
"message": "Сообщение",
|
||||
"address": "Адрес:",
|
||||
"addressValue": "744000, г. Ашхабад, просп. Битарап Туркменистан, 183",
|
||||
"phoneNumbers": "+99371871814; 99363719588"
|
||||
},
|
||||
"footer": {
|
||||
"address": "Адрес: 744000, г. Ашхабад, просп. Битарап Туркменистан, 183",
|
||||
"phone": "Тел.: +99362006200",
|
||||
"phone2": "+99312454111",
|
||||
"email": "E-mail: contact@turkmenexpo.com",
|
||||
"territory": "Территория комплекса",
|
||||
"pressCenter": "Пресс-центр",
|
||||
"callCentre": "Справочный центр"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "Что найти?",
|
||||
"all": "Везде",
|
||||
"inEvents": "В событиях",
|
||||
"inNews": "В новостях",
|
||||
"findButton": "Найти",
|
||||
"byRequest": "По запросу «",
|
||||
"found": "» нашлось",
|
||||
"results": "результатов",
|
||||
"goToPage": "Перейти на страницу",
|
||||
"noResults": "По вашему запросу ничего не найдено"
|
||||
},
|
||||
"services": {
|
||||
"byCar": "На машине",
|
||||
"byBus": "На автобусе",
|
||||
"freight": "Грузовой транспорт",
|
||||
"callCentre": "Справочный центр",
|
||||
"serviceBureau": "Услуги",
|
||||
"advertising": "Реклама",
|
||||
"breadcrumb": "Сервисы",
|
||||
"routes": "Как добраться"
|
||||
},
|
||||
"validation": {
|
||||
"nameRequired": "Имя необходимо",
|
||||
"emailRequired": "Email необходим",
|
||||
"phoneRequired": "Номер телефона необходим",
|
||||
"messageRequired": "Сообщение необходимо"
|
||||
},
|
||||
"visitors": {
|
||||
"title": "Посетителям",
|
||||
"information": "Информация для посетителей",
|
||||
"rules": "Порядок регистрации посетителей"
|
||||
},
|
||||
"about": {
|
||||
"title": "Коротко о нас"
|
||||
},
|
||||
"news": {
|
||||
"title": "Новости",
|
||||
"mediaAboutUs": "СМИ о нас",
|
||||
"allNews": "Все новости",
|
||||
"article": "Статья"
|
||||
},
|
||||
"calendar": {
|
||||
"title": "Календарь мероприятий"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import createMiddleware from "next-intl/middleware";
|
||||
import { locales, defaultLocale } from "./i18n/config";
|
||||
|
||||
export default createMiddleware({
|
||||
locales,
|
||||
defaultLocale,
|
||||
localePrefix: "always",
|
||||
localeDetection: true,
|
||||
});
|
||||
|
||||
export const config = {
|
||||
matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
|
||||
};
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
import createNextIntlPlugin from "next-intl/plugin";
|
||||
|
||||
const withNextIntl = createNextIntlPlugin("./i18n/request.ts");
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
images: { unoptimized: true },
|
||||
distDir: "build",
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
export default withNextIntl(nextConfig);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"clsx": "^2.1.0",
|
||||
"framer-motion": "^11.0.25",
|
||||
"next": "14.1.0",
|
||||
"next-intl": "^4.8.2",
|
||||
"pm2": "^5.3.1",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
|
|
@ -59,6 +60,67 @@
|
|||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/ecma402-abstract": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-3.1.1.tgz",
|
||||
"integrity": "sha512-jhZbTwda+2tcNrs4kKvxrPLPjx8QsBCLCUgrrJ/S+G9YrGHWLhAyFMMBHJBnBoOwuLHd7L14FgYudviKaxkO2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/fast-memoize": "3.1.0",
|
||||
"@formatjs/intl-localematcher": "0.8.1",
|
||||
"decimal.js": "^10.6.0",
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.1.tgz",
|
||||
"integrity": "sha512-xwEuwQFdtSq1UKtQnyTZWC+eHdv7Uygoa+H2k/9uzBVQjDyp9r20LNDNKedWXll7FssT3GRHvqsdJGYSUWqYFA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/fast-memoize": "3.1.0",
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/fast-memoize": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.0.tgz",
|
||||
"integrity": "sha512-b5mvSWCI+XVKiz5WhnBCY3RJ4ZwfjAidU0yVlKa3d3MSgKmH1hC3tBGEAtYyN5mqL7N0G5x0BOUYyO8CEupWgg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/icu-messageformat-parser": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.1.tgz",
|
||||
"integrity": "sha512-sSDmSvmmoVQ92XqWb499KrIhv/vLisJU8ITFrx7T7NZHUmMY7EL9xgRowAosaljhqnj/5iufG24QrdzB6X3ItA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "3.1.1",
|
||||
"@formatjs/icu-skeleton-parser": "2.1.1",
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/icu-skeleton-parser": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.1.tgz",
|
||||
"integrity": "sha512-PSFABlcNefjI6yyk8f7nyX1DC7NHmq6WaCHZLySEXBrXuLOB2f935YsnzuPjlz+ibhb9yWTdPeVX1OVcj24w2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "3.1.1",
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-localematcher": {
|
||||
"version": "0.5.10",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz",
|
||||
"integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@googlemaps/js-api-loader": {
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.16.2.tgz",
|
||||
|
|
@ -831,6 +893,313 @@
|
|||
"uuid": "bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
|
||||
"integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.3",
|
||||
"is-glob": "^4.0.3",
|
||||
"node-addon-api": "^7.0.0",
|
||||
"picomatch": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@parcel/watcher-android-arm64": "2.5.6",
|
||||
"@parcel/watcher-darwin-arm64": "2.5.6",
|
||||
"@parcel/watcher-darwin-x64": "2.5.6",
|
||||
"@parcel/watcher-freebsd-x64": "2.5.6",
|
||||
"@parcel/watcher-linux-arm-glibc": "2.5.6",
|
||||
"@parcel/watcher-linux-arm-musl": "2.5.6",
|
||||
"@parcel/watcher-linux-arm64-glibc": "2.5.6",
|
||||
"@parcel/watcher-linux-arm64-musl": "2.5.6",
|
||||
"@parcel/watcher-linux-x64-glibc": "2.5.6",
|
||||
"@parcel/watcher-linux-x64-musl": "2.5.6",
|
||||
"@parcel/watcher-win32-arm64": "2.5.6",
|
||||
"@parcel/watcher-win32-ia32": "2.5.6",
|
||||
"@parcel/watcher-win32-x64": "2.5.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-android-arm64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
|
||||
"integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-darwin-arm64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
|
||||
"integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-darwin-x64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
|
||||
"integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-freebsd-x64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
|
||||
"integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm-glibc": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
|
||||
"integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm-musl": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
|
||||
"integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm64-glibc": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
|
||||
"integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm64-musl": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
|
||||
"integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-x64-glibc": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
|
||||
"integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-x64-musl": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
|
||||
"integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-arm64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
|
||||
"integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-ia32": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
|
||||
"integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-x64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
|
||||
"integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
|
|
@ -1155,6 +1524,178 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@schummar/icu-type-parser": {
|
||||
"version": "1.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz",
|
||||
"integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@swc/core-darwin-arm64": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.11.tgz",
|
||||
"integrity": "sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-darwin-x64": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.11.tgz",
|
||||
"integrity": "sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.11.tgz",
|
||||
"integrity": "sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.11.tgz",
|
||||
"integrity": "sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-musl": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.11.tgz",
|
||||
"integrity": "sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-x64-gnu": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.11.tgz",
|
||||
"integrity": "sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-x64-musl": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.11.tgz",
|
||||
"integrity": "sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.11.tgz",
|
||||
"integrity": "sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.11.tgz",
|
||||
"integrity": "sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-x64-msvc": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.11.tgz",
|
||||
"integrity": "sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/counter": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||
|
|
@ -1163,6 +1704,15 @@
|
|||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/types": {
|
||||
"version": "0.1.25",
|
||||
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz",
|
||||
"integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tootallnate/quickjs-emscripten": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
|
||||
|
|
@ -2005,6 +2555,12 @@
|
|||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/decimal.js": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
||||
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
|
|
@ -2027,9 +2583,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
|
||||
"integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
|
|
@ -2570,6 +3127,21 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/icu-minify": {
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.8.2.tgz",
|
||||
"integrity": "sha512-LHBQV+skKkjZSPd590pZ7ZAHftUgda3eFjeuNwA8/15L8T8loCNBktKQyTlkodAU86KovFXeg/9WntlAo5wA5A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/amannn"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/icu-messageformat-parser": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/immer": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.0.3.tgz",
|
||||
|
|
@ -2598,6 +3170,18 @@
|
|||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
||||
},
|
||||
"node_modules/intl-messageformat": {
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.1.2.tgz",
|
||||
"integrity": "sha512-ucSrQmZGAxfiBHfBRXW/k7UC8MaGFlEj4Ry1tKiDcmgwQm1y3EDl40u+4VNHYomxJQMJi9NEI3riDRlth96jKg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "3.1.1",
|
||||
"@formatjs/fast-memoize": "3.1.0",
|
||||
"@formatjs/icu-messageformat-parser": "3.5.1",
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/invariant": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||
|
|
@ -3103,6 +3687,91 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-intl": {
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.8.2.tgz",
|
||||
"integrity": "sha512-GuuwyvyEI49/oehQbBXEoY8KSIYCzmfMLhmIwhMXTb+yeBmly1PnJcpgph3KczQ+HTJMXwXCmkizgtT8jBMf3A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/amannn"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "^0.5.4",
|
||||
"@parcel/watcher": "^2.4.1",
|
||||
"@swc/core": "^1.15.2",
|
||||
"icu-minify": "^4.8.2",
|
||||
"negotiator": "^1.0.0",
|
||||
"next-intl-swc-plugin-extractor": "^4.8.2",
|
||||
"po-parser": "^2.1.1",
|
||||
"use-intl": "^4.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-intl-swc-plugin-extractor": {
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.8.2.tgz",
|
||||
"integrity": "sha512-sHDs36L1VZmFHj3tPHsD+KZJtnsRudHlNvT0ieIe3iFVn5OpGLTxW3d/Zc/2LXSj5GpGuR6wQeikbhFjU9tMQQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/next-intl/node_modules/@swc/core": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.11.tgz",
|
||||
"integrity": "sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3",
|
||||
"@swc/types": "^0.1.25"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/swc"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-darwin-arm64": "1.15.11",
|
||||
"@swc/core-darwin-x64": "1.15.11",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.15.11",
|
||||
"@swc/core-linux-arm64-gnu": "1.15.11",
|
||||
"@swc/core-linux-arm64-musl": "1.15.11",
|
||||
"@swc/core-linux-x64-gnu": "1.15.11",
|
||||
"@swc/core-linux-x64-musl": "1.15.11",
|
||||
"@swc/core-win32-arm64-msvc": "1.15.11",
|
||||
"@swc/core-win32-ia32-msvc": "1.15.11",
|
||||
"@swc/core-win32-x64-msvc": "1.15.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/helpers": ">=0.5.17"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/helpers": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-intl/node_modules/negotiator": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/next/node_modules/postcss": {
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
|
|
@ -3130,6 +3799,12 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
|
|
@ -3651,6 +4326,12 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/po-parser": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.1.1.tgz",
|
||||
"integrity": "sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
||||
|
|
@ -4757,9 +5438,10 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tv4": {
|
||||
"version": "1.3.0",
|
||||
|
|
@ -4793,7 +5475,7 @@
|
|||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
@ -4882,6 +5564,27 @@
|
|||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-intl": {
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.8.2.tgz",
|
||||
"integrity": "sha512-3VNXZgDnPFqhIYosQ9W1Hc6K5q+ZelMfawNbexdwL/dY7BTHbceLUBX5Eeex9lgogxTp0pf1SjHuhYNAjr9H3g==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/amannn"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/fast-memoize": "^3.1.0",
|
||||
"@schummar/icu-type-parser": "1.21.5",
|
||||
"icu-minify": "^4.8.2",
|
||||
"intl-messageformat": "^11.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
"clsx": "^2.1.0",
|
||||
"framer-motion": "^11.0.25",
|
||||
"next": "14.1.0",
|
||||
"next-intl": "^4.8.2",
|
||||
"pm2": "^5.3.1",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import { baseAPI } from "@/lib/API";
|
||||
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
||||
import { activeLangType } from "./headerSlice";
|
||||
|
||||
export const fetchAbout = createAsyncThunk(
|
||||
"about/fetchAbout",
|
||||
async ({ activeLang }: { activeLang: activeLangType }) => {
|
||||
async ({ locale }: { locale: string }) => {
|
||||
const res = await fetch(`${baseAPI}settings/about_us`, {
|
||||
headers: {
|
||||
"Accept-Language": activeLang.localization,
|
||||
"Accept-Language": locale,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -35,12 +34,9 @@ const aboutSlice = createSlice({
|
|||
},
|
||||
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(fetchAbout.pending, (state) => {
|
||||
// console.log('pending');
|
||||
});
|
||||
builder.addCase(fetchAbout.pending, (state) => {});
|
||||
|
||||
builder.addCase(fetchAbout.fulfilled, (state, action) => {
|
||||
// console.log('success', action.payload);
|
||||
state.aboutData = action.payload;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,11 @@
|
|||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { RootState } from '../store';
|
||||
|
||||
export type activeLangType = {
|
||||
title: 'Ру' | 'En' | 'Tm' | string;
|
||||
localization: 'ru' | 'en' | 'tm' | string;
|
||||
};
|
||||
|
||||
interface HeaderState {
|
||||
activeLang: activeLangType;
|
||||
showInput: boolean;
|
||||
}
|
||||
|
||||
const initialState: HeaderState = {
|
||||
activeLang: {
|
||||
title: 'Русский',
|
||||
localization: 'ru',
|
||||
},
|
||||
showInput: false,
|
||||
};
|
||||
|
||||
|
|
@ -23,9 +13,6 @@ const headerSlice = createSlice({
|
|||
name: 'header',
|
||||
initialState,
|
||||
reducers: {
|
||||
setActiveLang(state, action: PayloadAction<activeLangType>) {
|
||||
state.activeLang = action.payload;
|
||||
},
|
||||
setShowInput(state, action: PayloadAction<boolean>) {
|
||||
state.showInput = action.payload;
|
||||
},
|
||||
|
|
@ -34,6 +21,6 @@ const headerSlice = createSlice({
|
|||
|
||||
export const selectHeader = (state: RootState) => state.headerSlice;
|
||||
|
||||
export const { setActiveLang, setShowInput } = headerSlice.actions;
|
||||
export const { setShowInput } = headerSlice.actions;
|
||||
|
||||
export default headerSlice.reducer;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,18 @@
|
|||
import { baseAPI } from "@/lib/API";
|
||||
import { ServicesType } from "@/lib/types/Services.data";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
export const getServices = async () => {
|
||||
const lang = cookies().get("lang")?.value || "en";
|
||||
|
||||
export const getServices = async (lang: string) => {
|
||||
const res = await fetch(`${baseAPI}services`, {
|
||||
headers: {
|
||||
"Accept-Language": lang, // Передаём язык в заголовке
|
||||
"Accept-Language": lang,
|
||||
},
|
||||
next: {
|
||||
revalidate: 1000,
|
||||
tags: [`services-${lang}`], // Кешируем отдельно для каждого языка
|
||||
tags: [`services-${lang}`],
|
||||
},
|
||||
});
|
||||
|
||||
if (!res.ok) throw new Error("Ошибка при загрузке данных");
|
||||
if (!res.ok) throw new Error("Failed to load services");
|
||||
|
||||
return (await res.json()) as ServicesType;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
"use client";
|
||||
|
||||
export const useLang = (en: string, ru: string, localization: string) => {
|
||||
if (localization === "en") {
|
||||
return en;
|
||||
} else {
|
||||
return ru;
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue