upd
This commit is contained in:
-161
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 160 KiB |
Binary file not shown.
+212
-91
@@ -10,6 +10,8 @@ import api from "./utils/api";
|
||||
import FeatureCard from "./components/FeatureCard";
|
||||
import RelevantExpCard from "./components/RelevantExpCard";
|
||||
import Title from "./components/Title";
|
||||
import FeatureCardMobile from "./components/FeatureCardMobile";
|
||||
import { EffectComposer, Bloom } from "@react-three/postprocessing";
|
||||
|
||||
function App() {
|
||||
const parallaxRef = useRef<HTMLDivElement>(null);
|
||||
@@ -19,8 +21,8 @@ function App() {
|
||||
const [company, setCompany] = useState<string>("");
|
||||
const [phone, setPhone] = useState<string>("");
|
||||
|
||||
const [mapPosition, setMapPosition] = useState<string>("29% 100%");
|
||||
const [mapCity, setMapCity] = useState<string>("Тюмень");
|
||||
// const [mapPosition, setMapPosition] = useState<string>("29% 100%");
|
||||
// const [mapCity, setMapCity] = useState<string>("Тюмень");
|
||||
|
||||
const [isShowRelevantExpCards, setIsShowRelevantExpCards] =
|
||||
useState<boolean>(false);
|
||||
@@ -31,10 +33,14 @@ function App() {
|
||||
|
||||
const featureImagesContainer = useRef<HTMLDivElement>(null);
|
||||
const featureImages = [
|
||||
"https://images.unsplash.com/photo-1682595950157-8d1cc0ef388f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxfHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=500&q=60",
|
||||
"https://images.unsplash.com/photo-1682402178953-f2cb484d4024?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw1fHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=500&q=60",
|
||||
"https://plus.unsplash.com/premium_photo-1676648534973-dd205cb63d99?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxMnx8fGVufDB8fHx8&auto=format&fit=crop&w=500&q=60",
|
||||
"https://images.unsplash.com/photo-1682353213492-8433d437855a?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxM3x8fGVufDB8fHx8&auto=format&fit=crop&w=500&q=60",
|
||||
"/images/cards/1.jpg",
|
||||
"/images/cards/2.jpg",
|
||||
"/images/cards/3.jpg",
|
||||
"/images/cards/4.jpg",
|
||||
"/images/cards/5.jpg",
|
||||
"/images/cards/6.jpg",
|
||||
"/images/cards/7.jpg",
|
||||
"/images/cards/8.jpg",
|
||||
];
|
||||
const [selectedFeatureImageIndex, setSelectedFeatureImageIndex] =
|
||||
useState<number>(0);
|
||||
@@ -88,21 +94,21 @@ function App() {
|
||||
alert("Заявка отправлена!");
|
||||
}
|
||||
|
||||
function handleClickComplexCard(city: string) {
|
||||
if (city === "Тюмень") {
|
||||
setMapPosition("29% 100%");
|
||||
} else if (city === "Екатеринбург") {
|
||||
setMapPosition("22% 93%");
|
||||
} else if (city === "Брянск") {
|
||||
setMapPosition("1.5% 48%");
|
||||
} else if (city === "Санкт-Петербург") {
|
||||
setMapPosition("6% 16%");
|
||||
} else if (city === "Нижний Тагил") {
|
||||
setMapPosition("19% 77%");
|
||||
}
|
||||
// function handleClickComplexCard(city: string) {
|
||||
// if (city === "Тюмень") {
|
||||
// setMapPosition("29% 100%");
|
||||
// } else if (city === "Екатеринбург") {
|
||||
// setMapPosition("22% 93%");
|
||||
// } else if (city === "Брянск") {
|
||||
// setMapPosition("1.5% 48%");
|
||||
// } else if (city === "Санкт-Петербург") {
|
||||
// setMapPosition("6% 16%");
|
||||
// } else if (city === "Нижний Тагил") {
|
||||
// setMapPosition("19% 77%");
|
||||
// }
|
||||
|
||||
setMapCity(city);
|
||||
}
|
||||
// setMapCity(city);
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
@@ -116,7 +122,7 @@ function App() {
|
||||
<div className="bg-[#131317] text-white">
|
||||
<div
|
||||
ref={parallaxRef}
|
||||
className="relative lg:container mx-auto p-4 xl:max-w-screen-2xl"
|
||||
className="relative lg:container mx-auto p-4 xl:max-w-screen-2xl overflow-hidden"
|
||||
>
|
||||
<div className="space-y-44 mb-44">
|
||||
<div className="flex justify-between">
|
||||
@@ -154,12 +160,12 @@ function App() {
|
||||
{/* <div className="h-24 w-0.5 bg-[#23242A]"></div> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute top-0 right-0 ">
|
||||
<div className="absolute top-0 right-0 sm:translate-x-0 sm:translate-y-0 translate-x-[40%] translate-y-[40%]">
|
||||
<img src="/images/shapes/1.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative xl:space-y-48 lg:space-y-40 sm:space-y-28 space-y-20">
|
||||
<div className="relative xl:space-y-48 lg:space-y-40 sm:space-y-28 space-y-20 overflow-hidden">
|
||||
<div
|
||||
className="max-w-screen h-screen bg-cover bg-no-repeat bg-center rounded-xl overflow-hidden"
|
||||
style={{ backgroundImage: "url('/images/content.jpg')" }}
|
||||
@@ -265,14 +271,12 @@ function App() {
|
||||
преимущества объекта
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute w-full h-full top-[-64px] left-0 flex justify-center items-center">
|
||||
<div className="lg:flex hidden absolute w-full h-full top-[-64px] left-0 justify-center items-center">
|
||||
<img src="/images/shapes/2.svg" alt="" />
|
||||
</div>
|
||||
{/* <div className="h-0.5 bg-[#23242A]"></div> */}
|
||||
{/* <Slider /> */}
|
||||
<div className="grid lg:grid-cols-2 relative z-50">
|
||||
<div className="relative lg:grid hidden lg:grid-cols-2 ">
|
||||
<FeatureCard
|
||||
text="Интеграция с CRM - системой"
|
||||
text="Интеграция с CRM системой"
|
||||
handleMouseEnter={() => setSelectedFeatureImageIndex(0)}
|
||||
/>
|
||||
<div className="row-span-2 h-full border border-[#454554] backdrop-blur-lg bg-[rgba(46, 46, 56, 0.1)] ">
|
||||
@@ -301,54 +305,70 @@ function App() {
|
||||
/>
|
||||
<FeatureCard
|
||||
text="Виртуальный тур по жилому комплексу"
|
||||
handleMouseEnter={() => setSelectedFeatureImageIndex(2)}
|
||||
handleMouseEnter={() => setSelectedFeatureImageIndex(4)}
|
||||
/>
|
||||
<FeatureCard
|
||||
text="Формирование вишлиста"
|
||||
handleMouseEnter={() => setSelectedFeatureImageIndex(1)}
|
||||
handleMouseEnter={() => setSelectedFeatureImageIndex(5)}
|
||||
/>
|
||||
<FeatureCard
|
||||
text="Конфигуратор интерьера"
|
||||
handleMouseEnter={() => setSelectedFeatureImageIndex(0)}
|
||||
handleMouseEnter={() => setSelectedFeatureImageIndex(6)}
|
||||
/>
|
||||
<FeatureCard
|
||||
text="Отправка коммерческого предложения"
|
||||
handleMouseEnter={() => setSelectedFeatureImageIndex(3)}
|
||||
handleMouseEnter={() => setSelectedFeatureImageIndex(7)}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative lg:hidden block">
|
||||
<FeatureCardMobile
|
||||
title="Интеграция с CRM системой"
|
||||
text="Фильтр позволит отметить конкретные преимущества, определить количество комнат, желаемый этаж, цену, и получить выборку подходящих вариантов"
|
||||
image="/images/cards/1.jpg"
|
||||
/>
|
||||
<FeatureCardMobile
|
||||
title="Параметрический поиск квартир"
|
||||
text="Приложение передает информацию о клиенте в CRM систему застройщика и получает актуальную информацию по ценам и статусам квартир."
|
||||
image="/images/cards/2.jpg"
|
||||
/>
|
||||
<FeatureCardMobile
|
||||
title="Вся инфрастуктура на одном экране"
|
||||
text="Возможность оценить инфраструктуру района покажет важные для клиента точки интереса и время, за которое он сможет до них дойти."
|
||||
image="/images/cards/3.jpg"
|
||||
/>
|
||||
<FeatureCardMobile
|
||||
title="Виртуальный тур по жилому комплексу"
|
||||
text="Клиент лично оценит угол инсоляции, малые архитектурные формы и ландшафт, перемещаясь по комплексу с помощью тапа."
|
||||
image="/images/cards/4.jpg"
|
||||
/>
|
||||
<FeatureCardMobile
|
||||
title="Конфигуратор интерьера"
|
||||
text="Клиент может свободно выбирать мебель и дизайн с помощью конфигуратора интерьера. Возможно выбрать стиль всей квартиры или изменить отдельные детали."
|
||||
image="/images/cards/5.jpg"
|
||||
/>
|
||||
<FeatureCardMobile
|
||||
title="Формирование вишлиста"
|
||||
text="Клиент может добавить варианты квартир в избранное, сравнить их между собой по основным параметрам и выбрать свою будущую квартиру."
|
||||
image="/images/cards/6.jpg"
|
||||
/>
|
||||
<FeatureCardMobile
|
||||
title="Любой рендер за несколько секунд"
|
||||
text="Когда для рекламы вам понадобится любой объект с любого ракурса, просто сделайте фотографию внутри презентации."
|
||||
image="/images/cards/7.jpg"
|
||||
/>
|
||||
<FeatureCardMobile
|
||||
title="Отправка коммерческого предложения"
|
||||
text="Коммерческое предложение с выбранными квартирами может быть отправлено клиенту на почту или распечатано и отдано лично в руки."
|
||||
image="/images/cards/8.jpg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:container mx-auto p-4 xl:max-w-screen-2xl overflow-hidden">
|
||||
<div className="lg:container mx-auto p-4 xl:max-w-screen-2xl">
|
||||
<div className="xl:grid grid-cols-2 gap-6 relative">
|
||||
<div ref={helmetRef} className="xl:block hidden">
|
||||
{helmetInView && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: "25%" }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.25, duration: 3 }}
|
||||
viewport={{ once: true }}
|
||||
className=" absolute w-full h-full"
|
||||
>
|
||||
<Canvas className="min-h-[1024px] w-[600px] -translate-y-80 -translate-x-[30%]">
|
||||
<Suspense fallback={null}>
|
||||
<ambientLight intensity={0.5} />
|
||||
<directionalLight color="white" position={[-3, 1, 5]} />
|
||||
<Model />
|
||||
{/* <OrbitControls
|
||||
enablePan={true}
|
||||
enableZoom={true}
|
||||
enableRotate={true}
|
||||
/> */}
|
||||
</Suspense>
|
||||
</Canvas>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 2.5, duration: 1 }}
|
||||
className="lg:space-y-16 space-y-8"
|
||||
>
|
||||
<Title>Экскурсия в виртуальной реальности</Title>
|
||||
@@ -362,6 +382,45 @@ function App() {
|
||||
Записаться в шоу-рум
|
||||
</button>
|
||||
</motion.div>
|
||||
|
||||
<div ref={helmetRef} className="xl:block hidden">
|
||||
{helmetInView && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: "25%" }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.25, duration: 3.25 }}
|
||||
className=" absolute w-full h-full"
|
||||
>
|
||||
<Canvas className="min-h-[1024px] w-[600px] -translate-y-[25%] -translate-x-[25%]">
|
||||
<Suspense fallback={null}>
|
||||
<ambientLight intensity={1} />
|
||||
<directionalLight color="white" position={[-3, 1, 5]} />
|
||||
<Model />
|
||||
{/* <OrbitControls
|
||||
enablePan={true}
|
||||
enableZoom={true}
|
||||
enableRotate={true}
|
||||
/> */}
|
||||
<EffectComposer>
|
||||
{/* <DepthOfField
|
||||
focusDistance={0}
|
||||
focalLength={0.02}
|
||||
bokehScale={2}
|
||||
height={480}
|
||||
/> */}
|
||||
{/* <Bloom
|
||||
luminanceThreshold={0}
|
||||
luminanceSmoothing={0.9}
|
||||
height={300}
|
||||
/> */}
|
||||
{/* <Noise opacity={0.02} />
|
||||
<Vignette eskil={false} offset={0.1} darkness={1.1} /> */}
|
||||
</EffectComposer>
|
||||
</Suspense>
|
||||
</Canvas>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -371,7 +430,11 @@ function App() {
|
||||
</div>
|
||||
|
||||
<div className="relative grid xl:grid-cols-2 gap-8">
|
||||
<div className="space-y-8">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
className="space-y-8"
|
||||
>
|
||||
<Title>Анализируем поведение пользователей</Title>
|
||||
<div className="2xl:text-2xl sm:text-xl text-lg space-y-4">
|
||||
<p>
|
||||
@@ -384,11 +447,15 @@ function App() {
|
||||
комплекса еще эффективнее.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid sm:grid-cols-3 gap-4">
|
||||
<div className="sm:col-span-2 space-y-4">
|
||||
<div className="2xl:p-8 sm:p-6 p-4 space-y-6 bg-[#272730] rounded-lg">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
className="2xl:p-8 sm:p-6 p-4 space-y-6 bg-[#272730] rounded-lg"
|
||||
>
|
||||
<p className="2xl:text-xl sm:text-base text-sm uppercase tracking-wider">
|
||||
Конверсия менеджеров в брони
|
||||
</p>
|
||||
@@ -398,7 +465,11 @@ function App() {
|
||||
К. Н. Федоров
|
||||
</span>
|
||||
<div className="w-[45%]">
|
||||
<div className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] h-10 rounded"></div>
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
whileInView={{ width: "100%" }}
|
||||
className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] h-10 rounded"
|
||||
></motion.div>
|
||||
</div>
|
||||
<span className="w-[15%] text-right">45%</span>
|
||||
</div>
|
||||
@@ -408,7 +479,11 @@ function App() {
|
||||
И. Ф. Яковлева
|
||||
</span>
|
||||
<div className="w-[45%]">
|
||||
<div className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] h-10 rounded"></div>
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
whileInView={{ width: "80%" }}
|
||||
className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] h-10 rounded"
|
||||
></motion.div>
|
||||
</div>
|
||||
<span className="w-[15%] text-right">30%</span>
|
||||
</div>
|
||||
@@ -418,14 +493,22 @@ function App() {
|
||||
А. М. Ташева
|
||||
</span>
|
||||
<div className="w-[45%]">
|
||||
<div className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] w-[83px] h-10 rounded"></div>
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
whileInView={{ width: "83px" }}
|
||||
className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] w-[83px] h-10 rounded"
|
||||
></motion.div>
|
||||
</div>
|
||||
<span className="w-[15%] text-right">20%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="2xl:p-8 sm:p-6 p-4 space-y-6 bg-[#272730] rounded-lg">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
className="2xl:p-8 sm:p-6 p-4 space-y-6 bg-[#272730] rounded-lg"
|
||||
>
|
||||
<p className="2xl:text-xl sm:text-base text-sm uppercase tracking-wider">
|
||||
Популярные типы квартир, %
|
||||
</p>
|
||||
@@ -469,27 +552,41 @@ function App() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="2xl:p-8 sm:p-6 p-4 space-y-6 bg-[#272730] rounded-lg">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
className="2xl:p-8 sm:p-6 p-4 space-y-6 bg-[#272730] rounded-lg"
|
||||
>
|
||||
<p className="2xl:text-xl sm:text-base text-sm uppercase tracking-wider">
|
||||
Воронка продаж
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-5 gap-4 items-center">
|
||||
<div className="col-span-4 2xl:text-xl sm:text-base text-sm bg-gradient-to-bl from-[#D375FF1A] to-[#798FFF1A] rounded flex justify-center space-x-4">
|
||||
<div className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] w-full h-10 rounded flex justify-center items-center">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
whileInView={{ width: "100%" }}
|
||||
className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] w-full h-10 rounded flex justify-center items-center"
|
||||
>
|
||||
Сеансы
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
<span className="col-span-1 text-right w-full 2xl:text-xl">100%</span>
|
||||
<span className="col-span-1 text-right w-full 2xl:text-xl">
|
||||
100%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-5 gap-4 items-center">
|
||||
<div className="col-span-4 2xl:text-xl sm:text-base text-sm bg-gradient-to-bl from-[#D375FF1A] to-[#798FFF1A] rounded flex justify-center space-x-4">
|
||||
<div className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] w-[90%] h-10 rounded flex justify-center items-center">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
whileInView={{ width: "90%" }}
|
||||
className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] w-[90%] h-10 rounded flex justify-center items-center whitespace-nowrap"
|
||||
>
|
||||
В избранное
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
<span className="col-span-1 text-right w-full 2xl:text-xl">
|
||||
93,47%
|
||||
@@ -498,9 +595,13 @@ function App() {
|
||||
|
||||
<div className="grid grid-cols-5 gap-4 items-center">
|
||||
<div className="col-span-4 2xl:text-xl sm:text-base text-sm bg-gradient-to-bl from-[#D375FF1A] to-[#798FFF1A] rounded flex justify-center space-x-4">
|
||||
<div className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] w-[50%] h-10 rounded flex justify-center items-center">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
whileInView={{ width: "50%" }}
|
||||
className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] w-[50%] h-10 rounded flex justify-center items-center"
|
||||
>
|
||||
Брони
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
<span className="col-span-1 text-right w-full 2xl:text-xl">
|
||||
45,68%
|
||||
@@ -509,20 +610,28 @@ function App() {
|
||||
|
||||
<div className="grid grid-cols-5 gap-4 items-center">
|
||||
<div className="col-span-4 2xl:text-xl sm:text-base text-sm bg-gradient-to-bl from-[#D375FF1A] to-[#798FFF1A] rounded flex justify-center space-x-4">
|
||||
<div className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] w-[35%] h-10 rounded flex justify-center items-center">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
whileInView={{ width: "35%" }}
|
||||
className="bg-gradient-to-bl from-[#BC75FF] to-[#798FFF] w-[35%] h-10 rounded flex justify-center items-center"
|
||||
>
|
||||
Продажи
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
<span className="col-span-1 text-right w-full 2xl:text-xl">
|
||||
29,13%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<div className="grid sm:grid-cols-1 grid-cols-2 gap-4 sm:space-y-4">
|
||||
<div className="bg-[#272730] rounded-lg flex flex-col justify-between items-start 2xl:p-8 sm:p-6 p-4 space-y-4">
|
||||
<div className="grid sm:grid-cols-1 grid-cols-2 gap-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
className="bg-[#272730] rounded-lg flex flex-col justify-between items-start 2xl:p-8 sm:p-6 p-4 space-y-4"
|
||||
>
|
||||
<div className="font-gilroy text-gradient">
|
||||
<p className="sm:text-6xl text-5xl">12</p>
|
||||
<p className="text-xl font-medium">минут</p>
|
||||
@@ -530,9 +639,13 @@ function App() {
|
||||
<p className="2xl:text-xl sm:text-base text-sm">
|
||||
Среднее время сеанса
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="bg-[#272730] rounded-lg flex flex-col justify-between items-start 2xl:p-8 sm:p-6 p-4 space-y-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
className="bg-[#272730] rounded-lg flex flex-col justify-between items-start 2xl:p-8 sm:p-6 p-4 space-y-4"
|
||||
>
|
||||
<div className="font-gilroy text-gradient">
|
||||
<p className="sm:text-6xl text-5xl">856</p>
|
||||
{/* <p className="text-xl font-medium">минут</p> */}
|
||||
@@ -540,9 +653,13 @@ function App() {
|
||||
<p className="2xl:text-xl sm:text-base text-sm">
|
||||
Коммерческих предложений сформировано
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="bg-[#272730] rounded-lg flex flex-col justify-between items-start 2xl:p-8 sm:p-6 p-4 space-y-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
className="bg-[#272730] rounded-lg flex flex-col justify-between items-start 2xl:p-8 sm:p-6 p-4 space-y-4"
|
||||
>
|
||||
<div className="font-gilroy text-gradient">
|
||||
<p className="sm:text-6xl text-5xl">12,41%</p>
|
||||
{/* <p className="text-xl font-medium">минут</p> */}
|
||||
@@ -550,9 +667,13 @@ function App() {
|
||||
<p className="2xl:text-xl sm:text-base text-sm">
|
||||
Конверсия из сеанса в бронь
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="bg-[#272730] rounded-lg flex flex-col justify-between items-start 2xl:p-8 sm:p-6 p-4 space-y-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
className="bg-[#272730] rounded-lg flex flex-col justify-between items-start 2xl:p-8 sm:p-6 p-4 space-y-4"
|
||||
>
|
||||
<div className="font-gilroy text-gradient">
|
||||
<p className="sm:text-6xl text-5xl">132,1</p>
|
||||
<p className="text-xl font-medium">млн. р.</p>
|
||||
@@ -560,7 +681,7 @@ function App() {
|
||||
<p className="2xl:text-xl sm:text-base text-sm">
|
||||
Получено через Graff.estate
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -829,12 +950,12 @@ function App() {
|
||||
<div className="space-y-8">
|
||||
<div className="space-x-4">
|
||||
<label className="text-xl">
|
||||
<input
|
||||
{/* <input
|
||||
type="checkbox"
|
||||
name=""
|
||||
className="bg-[#454554] hover:bg-[#454554] cursor-pointer
|
||||
w-10 h-10 rounded-lg checked:bg-[#454554] checked:hover:bg-[#454554] border-0 focus:ring-offset-0 focus:ring-transparent mr-4"
|
||||
/>
|
||||
/> */}
|
||||
<span className="text-[#ABABBA] text-opacity-50">
|
||||
Я согласен с
|
||||
</span>{" "}
|
||||
@@ -845,7 +966,7 @@ function App() {
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="2xl:text-3xl text-2xl w-full px-8 py-4 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] rounded-full opacity-95 hover:opacity-100 transition-opacity"
|
||||
className="2xl:text-3xl sm:text-2xl text-lg w-full px-8 py-4 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] rounded-full opacity-95 hover:opacity-100 transition-opacity"
|
||||
>
|
||||
Отправить заявку
|
||||
</button>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { ChangeEvent, useEffect, useState } from "react";
|
||||
import SearchableSelect from "./SearchableSelect";
|
||||
import api from "../utils/api";
|
||||
import { motion } from "framer-motion";
|
||||
import RangeSlider from "./RangeSlider";
|
||||
|
||||
function Calc() {
|
||||
const [regions, setRegions] = useState<any[]>([]);
|
||||
@@ -225,13 +226,23 @@ function Calc() {
|
||||
<p className="2xl:text-base text-sm text-[#C5C7CE]">
|
||||
Очных консультаций в месяц
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
pattern="^\d+$"
|
||||
className="px-5 py-3.5 w-full outline-none placeholder:text-white rounded border border-transparent transition-colors bg-[#454554] focus:border-[#BC75FF]"
|
||||
value={consultsPerMonth}
|
||||
onChange={handleChangeConsultsPerMonth}
|
||||
/>
|
||||
<div className="relative">
|
||||
<div className="absolute -bottom-[6px] left-0 w-full">
|
||||
<RangeSlider
|
||||
min={10}
|
||||
max={1000}
|
||||
defaultValue={consultsPerMonth}
|
||||
handleChange={(value) => setConsultsPerMonth(value)}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
pattern="^\d+$"
|
||||
className="px-5 py-3.5 w-full outline-none placeholder:text-white rounded border border-transparent transition-colors bg-[#454554] focus:border-[#BC75FF]"
|
||||
value={consultsPerMonth}
|
||||
onChange={handleChangeConsultsPerMonth}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-[#272730] 2xl:p-6 p-4 2xl:space-y-[29px] space-y-[25px] rounded">
|
||||
@@ -294,8 +305,8 @@ function Calc() {
|
||||
Результаты расчета
|
||||
</p>
|
||||
|
||||
<div className="grid xl:grid-cols-1 grid-cols-2 xl:gap-0 gap-6 xl:space-y-6">
|
||||
<div className="xl:border-b-2 xl:border-t-0 border-t-2 border-[#2E3038] xl:pt-0 pt-6 xl:pb-6 space-y-6">
|
||||
<div className="grid xl:grid-cols-1 lg:grid-cols-2 grid-cols-1 xl:gap-0 gap-6 xl:space-y-6">
|
||||
<div className="xl:border-b-2 xl:border-t-0 lg:border-t-2 border-t-0 border-[#2E3038] xl:pt-0 pt-6 xl:pb-6 space-y-6">
|
||||
<p className="text-[#C5C7CE]">Срок реализации</p>
|
||||
<div className="grid grid-cols-2">
|
||||
<div className="space-y-1">
|
||||
@@ -310,7 +321,7 @@ function Calc() {
|
||||
<p className="2xl:text-xl">{implementationTimeCase}</p>
|
||||
</div>
|
||||
{isEnabled && (
|
||||
<p className="text-xl fade-in">
|
||||
<p className="2xl:text-xl sm:text-base text-sm fade-in">
|
||||
На{" "}
|
||||
<span className="text-[#D375FF]">
|
||||
{diffImplementationTime} {diffImplementationTimeCase}
|
||||
@@ -336,7 +347,7 @@ function Calc() {
|
||||
<p className="2xl:text-xl">млн. р.</p>
|
||||
</div>
|
||||
{isEnabled && (
|
||||
<p className="text-xl fade-in">
|
||||
<p className="2xl:text-xl sm:text-base text-sm fade-in">
|
||||
На{" "}
|
||||
<span className="text-[#D375FF]">
|
||||
{diffProfitPerMonth} млн. р.
|
||||
@@ -351,7 +362,7 @@ function Calc() {
|
||||
<div className="pt-3.5">
|
||||
<button
|
||||
className={[
|
||||
"w-full px-6 py-3 rounded-full",
|
||||
"xl:w-full lg:w-[calc(100%/2-12px)] sm:w-fit w-full px-6 py-3 rounded-full sm:text-xl text-lg",
|
||||
isEnabled ? "bg-[#2E2E38]" : "bg-gradient",
|
||||
].join(" ")}
|
||||
onClick={() => setIsEnabled(!isEnabled)}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { motion } from "framer-motion";
|
||||
import React from "react";
|
||||
|
||||
interface IFeatureCard {
|
||||
title: string;
|
||||
text: string;
|
||||
image: string;
|
||||
}
|
||||
|
||||
function FeatureCardMobile({ title, text, image }: IFeatureCard) {
|
||||
return (
|
||||
<div>
|
||||
<motion.div
|
||||
initial={{ height: "144px" }}
|
||||
whileInView={{ height: "448px" }}
|
||||
viewport={{ once: true }}
|
||||
className="relative flex flex-col justify-between border border-[#454554] 2xl:h-44 h-36 p-4 transition-colors bg-[#2E2E38] bg-opacity-10"
|
||||
>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
className="absolute top-0 left-0 w-full h-full bg-center bg-no-repeat bg-cover"
|
||||
style={{ backgroundImage: `url('${image}')` }}
|
||||
></motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
className="absolute top-0 left-0 w-full h-full bg-gradient-to-b from-transparent to-[#17171CE5]"
|
||||
></motion.div>
|
||||
|
||||
<motion.svg
|
||||
initial={{ opacity: 1 }}
|
||||
whileInView={{ opacity: 0 }}
|
||||
viewport={{ once: true }}
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="relative 2xl:h-full 2xl:w-full h-6 w-6"
|
||||
>
|
||||
<g clipPath="url(#clip0_1414_844)">
|
||||
<path
|
||||
d="M2.14528 38.9894L39 1M39 1L38.7903 39M39 1H1"
|
||||
stroke="#D9D9D9"
|
||||
strokeWidth="3"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1414_844">
|
||||
<rect
|
||||
width="40"
|
||||
height="40"
|
||||
fill="white"
|
||||
transform="matrix(1 0 0 -1 0 40)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</motion.svg>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
className="relative overflow-hidden"
|
||||
>
|
||||
<p className="font-gilroy uppercase 2xl:text-2xl sm:text-xl tracking-wider">
|
||||
{title}
|
||||
</p>
|
||||
<p className="relative 2xl:text-2xl sm:text-xl">{text}</p>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FeatureCardMobile;
|
||||
@@ -4,124 +4,27 @@ interface IRangeSlider {
|
||||
min: number;
|
||||
max: number;
|
||||
defaultValue: number;
|
||||
onChange: (value: number) => void;
|
||||
handleChange: (value: number) => void;
|
||||
}
|
||||
|
||||
function RangeSlider({ min, max, defaultValue, onChange }: IRangeSlider) {
|
||||
function RangeSlider({ min, max, defaultValue, handleChange }: IRangeSlider) {
|
||||
const [value, setValue] = useState<number>(defaultValue);
|
||||
const [isEdited, setIsEdited] = useState<boolean>(false);
|
||||
const [editableValue, setEditableValue] = useState<number>(value);
|
||||
|
||||
function saveValue() {
|
||||
if (editableValue < min || editableValue > max) {
|
||||
setEditableValue(Math.round((max + min) / 2));
|
||||
setValue(Math.round((max + min) / 2));
|
||||
} else {
|
||||
setValue(editableValue);
|
||||
}
|
||||
|
||||
setIsEdited(false);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setValue(defaultValue);
|
||||
}, [defaultValue]);
|
||||
|
||||
useEffect(() => {
|
||||
onChange(value);
|
||||
}, [value]);
|
||||
|
||||
function handleChangeEditableValue(e: ChangeEvent<HTMLInputElement>) {
|
||||
const pattern = new RegExp(e.target.pattern);
|
||||
|
||||
if (pattern.test(e.target.value)) {
|
||||
if (+e.target.value >= 1000000000) {
|
||||
setEditableValue(1000000000);
|
||||
} else if (+e.target.value < 1) {
|
||||
setEditableValue(1);
|
||||
} else {
|
||||
setEditableValue(+e.target.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<p className="text-xl flex space-x-2">
|
||||
{!isEdited ? (
|
||||
<>
|
||||
<span>{value.toLocaleString()}</span>
|
||||
<button
|
||||
className="opacity-90 hover:opacity-100 transition-opacity"
|
||||
onClick={() => setIsEdited(true)}
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 5H4C3.44772 5 3 5.44772 3 6V20C3 20.5523 3.44772 21 4 21H18C18.5523 21 19 20.5523 19 20V12M8 16V14L15 7L17 9L10 16H8ZM17 5L19 3L21 5L19 7L17 5Z"
|
||||
stroke="#F2F2F2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
pattern="^[0-9]+$"
|
||||
size={editableValue.toString().length}
|
||||
className="bg-[#23242A] outline-none"
|
||||
value={editableValue}
|
||||
onChange={handleChangeEditableValue}
|
||||
autoFocus
|
||||
/>
|
||||
<button
|
||||
className="opacity-90 hover:opacity-100 transition-opacity"
|
||||
onClick={saveValue}
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5 11L10 16L19 8"
|
||||
stroke="#F2F2F2"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
<input
|
||||
type="range"
|
||||
min={min}
|
||||
max={max}
|
||||
value={value}
|
||||
className="transition-all w-full h-0.5 bg-[#798FFF] custom-slider"
|
||||
onChange={(e) => (
|
||||
setValue(+e.target.value),
|
||||
setEditableValue(Math.round(+e.target.value))
|
||||
)}
|
||||
/>
|
||||
<div className="flex justify-between text-[#798FFF]">
|
||||
<span>{min.toLocaleString()}</span>
|
||||
|
||||
<span>{max.toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min={min}
|
||||
max={max}
|
||||
value={value}
|
||||
className="transition-all w-full h-0.5 bg-[#798FFF] custom-slider"
|
||||
onChange={(e) => (
|
||||
handleChange(+e.target.value), setValue(+e.target.value)
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useGLTF, useAnimations } from "@react-three/drei";
|
||||
|
||||
export function Model(props: any) {
|
||||
const group = useRef(null);
|
||||
const { nodes, materials, animations }: any = useGLTF("/VRAnim_46.glb");
|
||||
const { nodes, materials, animations }: any = useGLTF("/VRAnim_47.glb");
|
||||
const { actions }: any = useAnimations(animations, group);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -22,7 +22,7 @@ export function Model(props: any) {
|
||||
<group ref={group} {...props} dispose={null}>
|
||||
<group
|
||||
name="Scene"
|
||||
position={[0, 0, -100]}
|
||||
position={[0, 0, -80]}
|
||||
rotation={[0, Math.PI / -2, 0]}
|
||||
>
|
||||
<mesh
|
||||
@@ -35,4 +35,4 @@ export function Model(props: any) {
|
||||
);
|
||||
}
|
||||
|
||||
useGLTF.preload("/VRAnim_46.glb");
|
||||
useGLTF.preload("/VRAnim_47.glb");
|
||||
|
||||
Reference in New Issue
Block a user