This commit is contained in:
2025-09-23 12:35:35 +05:00
14 changed files with 326 additions and 143 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 763 KiB

After

Width:  |  Height:  |  Size: 762 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 KiB

After

Width:  |  Height:  |  Size: 360 KiB

+14 -1
View File
@@ -85,7 +85,20 @@ export function MapPointProjectFormModal<TAction extends "create" | "edit">({
className="px-8 py-7 rounded-2xl outline-none bg-[#37393B99] bg-no-repeat bg-[right_32px_top_24px] h-fit font-medium btnl appearance-none"
>
<option value={undefined}>Не выбрано</option>
{companies?.map((company) => (
{companies
?.sort((a, b) => {
// Проверяем, содержит ли название русские символы
const aHasCyrillic = /[а-яё]/i.test(a.title);
const bHasCyrillic = /[а-яё]/i.test(b.title);
// Если одно название латиница, а другое кириллица
if (!aHasCyrillic && bHasCyrillic) return -1; // латиница идет первой
if (aHasCyrillic && !bHasCyrillic) return 1; // кириллица идет второй
// Если оба одного типа, сортируем алфавитно
return a.title.localeCompare(b.title);
})
.map((company) => (
<option
key={company.id}
value={company.id}
+14 -1
View File
@@ -130,7 +130,20 @@ export function ProjectFormModal<TAction extends "create" | "edit">({
className="px-8 py-7 rounded-2xl outline-none bg-[#37393B99] bg-no-repeat bg-[right_32px_top_24px] h-fit font-medium btnl appearance-none"
>
<option value={undefined}>Не выбрано</option>
{companies?.map((company) => (
{companies
?.sort((a, b) => {
// Проверяем, содержит ли название русские символы
const aHasCyrillic = /[а-яё]/i.test(a.title);
const bHasCyrillic = /[а-яё]/i.test(b.title);
// Если одно название латиница, а другое кириллица
if (!aHasCyrillic && bHasCyrillic) return -1; // латиница идет первой
if (aHasCyrillic && !bHasCyrillic) return 1; // кириллица идет второй
// Если оба одного типа, сортируем алфавитно
return a.title.localeCompare(b.title);
})
.map((company) => (
<option
key={company.id}
value={company.id}
+210 -55
View File
@@ -1,86 +1,241 @@
import React, { useRef } from "react";
import React, { useEffect, useMemo, useRef, useState } from "react";
import { Button } from "@/ui/Button";
import QuestionFormInput from "@/ui/QuestionFormInput";
import { useOnClickOutside } from "usehooks-ts";
import { useModalStore } from "@/stores/useModalStore";
import { FormProvider, useForm } from "react-hook-form";
import CheckIcon from "../icons/CheckIcon";
import ReactInputMask from "react-input-mask";
import { projectsTags } from "@/consts/projectsTags";
import { CheckboxesGroup } from "@/ui/CheckboxesGroup";
import { Product } from "@/types/Product";
import FeedbackModal from "./FeedbackFormModal";
import { api } from "@/api";
import { useRefererStore } from "@/stores/useRefererStore";
import { Country } from "react-phone-number-input";
import { getExampleNumber } from "libphonenumber-js";
import examples from "libphonenumber-js/mobile/examples";
interface IInput {
fullname: string;
phone: string;
email: string;
products: Product[];
referer?: string | null;
}
export default function QuestionFormModal() {
const { setModal } = useModalStore();
const [[phoneCode, country], setPhoneCodeAndCountry] = useState<
[string, Country]
>(["+7", "RU"]);
const { referer } = useRefererStore();
const { modal, setModal } = useModalStore();
const placeholder = useMemo(
() =>
getExampleNumber(country, examples)
?.formatInternational()
.split(" ")
.slice(1)
.join(" "),
[country]
);
const formRef = useRef(null);
useOnClickOutside(formRef, () => {
setModal(null);
});
const form = useForm<IInput>({
defaultValues: {
products: ["Создание сайтов", "Web-тур по 360 сферам"] as Product[],
},
});
const { register, handleSubmit, formState, setValue } = form;
async function onSubmit(data: IInput) {
const { id } = await api
.post("mail", { json: { ...data, referer } })
.json<{ id: string }>();
setModal(<FeedbackModal id={id} />);
}
return (
<form
ref={formRef}
className="p-[3.333vw] w-[64.514vw] backdrop-blur-[20px] rounded-[1.111vw] z-10 bg-[radial-gradient(circle_at_bottom_right,rgba(24,25,26,0.84)_0%,rgba(45,46,47,0.86)_100%)] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2
max-md:w-[94.444vw] max-md:px-[6.667vw] max-md:py-[5.556vw] max-md:rounded-[4.444vw]"
>
<h3 className="text-center accent bg-gradient-to-r from-[#FF79D2] to-[#C932E8] bg-clip-text mb-[1.389vw] max-md:mb-[5.556vw]">
<span className="text-transparent bg-clip-text">
{" "}
Напишите свой вопрос,{" "}
</span>
<br /> мы свяжемся с вами <br className="hidden max-md:block" /> в
ближайшее время
<br /> и ответим на него
</h3>
<div
className="flex justify-between w-full mb-[1.944vw] gap-[1.944vw]
max-md:flex-col max-md:gap-[3.333vw] max-md:mb-[3.333vw]"
ref={formRef}
className="p-[3.333vw] w-[64.514vw] backdrop-blur-[20px] rounded-[1.111vw] z-10 bg-[radial-gradient(circle_at_bottom_right,rgba(24,25,26,0.84)_0%,rgba(45,46,47,0.86)_100%)] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 max-md:w-[94.444vw] max-md:px-[6.667vw] max-md:py-[5.556vw] max-md:rounded-[4.444vw]"
>
<QuestionFormInput
type={"text"}
name={"web-q-name"}
placeholder={"Имя"}
activeFieldName={"name"}
required
/>
<QuestionFormInput
type={"email"}
name={"web-q-email"}
placeholder={"E-mail"}
activeFieldName={"email"}
required
/>
{!formState.isSubmitted ? (
<FormProvider {...form}>
<form
className="lg:space-y-[1.944vw] md:max-lg:space-y-7 space-y-3"
onSubmit={handleSubmit(onSubmit)}
>
<div className="lg:space-y-[1.111vw] space-y-4">
<p className="heading2 font-medium">Нам нужно</p>
<CheckboxesGroup name="products" options={projectsTags} />
</div>
<QuestionFormInput
type={"text"}
name={"web-q-content"}
placeholder={"Ваш вопрос"}
activeFieldName={"content"}
<input
id="name"
autoComplete="none"
type="text"
required
placeholder="Имя*"
{...register("fullname")}
className="bg-transparent border-b border-[#37393B] focus:border-white py-4 rounded-none outline-none transition-all w-full placeholder:btnl btnl placeholder:font-medium placeholder:select-none"
/>
<input
autoComplete="none"
required
id="email"
type="email"
placeholder="Email*"
{...register("email")}
className="bg-transparent border-b border-[#37393B] focus:border-white py-4 rounded-none btnl outline-none transition-all w-full placeholder:btnl placeholder:font-medium placeholder:select-none"
/>
<div className="flex gap-x-3 py-2 border-[#3D425C] relative">
<ReactInputMask
type="tel"
autoComplete="tel"
onChange={(e) => {
if (e.nativeEvent.type.startsWith("input")) {
const cleanValue = e.target.value.replaceAll(/ /g, "");
const inputType = (e.nativeEvent as InputEvent)?.inputType;
<div className="text-center max-md:mt-[6.667vw]">
const shouldAddPhoneCode =
inputType !== "insertFromPaste" &&
inputType !== "insertFromDrop" &&
inputType !== "insertCompositionText" &&
!cleanValue.startsWith("+") &&
!cleanValue.startsWith("7") &&
!cleanValue.startsWith(phoneCode.replace("+", ""));
form.setValue(
"phone",
(shouldAddPhoneCode ? phoneCode : "") + cleanValue
);
}
}}
id={"tel"}
maskChar={null}
mask={"+7 " + (placeholder?.replace(/\d/g, "9") ?? "")}
placeholder={"+7 " + placeholder}
className="placeholder:btnl placeholder:font-medium placeholder:select-none peer btnl w-full h-full bg-transparent rounded-none transition-all outline-none"
/>
<div className="bottom-0 absolute w-full border-b border-[#37393B] peer-focus:border-white -mb-2" />
</div>
<div className="md:flex items-stretch lg:gap-[0.833vw] gap-3">
<Button
className="rounded-[1.111vw] py-[1.389vw] px-[2.778vw] mx-auto mt-[1.944vw] mb-[0.833vw] btn-l max-md:w-full max-md:py-[4.556vw] max-md:rounded-[4.444vw] max-md:mb-[3.333vw]"
type="submit"
className="btnl max-md:mb-3 max-md:w-full lg:px-[2.222vw] lg:py-[1.389vw] px-8 py-5 cursor-pointer lg:rounded-[1.111vw] rounded-2xl"
>
Отправить
Оставить заявку
</Button>
<span className="text2 w-[31.875vw] text-[#7A7A7A] max-md:text-[3.333vw]">
*Нажимая кнопку отправить, вы принимаете 
<a
href="/policy"
target="_blank"
className="text-[#FFFFFF] cursor-pointer hover:underline"
>
условия использования{" "}
</a>
<br className="block max-md:hidden" />  и 
<div className="text2 xl:max-w-[60%] md:max-lg:max-w-[40%] md:max-lg:py-1">
<span className="text-[#7A7A7A]">
*Нажимая кнопку отправить, вы даете
</span>{" "}
<a
target="_blank"
href={"/privacy-policy"}
className="text-[#FFFFFF] cursor-pointer hover:underline"
className="underline"
>
политику конфиденциальности
согласие на обработку персональных данных
</a>{" "}
<span className="text-[#7A7A7A]">и принимаете </span>
<a target="_blank" href={"/policy"} className="underline">
условия политики
</a>
</span>
</div>
</div>
</form>
</FormProvider>
) : (
<div className="bg-[#37393B99] aspect-[643/480] w-full rounded-2xl flex justify-center items-center">
<div className="flex gap-3 justify-center items-center">
<div className="bg-gradient p-3 rounded-full">
<div className="text-white lg:size-[1.667vw] size-6">
<CheckIcon />
</div>
</div>
<p className="heading2 font-medium">
Мы получили заявку
<br />и скоро свяжемся с вами!
</p>
</div>
</div>
)}
</div>
// <form
// ref={formRef}
// className="p-[3.333vw] w-[64.514vw] backdrop-blur-[20px] rounded-[1.111vw] z-10 bg-[radial-gradient(circle_at_bottom_right,rgba(24,25,26,0.84)_0%,rgba(45,46,47,0.86)_100%)] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 max-md:w-[94.444vw] max-md:px-[6.667vw] max-md:py-[5.556vw] max-md:rounded-[4.444vw]"
// >
// <h3 className="text-center accent bg-gradient-to-r from-[#FF79D2] to-[#C932E8] bg-clip-text mb-[1.389vw] max-md:mb-[5.556vw]">
// <span className="text-transparent bg-clip-text">
// {" "}
// Напишите свой вопрос,{" "}
// </span>
// <br /> мы свяжемся с вами <br className="max-md:block hidden" /> в
// ближайшее время
// <br /> и ответим на него
// </h3>
// <div
// className="flex justify-between w-full mb-[1.944vw] gap-[1.944vw]
// max-md:flex-col max-md:gap-[3.333vw] max-md:mb-[3.333vw]"
// >
// <QuestionFormInput
// type={"text"}
// name={"web-q-name"}
// placeholder={"Имя"}
// activeFieldName={"name"}
// required
// />
// <QuestionFormInput
// type={"email"}
// name={"web-q-email"}
// placeholder={"E-mail"}
// activeFieldName={"email"}
// required
// />
// </div>
// <QuestionFormInput
// type={"text"}
// name={"web-q-content"}
// placeholder={"Ваш вопрос"}
// activeFieldName={"content"}
// />
// <div className="text-center max-md:mt-[6.667vw]">
// <Button
// className="rounded-[1.111vw] py-[1.389vw] px-[2.778vw] mx-auto mt-[1.944vw] mb-[0.833vw] btn-l max-md:w-full max-md:py-[4.556vw] max-md:rounded-[4.444vw] max-md:mb-[3.333vw]"
// type="submit"
// >
// Отправить
// </Button>
// <span className="text2 w-[31.875vw] text-[#7A7A7A] max-md:text-[3.333vw]">
// *Нажимая кнопку отправить, вы принимаете
// <a
// href="/policy"
// target="_blank"
// className="text-[#FFFFFF] cursor-pointer hover:underline"
// >
// условия использования{" "}
// </a>
// <br className="max-md:hidden block" />  и
// <a
// target="_blank"
// href={"/privacy-policy"}
// className="text-[#FFFFFF] cursor-pointer hover:underline"
// >
// политику конфиденциальности
// </a>
// </span>
// </div>
// </form>
);
}
@@ -46,14 +46,14 @@ export default function AboutEventCard({
style={{ backgroundColor: color }}
className="size-2.5 rounded-full mr-2"
/>
<span className="font-medium">{location}</span>
<span className="btnm font-medium">{location}</span>
</div>
<div
className="py-2.5 px-[0.833vw] rounded-[1.111vw] border border-[#FFFFFF1A]
max-lg:w-max md:max-lg:py-[1.432vw] md:max-lg:px-[2.604vw] md:max-lg:rounded-full
max-md:rounded-[4.444vw] max-md:px-[3.333vw] max-md:py-[1.333vw]"
>
<span className="font-medium">
<span className="btnm font-medium">
{dates
.map((date) => format(date, "d MMMM", { locale: ru }))
.join(" ")}
@@ -19,7 +19,7 @@ function AboutEvents() {
</span>
где каждый девелопер может лично протестировать{" "}
<br className="md:max-lg:block hidden" />
и оценить функционал наших интерактивных макетов недвижимости
и оценить функционал наших интерактивных инструментов продаж
</p>
<OpenFormModalWrapper modal={<EventFormModal action="create" />}>
<GradientButton>
@@ -48,7 +48,7 @@ export default function AboutExperience() {
>
<motion.div
initial={{ y: 100, opacity: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 1 }}
transition={{ delay: 0.4, bounce: 0, duration: 0.5 }}
className="[grid-area:a] w-full aspect-[340/360] rounded-2xl border border-[#FFFFFF1A] flex flex-col items-center justify-between p-[1.667vw]
md:max-lg:p-[3.125vw]
@@ -67,7 +67,7 @@ export default function AboutExperience() {
</motion.div>
<motion.div
initial={{ y: 100, opacity: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 1 }}
transition={{ delay: 0.6, bounce: 0, duration: 0.5 }}
className="relative overflow-hidden [grid-area:d] w-full aspect-[340/360] rounded-2xl border border-[#FFFFFF1A] flex flex-col-reverse items-center justify-between p-[1.667vw]
md:max-lg:p-[3.125vw]
@@ -84,7 +84,7 @@ export default function AboutExperience() {
</motion.div>
<motion.div
initial={{ y: 100, opacity: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 1 }}
transition={{ bounce: 0, duration: 0.5 }}
className="relative [grid-area:b] rounded-2xl
max-lg:border max-lg:border-[#FFFFFF1A] md:max-lg:p-[3.125vw]
@@ -101,7 +101,7 @@ export default function AboutExperience() {
</motion.div>
<motion.div
initial={{ y: 100, opacity: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 1 }}
transition={{ delay: 0.8, bounce: 0, duration: 0.5 }}
className="relative overflow-hidden [grid-area:c] w-full aspect-[340/360] rounded-2xl border border-[#FFFFFF1A] flex flex-col-reverse items-center justify-between p-[1.667vw]
md:max-lg:p-[3.125vw]
@@ -130,7 +130,7 @@ export default function AboutExperience() {
alt=""
/>
<span className="headline1 text-center">
Виртуальные туры <br /> по 360 сферам
Виртуальные туры <br /> по 360° сферам
</span>
</motion.div>
</div>
@@ -153,9 +153,9 @@ export default function AboutExperience() {
в 40 выставках
</h3>
<p className="headline1 text-[#7A7A7A]">
Наш продукт GRAFF.estate уже четыре раза был представлен
на форуме Движение, Иннопром, Восточный экономический форум,
100+ TechnoBuild
Продукты GRAFF.estate уже четыре раза были представлены
на форуме «Движение», Восточном экономическом форуме, Иннопроме
и форуме 100+ TechnoBuild
</p>
</div>
<div
@@ -247,16 +247,18 @@ export default function AboutExperience() {
<DisplacementCard
index={1}
className="lg:top-[8.333vw] lg:left-0 md:max-lg:!translate-y-[3.125vw]"
className={
"lg:top-[8.333vw] lg:left-0 md:max-lg:!translate-y-[3.125vw] !aspect-[223/260]"
}
>
<img
src={"/img/pages/about/experience/cards/wow.png"}
className="lg:w-[9.444vw] lg:h-[9.444vw] md:w-[17.708vw] md:h-[17.708vw] w-[33.333vw] h-[33.333vw] my-auto object-cover hover:scale-[1.15] transition-all duration-300"
alt="Заняли 1 место на WOW AWARDS 2024 совместно с застройщиком Upside Development"
/>
<p className="z-10 text-center btns md:max-lg:text-[1.563vw] max-md:text-[3.333vw]">
Мы заняли 1 место на WOW AWARDS 2024 совместно c застройщиком
Upside Development
<p className="z-10 text-center btns !leading-[120%]">
Мы дважды заняли 1 место на WOW AWARDS в 2024 и 2025 году
совместно с застройщиками Upside Development и LEGENDA
</p>
</DisplacementCard>
@@ -159,7 +159,6 @@ export default function Accordeon({
ease: "easeIn",
},
}}
className="text1 lg:text-[0.972vw] md:text-[1.823vw] text-[3.889vw]"
key={index}
>
<span
@@ -168,6 +167,7 @@ export default function Accordeon({
display: isFaqPage ? "inline-block" : "inline",
opacity: isFaqPage ? 0.6 : 1,
}}
className={isFaqPage ? "text1" : "text2"}
>
{item.content}
</span>
@@ -7,35 +7,35 @@ function Iphone({ active }: { active: boolean }) {
return (
<div className="relative w-[17vw] h-auto aspect-[576/1182] items-center mx-auto my-auto md:max-lg:w-[24.881vw] max-md:w-[50vw]">
<motion.img
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
initial={{ opacity: 0, scale: 0.75 }}
animate={{ opacity: 1, scale: active ? 1 : 0.75 }}
exit={{ opacity: 0, scale: 0.75 }}
src="/img/pages/web/iphone.png"
className="absolute w-full h-full z-[8]"
className={`z-[8] absolute w-full h-full`}
alt=""
/>
<AnimatePresence mode="popLayout">
{active ? (
<motion.video
key={1}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
initial={{ opacity: 0, scale: 0.75 }}
animate={{ opacity: 1, scale: active ? 1 : 0.75 }}
exit={{ opacity: 0, scale: 0.75 }}
src={"/videos/pages/web/iphone_vid.mp4"}
muted
autoPlay
loop
className="mx-auto w-[98%] h-[99%] translate-y-[0.5%] z-[7] lg:rounded-[3vw] rounded-2xl md:max-lg:rounded-[4vw] max-md:rounded-[8vw]"
className={`mx-auto w-[98%] h-[99%] translate-y-[0.5%] z-[7] lg:rounded-[3vw] rounded-2xl md:max-lg:rounded-[4vw] max-md:rounded-[8vw]`}
/>
) : (
<motion.img
key={2}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
initial={{ opacity: 0, scale: 0.75 }}
animate={{ opacity: 1, scale: active ? 1 : 0.75 }}
exit={{ opacity: 0, scale: 0.75 }}
src={"/img/pages/web/content/iphone_disabled.png"}
alt=""
className="mx-auto w-auto h-full lg:rounded-[3.333vw] md:rounded-[4.444vw] rounded-3xl"
className={`mx-auto w-auto h-full lg:rounded-[3.333vw] md:rounded-[4.444vw] rounded-3xl`}
/>
)}
</AnimatePresence>
+2 -2
View File
@@ -106,7 +106,7 @@ export default function WebDemo() {
</div>
<p className="z-10 text-center btns md:max-lg:text-[1.563vw] max-md:text-[3.333vw]">
Люди гуляют в окрестностях ЖК
Люди гуляют в окрестностях
</p>
</DisplacementCard>
@@ -180,7 +180,7 @@ export default function WebDemo() {
</div>
<p className="z-10 text-center btns md:max-lg:text-[1.563vw] max-md:text-[3.333vw]">
Фасады ЖК можно изучить со всех сторон
Фасады можно изучить со всех сторон
</p>
</DisplacementCard>
@@ -27,8 +27,7 @@ export const WebTimelineData: { [key: string]: IAccordeonContent[] } = {
},
{
type: "new",
content:
"Получаем исходные данные: чертежи, фотографии, сканы помещений",
content: "Получаем исходные данные: чертежи, фотографии, сканы помещений",
},
{
type: "general",
@@ -36,22 +35,25 @@ export const WebTimelineData: { [key: string]: IAccordeonContent[] } = {
},
],
"Прототипирование": [
Прототипирование: [
{
type: "new",
content: "Моделируем пространства в Blender и 3Ds Max",
},
{
type: "general",
content: "Оптимизируем полигоны для использования на сайте: уменьшаем нагрузку",
content:
"Оптимизируем полигоны для использования на сайте: уменьшаем нагрузку",
},
{
type: "new",
content: "Создаем материалы и текстуры для реализма картинки с помощью PBR-рендеринга ",
content:
"Создаем материалы и текстуры для реализма картинки с помощью PBR-рендеринга ",
},
{
type: "general",
content: "Создаем предварительный рендер и согласовываем общий стиль проекта",
content:
"Создаем предварительный рендер и согласовываем общий стиль проекта",
},
],
@@ -68,10 +70,6 @@ export const WebTimelineData: { [key: string]: IAccordeonContent[] } = {
type: "general",
content: "Отрисовываем дизайн-макеты ключевых блоков",
},
{
type: "general",
content: "Согласование с заказчиком и правки",
},
],
"Дизайн сайта": [
@@ -91,16 +89,13 @@ export const WebTimelineData: { [key: string]: IAccordeonContent[] } = {
type: "general",
content: "Наполняем страницы контентом",
},
{
type: "general",
content: "Согласование с заказчиком и правки",
},
],
"Сборка сцен": [
{
type: "general",
content: "Компонуем виртуальные сцены: расставляем мебель, освещение и камеры",
content:
"Компонуем виртуальные сцены: расставляем мебель, освещение и камеры",
},
{
type: "general",
@@ -108,7 +103,7 @@ export const WebTimelineData: { [key: string]: IAccordeonContent[] } = {
"Готовим тестовые интерактивные 360° панорамы и экспортируем в веб-формат",
},
],
"Рендеринг": [
Рендеринг: [
{
type: "general",
content: "Готовим финальные версии всех интерактивных 360° панорам ",
@@ -129,10 +124,11 @@ export const WebTimelineData: { [key: string]: IAccordeonContent[] } = {
},
{
type: "general",
content: "Адаптируем страницы под мобильные устройства: жесты, touch-управление",
content:
"Адаптируем страницы под мобильные устройства: жесты, touch-управление",
},
],
"Оптимизация": [
Оптимизация: [
{
type: "general",
content: "Тестируем готовый сайт на разных устройствах.",
+5 -1
View File
@@ -10,8 +10,11 @@ import {
useState,
} from "react";
import { useModalStore } from "@/stores/useModalStore";
import { useAuthStore } from "@/stores/useAuthStore";
import { useCheckAuthQuery } from "@/queries/checkAuth";
export function LenisProvider({ children }: PropsWithChildren) {
const { data: auth } = useCheckAuthQuery();
const lenis = useRef<LenisRef>(null);
const { modal } = useModalStore();
const [lenisKey, setLenisKey] = useState(0);
@@ -35,6 +38,7 @@ export function LenisProvider({ children }: PropsWithChildren) {
);
useEffect(() => {
if (!auth) return;
if (modal) {
// Отключаем Lenis и блокируем скролл body
lenis.current?.lenis?.destroy();
@@ -61,7 +65,7 @@ export function LenisProvider({ children }: PropsWithChildren) {
lenis.current?.lenis?.start();
document.body.style.overflow = "";
}
}, [modal]);
}, [modal, auth]);
return (
<ReactLenis
+5 -5
View File
@@ -1,13 +1,13 @@
import { api } from '@/api';
import { useAuthStore } from '@/stores/useAuthStore';
import { useQuery } from '@tanstack/react-query';
import { api } from "@/api";
import { useAuthStore } from "@/stores/useAuthStore";
import { useQuery } from "@tanstack/react-query";
export function useCheckAuthQuery() {
const { token } = useAuthStore();
return useQuery({
queryKey: ['checkAuth'],
queryFn: () => api.get('auth/check').json<{ auth: boolean }>(),
queryKey: ["checkAuth"],
queryFn: () => api.get("auth/check").json<{ auth: boolean }>(),
select: (data) => data.auth,
enabled: !!token,
});