This commit is contained in:
2023-05-19 20:35:05 +05:00
parent f258778f2d
commit 079d0a6107
33 changed files with 808 additions and 368 deletions
+2 -2
View File
@@ -2,9 +2,9 @@
<html lang="">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>Интерактивные решения для застройщиков</title>
</head>
<body>
<div id="root"></div>
+1
View File
@@ -20,6 +20,7 @@
"react-rangeslider": "^2.2.0",
"react-responsive-carousel": "^3.2.23",
"react-scroll": "^1.8.9",
"react-yandex-metrika": "^2.6.0",
"swiper": "^9.2.0",
"three": "^0.151.3",
"zustand": "^4.3.8"
Binary file not shown.
Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.0126 63.5512C49.4365 63.5512 63.5613 49.3248 63.5613 31.7756C63.5613 29.6263 63.3495 27.5269 62.9456 25.4974H43.8858V38.0965H50.0359C47.4423 45.5818 40.3713 50.952 32.055 50.952C21.5398 50.952 13.0155 42.3664 13.0155 31.7756C13.0155 21.1848 21.5398 12.5992 32.055 12.5992V0H32.0126C14.5887 0 0.463867 14.2264 0.463867 31.7756C0.463867 49.3248 14.5887 63.5512 32.0126 63.5512Z" fill="#798FFF"/>
<path d="M30.4261 0.0385644C16.871 2.15049 6.49536 13.931 6.49536 28.1472C6.49536 43.8574 19.1662 56.593 34.7964 56.593C50.4267 56.593 63.0975 43.8574 63.0975 28.1472C63.0975 27.2411 63.0553 26.3449 62.9729 25.4605H43.8765V38.0414H50.0305C47.4352 45.5158 40.3599 50.8783 32.0385 50.8783C21.5167 50.8783 12.9872 42.3051 12.9872 31.7296C12.9872 21.729 20.6146 13.519 30.3412 12.6559C30.9004 12.6063 31.4665 12.581 32.0385 12.581V0H31.996C31.4696 0 30.9462 0.0129614 30.4261 0.0385644Z" fill="#D375FF"/>
<path opacity="0.3" d="M28.3436 12.9886C29.5316 12.7616 30.7581 12.6428 32.0126 12.6428V0H31.9695C29.4296 0 26.9587 0.293941 24.5894 0.849496L28.3436 12.9886Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M11.709 7.1846L9.74292 35.7185L12.5531 33.0414C12.5364 32.7123 12.528 32.381 12.528 32.0477C12.528 21.4743 21.0239 12.8891 31.5487 12.7721L18.9275 2.7832C16.3203 3.9294 13.8956 5.41515 11.709 7.1846Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M0.465096 32.136L14.8417 39.8933C13.7036 37.4501 13.0683 34.7263 13.0683 31.8544C13.0683 28.9786 13.7054 26.2513 14.8464 23.8056L10.4426 8.81348C4.29975 14.5819 0.463867 22.771 0.463867 31.8544C0.463867 31.9484 0.464277 32.0422 0.465096 32.136Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M0.463867 30.8628L19.5379 59.4596L40.3637 59.8405L32.2394 50.8874C32.1539 50.8885 32.0683 50.8891 31.9826 50.8891C21.486 50.8891 12.9769 42.3811 12.9769 31.8859C12.9769 31.4592 12.991 31.0359 13.0187 30.6162L0.472692 30.6162C0.469436 30.6983 0.466494 30.7805 0.463867 30.8628Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M14.3097 57.985L26.4453 50.0062C18.5941 47.5782 12.9044 40.4127 12.9044 31.951C12.9044 30.8637 12.9983 29.7979 13.1787 28.7607L5.10352 48.7573C7.48783 52.4173 10.628 55.5629 14.3097 57.985Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M42.2195 61.8851C38.982 62.9654 35.513 63.5511 31.9052 63.5511C25.2415 63.5511 19.0515 61.5529 13.9185 58.1315L26.0686 50.0986C27.9232 50.6792 29.8987 50.9925 31.9484 50.9925C32.024 50.9925 32.0995 50.9921 32.1749 50.9912L42.2195 61.8851Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M38.9719 62.7762C36.6999 63.2834 34.3362 63.5511 31.9093 63.5511C30.8578 63.5511 29.8181 63.5008 28.7927 63.4026L25.9812 50.0986C26.3608 50.2208 26.7455 50.3317 27.135 50.4308L38.9719 62.7762Z" fill="black" fill-opacity="0.4"/>
<path opacity="0.3" d="M44.0754 30.6163L63.0975 26.1207C63.0625 25.9177 63.0258 25.7154 62.9872 25.5137H44.0754V30.6163Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M48.0297 38.0384L63.0974 25.5346C63.096 25.5276 63.0947 25.5206 63.0933 25.5137L44.5393 38.0384H48.0297Z" fill="black" fill-opacity="0.4"/>
<path opacity="0.3" d="M56.8738 25.5137L43.1475 61.2322C48.5545 59.1664 53.2472 55.6523 56.7525 51.1636L58.9218 25.5137H56.8738Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M62.9575 25.5137L43.1475 61.2322C55.0796 56.7129 63.5613 45.1896 63.5613 31.6876C63.5613 29.5748 63.3536 27.5104 62.9575 25.5137Z" fill="black" fill-opacity="0.4"/>
<path d="M50.5846 0H63.0975V12.5247H50.5846V0Z" fill="#798FFF"/>
<path d="M63.0975 12.5247H50.5846L44.0754 19.019H56.1248L63.0975 12.5247Z" fill="#798FFF"/>
<path d="M50.5846 12.5247V0L44.0754 6.95816V19.019L50.5846 12.5247Z" fill="#798FFF"/>
<path opacity="0.3" d="M63.0975 10.7935V12.5408L56.2041 19.0187H54.7463V10.6689L63.0975 10.7935Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M44.0754 19.0188V6.76921L52.8114 6.03027L55.6742 6.78702L44.0754 19.0188Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M49.6405 1.3916L44.0754 7.12313V19.0189H51.4987L49.6405 1.3916Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M44.4055 6.47622L44.0754 6.83173V19.0189L51.0638 12.294L54.3349 12.1655L61.7056 13.4079L61.0047 12.3583L52.1683 2.7832L44.4055 6.47622Z" fill="#D375FF"/>
<path opacity="0.3" d="M51.0346 0H50.5742L48.7148 1.98114L50.9086 3.24714L51.0346 0Z" fill="black" fill-opacity="0.6"/>
<path opacity="0.3" d="M60.7778 14.9295L56.4373 19.0187H55.6743L56.3559 10.8435L59.7322 10.2051L60.7778 14.9295Z" fill="black" fill-opacity="0.6"/>
<path d="M50.5708 12.5247H63.0975V0H50.5708V12.5247Z" fill="url(#paint0_linear_1658_21330)"/>
<defs>
<linearGradient id="paint0_linear_1658_21330" x1="57.6988" y1="0" x2="57.6988" y2="12.5247" gradientUnits="userSpaceOnUse">
<stop stopColor="#D375FF"/>
<stop offset="1" stopColor="#798FFF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

+126 -187
View File
@@ -1,69 +1,32 @@
import "./App.css";
// import { Canvas } from "@react-three/fiber";
import { FormEvent, useEffect, useRef, useState } from "react";
import { FormEvent, useRef, useState } from "react";
import PieChart from "./components/PieChart/PieChart";
// import { Model } from "./components/VRHemlet";
import Calc from "./components/Calc";
import { motion } from "framer-motion";
import InputMask from "react-input-mask";
import api from "./utils/api";
import FeatureCard from "./components/FeatureCard";
import ComplexCard from "./components/ComplexCard";
import Title from "./components/Title";
import FeatureCardMobile from "./components/FeatureCardMobile";
import Modal from "./components/Modal";
import Slider from "./components/Slider/Slider";
import FeatureSlider from "./components/FeatureSlider";
import useModalStore from "./store/modal";
import FeedbackForm from "./components/FeedbackForm";
import { YMInitializer } from "react-yandex-metrika";
import Map from "./components/Map";
function App() {
const parallaxRef = useRef<HTMLDivElement>(null);
const [fullname, setFullname] = useState<string>("");
const [email, setEmail] = useState<string>("");
const [company, setCompany] = useState<string>("");
const [phone, setPhone] = useState<string>("");
// const [mapPosition, setMapPosition] = useState<string>("29% 100%");
// const [mapCity, setMapCity] = useState<string>("Тюмень");
const [isShowComplexCards, setIsShowComplexCards] = useState<boolean>(false);
// const helmetRef = useRef(null);
// const helmetInView = useInView(helmetRef);
// const [helmetIsAnim, setHelmetIsAnim] = useState<boolean>(true);
// const featureImagesContainer = useRef<HTMLDivElement>(null);
// const featureImages = [
// "/videos/Integra_CRM.mp4",
// "/videos/Uralsky.mp4",
// "/videos/Integra_CRM.mp4",
// "/videos/Integra_CRM.mp4",
// "/videos/Integra_CRM.mp4",
// "/videos/Integra_CRM.mp4",
// "/videos/Integra_CRM.mp4",
// "/videos/Integra_CRM.mp4",
// ];
// const [selectedFeatureImageIndex, setSelectedFeatureImageIndex] =
// useState<number>(0);
// useEffect(() => {
// if (featureImagesContainer.current) {
// featureImagesContainer.current.insertAdjacentHTML(
// "beforeend",
// `<div>
// <video muted autoplay data-index="${selectedFeatureImageIndex}" class="absolute top-0 left-0 w-full h-full fade-in flex justify-center">
// <source src="${featureImages[selectedFeatureImageIndex]}">
// </video>
// </div>`
// );
// if (featureImagesContainer.current.children.length > 1) {
// setTimeout(() => {
// featureImagesContainer.current?.firstElementChild?.remove();
// }, 1000);
// }
// }
// }, [selectedFeatureImageIndex]);
const [modalComponent, setModalComponent] = useModalStore((state) => [
state.component,
state.setComponent,
]);
async function handleSubmitSendMail(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
@@ -89,17 +52,24 @@ function App() {
<div className="bg-[#131317] text-white">
<div
ref={parallaxRef}
className="relative lg:container mx-auto p-4 xl:max-w-screen-2xl overflow-hidden"
className="relative lg:container mx-auto px-4 lg:py-8 py-4 xl:max-w-screen-2xl overflow-hidden"
>
<div className="2xl:space-y-44 xl:space-y-40 sm:space-y-36 space-y-20 2xl:mb-44 xl:mb-40 sm:mb-36 mb-20">
<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 className="relative 2xl:space-y-44 xl:space-y-40 sm:space-y-36 space-y-20 2xl:mb-44 xl:mb-40 sm:mb-36 mb-20">
<div className="flex justify-between">
<div className="">
<a href="/">
<img src="/images/logo.png" alt="" />
<img src="/logo.svg" alt="" />
</a>
</div>
<div className="flex sm:space-x-8 space-x-2">
<button className="px-4 py-2 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] text-white rounded-full opacity-95 hover:opacity-100 transition-opacity">
<div className="sm:space-x-8 space-x-2">
<button
onClick={() => setModalComponent(<FeedbackForm />)}
className="px-4 py-2 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] text-white rounded-full opacity-95 hover:opacity-100 transition-opacity"
>
<span className="sm:block hidden">Связаться с нами</span>
<span className="sm:hidden block">Связаться</span>
</button>
@@ -116,21 +86,20 @@ function App() {
</p>
</div>
</div>
<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 overflow-hidden">
<video
autoPlay={true}
muted={true}
loop={true}
playsInline={true}
className="aspect-video w-full rounded-2xl"
>
<source src="/videos/showreel.webm" type="video/mp4" />
</video>
<div className="px-4">
<video
autoPlay={true}
muted={true}
loop={true}
playsInline={true}
className="aspect-video w-full 2xl:rounded-2xl xl:rounded-xl lg:rounded-lg rounded"
>
<source src="/videos/showreel.mp4" type="video/mp4" />
</video>
</div>
<div className="lg:container mx-auto p-4 xl:max-w-screen-2xl">
<div className="space-y-32">
<div className="space-y-8">
@@ -147,6 +116,35 @@ function App() {
<div className="grid lg:grid-cols-2 grid-cols-1 gap-8">
<div className="space-y-20">
<div className="grid sm:grid-cols-2 gap-6">
<div className="border-t-2 pt-8 border-[#454554]">
<p className="sm:text-lg">На</p>
<div className="space-x-2 pt-4">
<span className="text-gradient font-gilroy 2xl:text-8xl sm:text-8xl text-6xl ">
18%
</span>
</div>
<p className="sm:text-lg">
увеличивает конверсию
<br />
из консультации в бронирование
</p>
</div>
<div className="border-t-2 pt-8 border-[#454554]">
<p className="sm:text-lg">На</p>
<div className="space-x-2 pt-4">
<span className="text-gradient font-gilroy 2xl:text-8xl sm:text-8xl text-6xl ">
12%
</span>
{/* <span className="text-gradient text-2xl font-bold">раз</span> */}
</div>
<p className="sm:text-lg">
увеличивает конверсию
<br />
из бронирования в продажу
</p>
</div>
<div className="border-t-2 pt-8 border-[#454554]">
<p className="sm:text-lg">До</p>
<div className="space-x-2 pt-4">
@@ -170,53 +168,28 @@ function App() {
<span className="text-gradient font-gilroy 2xl:text-8xl sm:text-8xl text-6xl ">
26
</span>
{/* <span className="text-gradient text-2xl font-bold">раз</span> */}
</div>
<p className="sm:text-lg">
что-то на умном
<br />в две строчки
</p>
</div>
<div className="border-t-2 pt-8 border-[#454554]">
<p className="sm:text-lg">На</p>
<div className="space-x-2 pt-4">
<span className="text-gradient font-gilroy 2xl:text-8xl sm:text-8xl text-6xl ">
18%
</span>
{/* <span className="text-gradient text-2xl font-bold">раз</span> */}
</div>
<p className="sm:text-lg">
увеличивает конверсию
<br />
из консультации в бронирование
</p>
</div>
<div className="border-t-2 pt-8 border-[#454554]">
<p className="sm:text-lg">На</p>
<div className="space-x-2 pt-4">
<span className="text-gradient font-gilroy 2xl:text-8xl sm:text-8xl text-6xl ">
12%
</span>
{/* <span className="text-gradient text-2xl font-bold">раз</span> */}
</div>
<p className="sm:text-lg">
увеличивает конверсию
<br />
из бронирования в продажу
</p>
</div>
</div>
<button className="lg:block hidden self-start 2xl:text-xl px-8 py-3 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] text-white rounded-full opacity-95 hover:opacity-100 transition-opacity sm:w-auto w-full">
<button
onClick={() => setModalComponent(<FeedbackForm />)}
className="lg:block hidden self-start 2xl:text-xl px-8 py-3 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] text-white rounded-full opacity-95 hover:opacity-100 transition-opacity sm:w-auto w-full"
>
Заказать решение
</button>
</div>
<div className="h-full min-h-[520px] rounded overflow-hidden">
<Slider />
</div>
<button className="lg:hidden block sm:w-fit self-start 2xl:text-xl px-8 py-3 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] text-white rounded-full opacity-95 hover:opacity-100 transition-opacity">
<button
onClick={() => setModalComponent(<FeedbackForm />)}
className="lg:hidden block sm:w-fit self-start 2xl:text-xl px-8 py-3 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] text-white rounded-full opacity-95 hover:opacity-100 transition-opacity"
>
Заказать решение
</button>
</div>
@@ -239,95 +212,46 @@ function App() {
<FeatureSlider />
{/* <div className="relative lg:grid hidden lg:grid-cols-2 ">
<FeatureCard
title="Виртуальный тур по жилому комплексу"
text="Клиент лично оценит угол инсоляции, малые архитектурные формы и ландшафт, перемещаясь по комплексу с помощью тапа."
handleMouseEnter={() => setSelectedFeatureImageIndex(0)}
/>
<div className="overflow-hidden row-span-2 h-full border border-[#454554] backdrop-blur-lg bg-[rgba(46, 46, 56, 0.1)] ">
<div
ref={featureImagesContainer}
className="w-full h-full"
></div>
</div>
<FeatureCard
title="Вся инфрастуктура на одном экране"
text="Возможность оценить инфраструктуру района покажет важные для клиента точки интереса и время, за которое он сможет до них дойти."
handleMouseEnter={() => setSelectedFeatureImageIndex(1)}
/>
<FeatureCard
title="Конфигуратор интерьера"
text="Клиент может свободно выбирать мебель и дизайн с помощью конфигуратора интерьера. Возможно выбрать стиль всей квартиры или изменить отдельные детали."
handleMouseEnter={() => setSelectedFeatureImageIndex(2)}
/>
<FeatureCard
title="Параметрический поиск квартир"
text="Фильтр позволит отметить конкретные преимущества, определить количество комнат, желаемый этаж, цену, и получить выборку подходящих вариантов."
handleMouseEnter={() => setSelectedFeatureImageIndex(3)}
/>
<FeatureCard
title="Интеграция с CRM - системой"
text="Приложение передает информацию о клиенте в CRM-систему застройщика и получает актуальную информацию по ценам и статусам квартир."
handleMouseEnter={() => setSelectedFeatureImageIndex(4)}
/>
<FeatureCard
title="Любой рендер за несколько секунд"
text="Когда для рекламы вам понадобится любой объект с любого ракурса, просто сделайте фотографию внутри презентации."
handleMouseEnter={() => setSelectedFeatureImageIndex(5)}
/>
<FeatureCard
title="Формирование вишлиста"
text="Клиент может добавить варианты квартир в избранное, сравнить их между собой по основным параметрам и выбрать свою будущую квартиру."
handleMouseEnter={() => setSelectedFeatureImageIndex(6)}
/>
<FeatureCard
title="Отправка коммерческого предложения"
text="Коммерческое предложение с выбранными квартирами может быть отправлено клиенту на почту или распечатано и отдано лично в руки."
handleMouseEnter={() => setSelectedFeatureImageIndex(7)}
/>
</div> */}
<div className="relative lg:hidden block">
<FeatureCardMobile
title="Виртуальный тур по жилому комплексу"
text="Клиент лично оценит угол инсоляции, малые архитектурные формы и ландшафт, перемещаясь по комплексу с помощью тапа."
image="/images/cards/1.jpg"
src="/videos/features/virtual_tour.mp4"
/>
<FeatureCardMobile
title="Вся инфрастуктура на одном экране"
text="Возможность оценить инфраструктуру района покажет важные для клиента точки интереса и время, за которое он сможет до них дойти."
image="/images/cards/2.jpg"
src="/videos/features/nks_infra.mp4"
/>
<FeatureCardMobile
title="Конфигуратор интерьера"
text="Клиент может свободно выбирать мебель и дизайн с помощью конфигуратора интерьера. Возможно выбрать стиль всей квартиры или изменить отдельные детали."
image="/images/cards/3.jpg"
src="/videos/features/uralsky.mp4"
/>
<FeatureCardMobile
title="Параметрический поиск квартир"
text="Фильтр позволит отметить конкретные преимущества, определить количество комнат, желаемый этаж, цену, и получить выборку подходящих вариантов."
image="/images/cards/4.jpg"
src="/videos/features/parametric.mp4"
/>
<FeatureCardMobile
title="Интеграция с CRM - системой"
text="Приложение передает информацию о клиенте в CRM-систему застройщика и получает актуальную информацию по ценам и статусам квартир."
image="/images/cards/5.jpg"
src="/videos/features/integra_crm.mp4"
/>
<FeatureCardMobile
title="Любой рендер за несколько секунд"
text="Когда для рекламы вам понадобится любой объект с любого ракурса, просто сделайте фотографию внутри презентации."
image="/images/cards/6.jpg"
src="/videos/features/render.mp4"
/>
<FeatureCardMobile
title="Формирование вишлиста"
text="Клиент может добавить варианты квартир в избранное, сравнить их между собой по основным параметрам и выбрать свою будущую квартиру."
image="/images/cards/7.jpg"
src="/videos/features/wish.mp4"
/>
<FeatureCardMobile
title="Отправка коммерческого предложения"
text="Коммерческое предложение с выбранными квартирами может быть отправлено клиенту на почту или распечатано и отдано лично в руки."
image="/images/cards/8.jpg"
src="/videos/features/send.mp4"
/>
</div>
</div>
@@ -347,7 +271,10 @@ function App() {
</p>
</div>
<button className="2xl:text-xl px-8 py-4 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] text-white rounded-full opacity-95 hover:opacity-100 transition-opacity sm:w-auto w-full">
<button
onClick={() => setModalComponent(<FeedbackForm />)}
className="2xl:text-xl px-8 py-4 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] text-white rounded-full opacity-95 hover:opacity-100 transition-opacity sm:w-auto w-full"
>
Записаться в шоу-рум
</button>
</div>
@@ -628,7 +555,7 @@ function App() {
</div>
<div className="grid lg:grid-cols-2 lg:gap-4 items-center">
<div className="space-y-16">
<div className="relative z-10 space-y-16">
<div className="space-y-14">
<Title>
Покажите все преимущества вашего жилого комплекса клиенту{" "}
@@ -649,7 +576,11 @@ function App() {
</div>
</div>
<div className="space-y-8">
<button className="text-xl border border-[#BC75FF] px-6 py-3 pr-3 flex items-center space-x-2 rounded-full cursor-pointer">
<a
href="https://stream.graff.tech"
target="_blank"
className="w-fit text-xl border border-[#BC75FF] px-6 py-3 pr-3 flex items-center space-x-2 rounded-full cursor-pointer"
>
<span>Узнать больше</span>
<svg
width="24"
@@ -666,13 +597,15 @@ function App() {
strokeLinejoin="round"
/>
</svg>
</button>
</a>
</div>
</div>
<div className="lg:absolute z-10 xl:top-2 lg:top-[335px] right-4 2xl:w-full lg:w-[52%] w-full flex lg:justify-end">
<Map />
{/* <div className="lg:absolute xl:top-2 lg:top-[335px] right-4 2xl:w-full lg:w-[52%] w-full flex lg:justify-end">
<img src="/images/devices.png" alt="" className="" />
</div>
</div> */}
</div>
</div>
</div>
@@ -704,10 +637,16 @@ function App() {
<Title>Реализованные проекты</Title>
<div className="2xl:space-y-10 xl:space-y-8 space-y-6 flex flex-col">
<div className="grid lg:grid-cols-2 xl:gap-8 gap-4">
<ComplexCard
{/* <ComplexCard
image="/images/cards/1.jpg"
name="ЖК «Life Резиденция»"
location="Россия, Тюмень."
/> */}
<ComplexCard
image="/images/cards/5.jpg"
name="ЖК «Айвазовский»"
location="Россия, Тюмень."
src="/videos/Ivazowsky.mp4"
/>
<ComplexCard
image="/images/cards/2.jpg"
@@ -715,26 +654,26 @@ function App() {
location="Россия, Екатеринбург."
src="/videos/RE_Volution_Towers.mp4"
/>
<ComplexCard
image="/images/cards/3.jpg"
name="Iskan Abu Dhabi"
location="ОАЭ, Абу-Даби."
src="/videos/Iskan_Abu_Dhabi.mp4"
/>
<ComplexCard
image="/images/cards/4.jpg"
name="ЖК «Авторский квартал Машаров»"
location="Россия, Тюмень."
src="/videos/Masharov.mp4"
/>
<ComplexCard
image="/images/cards/8.jpg"
name="ЖК «Уральский»"
location="Россия, Екатеринбург."
src="/videos/Uralsky.mp4"
/>
{isShowComplexCards && (
<>
<ComplexCard
image="/images/cards/5.jpg"
name="ЖК «Айвазовский»"
location="Россия, Тюмень."
src="/videos/Ivazowsky.mp4"
image="/images/cards/3.jpg"
name="Iskan Abu Dhabi"
location="ОАЭ, Абу-Даби."
src="/videos/Iskan_Abu_Dhabi.mp4"
/>
<ComplexCard
image="/images/cards/6.jpg"
@@ -748,12 +687,7 @@ function App() {
location="Россия, Екатеринбург."
src="/videos/4you.mp4"
/>
<ComplexCard
image="/images/cards/8.jpg"
name="ЖК «Уральский»"
location="Россия, Екатеринбург."
src="/videos/Uralsky.mp4"
/>
<ComplexCard
image="/images/cards/9.jpg"
name="ЖК «Новая Атмосфера»"
@@ -772,16 +706,16 @@ function App() {
location="Россия, Екатеринбург."
src="/videos/Dom_na_Opal.mp4"
/>
<ComplexCard
{/* <ComplexCard
image="/images/cards/12.jpg"
name="ЖК «Свой Круг»"
location="Россия, Екатеринбург."
/>
<ComplexCard
/> */}
{/* <ComplexCard
image="/images/cards/13.jpg"
name="ЖК «Александровский»"
location="Россия, Нижний Тагил."
/>
/> */}
</>
)}
</div>
@@ -966,10 +900,12 @@ function App() {
<div className="space-y-8">
<div className="space-y-2">
<p className="2xl:text-xl sm:text-lg text-[#ABABBA]">
Адрес
Сайт
</p>
<p className="2xl:text-3xl sm:text-2xl text-xl">
ул. Московская, 47, Екатеринбург
<a href="https://graff.tech" target="_blank">
graff.tech
</a>
</p>
</div>
<div className="space-y-2">
@@ -995,11 +931,12 @@ function App() {
</div>
<div className="h-0.5 bg-[#454554]"></div>
<div className="flex sm:flex-row flex-col flex-wrap sm:items-center gap-4 items-start justify-between text-[#C5C7CE] 2xl:text-xl">
<a href="/">
<img src="/images/logo.png" alt="" />
</a>
<a href="#privacy">Политика конфиденциальности</a>
<a href="#site">graff.tech</a>
<div className="flex items-center space-x-8">
<img src="/logo-footer.svg" alt="" />
<a href="https://graff.tech/privacypolicy" target="_blank">
Политика конфиденциальности
</a>
</div>
<p>© 2023 GRAFF interactive. Все права защищены</p>
</div>
</div>
@@ -1009,6 +946,8 @@ function App() {
</div>
<Modal />
<YMInitializer accounts={[93606080]} options={{ webvisor: true }} />
</div>
);
}
+29 -9
View File
@@ -1,22 +1,42 @@
import { useInView } from "framer-motion";
import React, { useRef } from "react";
import React, { useEffect, useRef } from "react";
interface IFeatureCard2 {
title: string;
text: string;
image: string;
src: string;
}
function FeatureCardMobile2({ title, text, image }: IFeatureCard2) {
const fetureCard = useRef(null);
function FeatureCardMobile2({ title, text, src }: IFeatureCard2) {
const fetureCard = useRef<HTMLDivElement>(null);
const videoRef = useRef<HTMLVideoElement>(null);
const inView = useInView(fetureCard, { margin: "-50%" });
return (
<div ref={fetureCard} className="relative border border-[#23232A] bg-[#2E2E381A]">
useEffect(() => {
if (videoRef.current) {
if (inView) {
videoRef.current.play();
} else {
videoRef.current.pause();
}
}
}, [inView]);
<div className="">
<img src={image} alt="" className="aspect-video w-full" />
return (
<div
ref={fetureCard}
className="relative border border-[#23232A] bg-[#2E2E381A] overflow-hidden backdrop-blur-lg"
>
<div className="relative z-10 aspect-video">
<video
ref={videoRef}
muted={true}
autoPlay={true}
playsInline={true}
className=""
>
<source src={src} type="video/mp4" />
</video>
</div>
<div className="relative px-4 py-6 sm:space-y-4 space-y-2">
<p className="font-gilroy uppercase 2xl:text-2xl sm:text-xl tracking-wider">
+58 -87
View File
@@ -1,94 +1,65 @@
import React, { useEffect, useRef, useState } from "react";
import React from "react";
import FeatureSliderItem from "./FeatureSliderItem";
import { useInView } from "framer-motion";
function FeatureSlider() {
const [selectedItem, setSelectedItem] = useState<string>("");
const featureSliderRef = useRef(null);
const featureSliderItemRef = useRef<HTMLDivElement>(null);
const featureSliderInView = useInView(featureSliderRef);
useEffect(() => {
if (featureSliderInView) {
setSelectedItem("/videos/features/virtual_tour.webm");
} else {
setSelectedItem("");
}
}, [featureSliderInView]);
useEffect(() => {
if (featureSliderItemRef.current) {
featureSliderItemRef.current.insertAdjacentHTML(
"beforeend",
`<video muted autoplay loop playsinline class="absolute fade-in">
<source src="${selectedItem}" />
</video>`
);
if (featureSliderItemRef.current.children.length > 1) {
setTimeout(() => {
featureSliderItemRef.current?.firstElementChild?.remove();
}, 1000);
}
}
}, [selectedItem]);
return (
<div ref={featureSliderRef} className="grid grid-cols-2">
<div className="aspect-video">
<FeatureSliderItem
title="Виртуальный тур по жилому комплексу"
text="Клиент лично оценит угол инсоляции, малые архитектурные формы и ландшафт, перемещаясь по комплексу с помощью тапа."
handleHover={() => setSelectedItem("/videos/features/virtual_tour.webm")}
/>
<FeatureSliderItem
title="Вся инфрастуктура на одном экране"
text="Возможность оценить инфраструктуру района покажет важные для клиента точки интереса и время, за которое он сможет до них дойти."
handleHover={() => setSelectedItem("/videos/features/nks_infra.webm")}
/>
</div>
<div
ref={featureSliderItemRef}
className="relative aspect-video border border-[#454554] bg-black"
></div>
<div className="aspect-video">
<FeatureSliderItem
title="Конфигуратор интерьера"
text="Клиент может свободно выбирать мебель и дизайн с помощью конфигуратора интерьера. Возможно выбрать стиль всей квартиры или изменить отдельные детали."
handleHover={() => setSelectedItem("/videos/features/uralsky.webm")}
/>
<FeatureSliderItem
title="Параметрический поиск квартир"
text="Фильтр позволит отметить конкретные преимущества, определить количество комнат, желаемый этаж, цену, и получить выборку подходящих вариантов."
handleHover={() => setSelectedItem("/videos/features/parametric.webm")}
/>
</div>
<div className="aspect-video">
<FeatureSliderItem
title="Любой рендер за несколько секунд"
text="Когда для рекламы вам понадобится любой объект с любого ракурса, просто сделайте фотографию внутри презентации."
handleHover={() => setSelectedItem("/videos/features/render.webm")}
/>
<FeatureSliderItem
title="Формирование вишлиста"
text="Клиент может добавить варианты квартир в избранное, сравнить их между собой по основным параметрам и выбрать свою будущую квартиру."
handleHover={() => setSelectedItem("/videos/features/wish.webm")}
/>
</div>
<div className="aspect-video">
<FeatureSliderItem
title="Интеграция с CRM - системой"
text="Приложение передает информацию о клиенте в CRM-систему застройщика и получает актуальную информацию по ценам и статусам квартир."
handleHover={() => setSelectedItem("/videos/features/integra_crm.webm")}
/>
</div>
<div className="aspect-video">
<FeatureSliderItem
title="Отправка коммерческого предложения"
text="Коммерческое предложение с выбранными квартирами может быть отправлено клиенту на почту или распечатано и отдано лично в руки."
handleHover={() => setSelectedItem("/videos/features/send.webm")}
/>
</div>
<div className="lg:grid grid-cols-2 hidden">
<FeatureSliderItem
title={"Виртуальный тур по жилому комплексу"}
text={
"Клиент лично оценит угол инсоляции, малые архитектурные формы и ландшафт, перемещаясь по комплексу с помощью тапа."
}
video={"/videos/features/virtual_tour.mp4"}
/>
<FeatureSliderItem
title={"Вся инфрастуктура на одном экране"}
text={
"Возможность оценить инфраструктуру района покажет важные для клиента точки интереса и время, за которое он сможет до них дойти."
}
video={"/videos/features/nks_infra.mp4"}
/>
<FeatureSliderItem
title={"Конфигуратор интерьера"}
text={
"Клиент может свободно выбирать мебель и дизайн с помощью конфигуратора интерьера. Возможно выбрать стиль всей квартиры или изменить отдельные детали."
}
video={"/videos/features/uralsky.mp4"}
/>
<FeatureSliderItem
title={"Параметрический поиск квартир"}
text={
"Фильтр позволит отметить конкретные преимущества, определить количество комнат, желаемый этаж, цену, и получить выборку подходящих вариантов."
}
video={"/videos/features/parametric.mp4"}
/>
<FeatureSliderItem
title={"Любой рендер за несколько секунд"}
text={
"Когда для рекламы вам понадобится любой объект с любого ракурса, просто сделайте фотографию внутри презентации."
}
video={"/videos/features/render.mp4"}
/>
<FeatureSliderItem
title={"Формирование вишлиста"}
text={
"Клиент может добавить варианты квартир в избранное, сравнить их между собой по основным параметрам и выбрать свою будущую квартиру."
}
video={"/videos/features/wish.mp4"}
/>
<FeatureSliderItem
title={"Интеграция с CRM - системой"}
text={
"Приложение передает информацию о клиенте в CRM-систему застройщика и получает актуальную информацию по ценам и статусам квартир."
}
video={"/videos/features/integra_crm.mp4"}
/>
<FeatureSliderItem
title={"Отправка коммерческого предложения"}
text={
"Коммерческое предложение с выбранными квартирами может быть отправлено клиенту на почту или распечатано и отдано лично в руки."
}
video={"/videos/features/send.mp4"}
/>
</div>
);
}
+54 -23
View File
@@ -1,37 +1,68 @@
import React from "react";
import React, { useEffect, useRef, useState } from "react";
interface IFeatureSliderItem {
title: string;
text: string;
handleHover: () => void;
video: string;
}
function FeatureSliderItem({ title, text, handleHover }: IFeatureSliderItem) {
function FeatureSliderItem({ title, text, video }: IFeatureSliderItem) {
const videoRef = useRef<HTMLVideoElement>(null);
const [isTouchScreen, setIsTouchScreen] = useState<boolean>(false);
function handleMouseEnter() {
videoRef.current?.play();
}
function handleMouseLeave() {
videoRef.current?.pause();
}
useEffect(() => {
if (navigator.maxTouchPoints > 1) {
setIsTouchScreen(true);
}
}, []);
return (
<div
className="relative group border border-[#454554] h-1/2 bg-[#2E2E381A] hover:bg-[#2E2E3880] transition-colors duration-300 overflow-hidden backdrop-blur-lg"
onMouseEnter={handleHover}
className="relative group aspect-video border border-[#454554] backdrop-blur-lg overflow-hidden"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<div className="absolute top-0 xl:p-6 p-3.5 group-hover:opacity-0 transition-all duration-300">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2.60278 21.9944L22 2M22 2L21.8896 22M22 2H2"
stroke="#D9D9D9"
strokeWidth="2"
/>
</svg>
<video
ref={videoRef}
className="absolute top-0 left-0 w-full"
muted
loop
playsInline
>
<source src={video} />
</video>
<div className="absolute top-0 left-0 w-full h-full bg-gradient-to-b from-[#13131700] to-[#131317] flex justify-center items-center">
{isTouchScreen && (
<svg
className="group-hover:scale-125 transition-all group-hover:opacity-0"
width="64"
height="64"
viewBox="0 0 64 64"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M54.285 30.971C55.0618 31.4371 55.0618 32.5629 54.285 33.029L17.8174 54.9096C17.0176 55.3895 16 54.8133 16 53.8806L16 10.1194C16 9.18667 17.0176 8.61054 17.8174 9.09044L54.285 30.971Z"
fill="#F2F2F2"
/>
</svg>
)}
</div>
<div className="absolute top-[64%] xl:p-6 p-3.5 space-y-4 group-hover:top-0 transition-all duration-300">
<div className="font-gilroy font-medium uppercase 2xl:text-2xl xl:text-xl tracking-wider">
<div className="p-6 absolute 2xl:top-[83%] 2xl:group-hover:top-[59%] xl:top-[82%] xl:group-hover:top-[53%] top-[78%] group-hover:top-[45%] transition-all duration-300 space-y-4">
<p className="font-gilroy font-medium uppercase 2xl:text-2xl xl:text-xl tracking-wider">
{title}
</div>
<div className="2xl:text-xl xl:text-lg">{text}</div>
</p>
<p className="2xl:text-xl xl:text-lg">{text}</p>
</div>
</div>
);
@@ -0,0 +1,40 @@
import React from "react";
interface IFeatureSliderItem {
title: string;
text: string;
handleHover: () => void;
}
function FeatureSliderItem({ title, text, handleHover }: IFeatureSliderItem) {
return (
<div
className="relative group border border-[#454554] h-1/2 bg-[#2E2E381A] hover:bg-[#2E2E3880] transition-colors duration-300 overflow-hidden backdrop-blur-lg"
onMouseEnter={handleHover}
>
<div className="absolute top-0 xl:p-6 p-3.5 group-hover:opacity-0 transition-all duration-300">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2.60278 21.9944L22 2M22 2L21.8896 22M22 2H2"
stroke="#D9D9D9"
strokeWidth="2"
/>
</svg>
</div>
<div className="absolute top-[64%] xl:p-6 p-3.5 space-y-4 group-hover:top-0 transition-all duration-300">
<div className="font-gilroy font-medium uppercase 2xl:text-2xl xl:text-xl tracking-wider">
{title}
</div>
<div className="2xl:text-xl xl:text-lg">{text}</div>
</div>
</div>
);
}
export default FeatureSliderItem;
@@ -0,0 +1,96 @@
import React, { useEffect, useRef, useState } from "react";
import FeatureSliderItem from "./FeatureSliderItemOld";
import { useInView } from "framer-motion";
function FeatureSlider() {
const [selectedItem, setSelectedItem] = useState<string>("");
const featureSliderRef = useRef(null);
const featureSliderItemRef = useRef<HTMLDivElement>(null);
const featureSliderInView = useInView(featureSliderRef);
useEffect(() => {
if (featureSliderInView) {
setSelectedItem("/videos/features/virtual_tour.mp4");
} else {
setSelectedItem("");
}
}, [featureSliderInView]);
useEffect(() => {
if (featureSliderItemRef.current) {
featureSliderItemRef.current.insertAdjacentHTML(
"beforeend",
`<video muted autoplay loop playsinline class="absolute fade-in">
<source src="${selectedItem}" type="video/mp4" />
</video>`
);
if (featureSliderItemRef.current.children.length > 1) {
setTimeout(() => {
featureSliderItemRef.current?.firstElementChild?.remove();
}, 1000);
}
}
}, [selectedItem]);
return (
<div ref={featureSliderRef} className="lg:grid grid-cols-2 hidden">
<div className="aspect-video">
<FeatureSliderItem
title="Виртуальный тур по жилому комплексу"
text="Клиент лично оценит угол инсоляции, малые архитектурные формы и ландшафт, перемещаясь по комплексу с помощью тапа."
handleHover={() => setSelectedItem("/videos/features/virtual_tour.mp4")}
/>
<FeatureSliderItem
title="Вся инфрастуктура на одном экране"
text="Возможность оценить инфраструктуру района покажет важные для клиента точки интереса и время, за которое он сможет до них дойти."
handleHover={() => setSelectedItem("/videos/features/nks_infra.mp4")}
/>
</div>
<div
ref={featureSliderItemRef}
className="relative aspect-video border border-[#454554] bg-black"
></div>
<div className="aspect-video">
<FeatureSliderItem
title="Конфигуратор интерьера"
text="Клиент может свободно выбирать мебель и дизайн с помощью конфигуратора интерьера. Возможно выбрать стиль всей квартиры или изменить отдельные детали."
handleHover={() => setSelectedItem("/videos/features/uralsky.mp4")}
/>
<FeatureSliderItem
title="Параметрический поиск квартир"
text="Фильтр позволит отметить конкретные преимущества, определить количество комнат, желаемый этаж, цену, и получить выборку подходящих вариантов."
handleHover={() => setSelectedItem("/videos/features/parametric.mp4")}
/>
</div>
<div className="aspect-video">
<FeatureSliderItem
title="Любой рендер за несколько секунд"
text="Когда для рекламы вам понадобится любой объект с любого ракурса, просто сделайте фотографию внутри презентации."
handleHover={() => setSelectedItem("/videos/features/render.mp4")}
/>
<FeatureSliderItem
title="Формирование вишлиста"
text="Клиент может добавить варианты квартир в избранное, сравнить их между собой по основным параметрам и выбрать свою будущую квартиру."
handleHover={() => setSelectedItem("/videos/features/wish.mp4")}
/>
</div>
<div className="aspect-video">
<FeatureSliderItem
title="Интеграция с CRM - системой"
text="Приложение передает информацию о клиенте в CRM-систему застройщика и получает актуальную информацию по ценам и статусам квартир."
handleHover={() => setSelectedItem("/videos/features/integra_crm.mp4")}
/>
</div>
<div className="aspect-video">
<FeatureSliderItem
title="Отправка коммерческого предложения"
text="Коммерческое предложение с выбранными квартирами может быть отправлено клиенту на почту или распечатано и отдано лично в руки."
handleHover={() => setSelectedItem("/videos/features/send.mp4")}
/>
</div>
</div>
);
}
export default FeatureSlider;
+168
View File
@@ -0,0 +1,168 @@
import { FormEvent, useState } from "react";
import api from "../utils/api";
import InputMask from "react-input-mask";
import useModalStore from "../store/modal";
import FeedbackFormSuccess from "./FeedbackFormSuccess";
function FeedbackForm() {
const [modalComponent, setModalComponent] = useModalStore((state) => [
state.component,
state.setComponent,
]);
const [fullname, setFullname] = useState<string>("");
const [email, setEmail] = useState<string>("");
const [company, setCompany] = useState<string>("");
const [phone, setPhone] = useState<string>("");
async function handleSubmitSendMail(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
await api.post("mail", {
json: {
fullname,
email,
company,
phone,
},
});
setFullname("");
setEmail("");
setCompany("");
setPhone("");
setModalComponent(<FeedbackFormSuccess />);
}
return (
<div className="fixed top-0 left-0 w-full h-full overflow-y-auto">
<div className="lg:p-8 sm:p-6 p-4 w-full min-h-full relative flex flex-col justify-center items-center">
<div className="space-y-8 bg-[#23232A] 2xl:p-16 xl:p-12 lg:p-8 p-4 rounded-xl 2xl:w-[1472px] xl:w-[1248px] lg:w-[968px]">
<p className="font-gilroy 2xl:text-7xl xl:text-6xl sm:text-5xl text-4xl text-gradient w-fit">
Свяжитесь с нами
</p>
<form
className="grid lg:grid-cols-2 2xl:gap-8 xl:gap-6 gap-4"
onSubmit={handleSubmitSendMail}
>
<div className="2xl:space-y-8 xl:space-y-6 space-y-4">
<div className="space-y-2">
<label
htmlFor=""
className="text-[#D9D9D9] block 2xl:text-xl sm:text-lg text-sm"
>
Имя Фамилия*
</label>
<input
required
type="text"
className="lg:text-xl lg:py-4 px-3 py-2 bg-[#454554] outline-none border border-transparent rounded-lg focus:border-[#BC75FF] transition-colors w-full"
value={fullname}
onChange={(e) => setFullname(e.target.value)}
/>
</div>
<div className="space-y-2">
<label
htmlFor=""
className="text-[#D9D9D9] block 2xl:text-xl sm:text-lg text-sm"
>
Компания
</label>
<input
type="text"
className="lg:text-xl lg:py-4 px-3 py-2 bg-[#454554] outline-none border border-transparent rounded-lg focus:border-[#BC75FF] transition-colors w-full"
value={company}
onChange={(e) => setCompany(e.target.value)}
/>
</div>
</div>
<div className="2xl:space-y-8 xl:space-y-6 space-y-4">
<div className="space-y-2">
<label
htmlFor=""
className="text-[#D9D9D9] block 2xl:text-xl sm:text-lg text-sm"
>
Email*
</label>
<input
required
type="email"
className="lg:text-xl lg:py-4 px-3 py-2 bg-[#454554] outline-none border border-transparent rounded-lg focus:border-[#BC75FF] transition-colors w-full"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="space-y-2">
<label
htmlFor=""
className="text-[#D9D9D9] block 2xl:text-xl sm:text-lg text-sm"
>
Номер телефона
</label>
<InputMask
maskChar={null}
mask={"+7 (999) 999-99-99"}
type="text"
className="lg:text-xl lg:py-4 px-3 py-2 bg-[#454554] outline-none border border-transparent rounded-lg focus:border-[#BC75FF] transition-colors w-full"
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
</div>
</div>
<div className="space-y-8">
<div className="space-x-4">
<div className="2xl:text-xl sm:text-base text-sm flex items-center space-x-4">
<div>
<input
required
type="checkbox"
defaultChecked={true}
className="absolute opacity-0 h-10 w-10 cursor-pointer"
/>
<div className="bg-[#454554] rounded-lg h-10 w-10 p-1">
<svg
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="hidden"
>
<path
d="M6.6665 14.6667L13.3332 21.3334L25.3332 10.6667"
stroke="#F2F2F2"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</div>
</div>
<div>
<span className="text-[#ABABBA] text-opacity-50">
Я согласен с
</span>{" "}
<a href="#" className="text-white">
политикой конфиденциальности
</a>
</div>
</div>
</div>
<button
type="submit"
className="2xl:text-xl w-full lg:py-4 px-8 py-3 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] rounded-full opacity-95 hover:opacity-100 transition-opacity"
>
Отправить заявку
</button>
</div>
</form>
</div>
</div>
</div>
);
}
export default FeedbackForm;
@@ -0,0 +1,55 @@
import { FormEvent, useState } from "react";
import api from "../utils/api";
import InputMask from "react-input-mask";
import useModalStore from "../store/modal";
function FeedbackForm() {
const [fullname, setFullname] = useState<string>("");
const [email, setEmail] = useState<string>("");
const [company, setCompany] = useState<string>("");
const [phone, setPhone] = useState<string>("");
const [modalComponent, setModalComponent] = useModalStore((state) => [
state.component,
state.setComponent,
]);
return (
<div className="fixed top-0 left-0 w-full h-full overflow-y-auto">
<div className="lg:p-8 sm:p-6 p-4 w-full min-h-full relative flex flex-col justify-center items-center">
<div className="space-y-8 bg-[#23232A] 2xl:p-24 xl:p-20 lg:p-16 sm:p-14 p-8 rounded-xl 2xl:w-[1472px] xl:w-[1248px] lg:w-[968px]">
<div className="2xl:space-y-44 xl:space-y-40 lg:space-y-36 sm:space-y-36 space-y-8">
<div className="2xl:space-y-14 space-y-10 xl:w-2/3 lg:w-3/4">
<p className="font-gilroy 2xl:text-7xl xl:text-6xl sm:text-5xl text-4xl text-gradient w-fit">
Заявка отправлена
</p>
<div className="2xl:space-y-8 space-y-6 xl:space-y-6">
<p className="font-gilroy 2xl:text-4xl sm:text-3xl text-2xl">
Спасибо за подачу заявки!
</p>
<p className="2xl:text-2xl sm:text-xl text-lg">
Мы ценим ваш интерес к нашей компании и уже начинаем работу по
заявке.
</p>
<p className="2xl:text-2xl sm:text-xl text-lg">
Мы свяжемся с вами в ближайшее время для уточнения деталей и
ответим на все ваши вопросы.
</p>
</div>
</div>
<button
onClick={() => setModalComponent(null)}
className="2xl:text-3xl sm:text-2xl text-lg px-8 py-4 bg-gradient-to-tr from-[#BC75FF] to-[#798FFF] text-white rounded-full opacity-95 hover:opacity-100 transition-opacity lg:w-1/2 w-full"
>
Продолжить
</button>
</div>
</div>
</div>
</div>
);
}
export default FeedbackForm;
+2 -2
View File
@@ -915,8 +915,8 @@ function Map2() {
y2="982.338"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#BC75FF" />
<stop offset="1" stop-color="#798FFF" />
<stop stopColor="#BC75FF" />
<stop offset="1" stopColor="#798FFF" />
</linearGradient>
</defs>
</svg>
+6 -52
View File
@@ -32,64 +32,18 @@ function Slider() {
className="h-full"
>
<SwiperSlide>
<Slide image={"/images/cards/1.jpg"} />
<Slide image={"/images/slider/1.jpg"} />
</SwiperSlide>
<SwiperSlide>
<Slide image={"/images/cards/2.jpg"} />
<Slide image={"/images/slider/2.jpg"} />
</SwiperSlide>
<SwiperSlide>
<Slide image={"/images/cards/3.jpg"} />
<Slide image={"/images/slider/3.jpg"} />
</SwiperSlide>
<SwiperSlide>
<Slide image={"/images/slider/4.jpg"} />
</SwiperSlide>
</Swiper>
{/* <div className="flex justify-between">
<div className="flex items-center space-x-1 text-[#C5C7CE]">
<span>{realIndex + 1}</span>
<div className="h-[1px] w-12 bg-[#C5C7CE]"></div>
<span>8</span>
</div>
<div className="space-x-2">
<button
ref={prevRef}
className="p-2 bg-[#23242A] enabled:hover:bg-[#2E3038] transition-colors rounded-full"
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 12L18 12M6 12L12.3636 6M6 12L12.3636 18"
stroke="#F2F2F2"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
<button
ref={nextRef}
className="p-2 bg-[#23242A] enabled:hover:bg-[#2E3038] transition-colors rounded-full"
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M18 12L6 12M18 12L11.6364 18M18 12L11.6364 6"
stroke="#F2F2F2"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
</div>
</div> */}
</div>
);
}
+2 -2
View File
@@ -118,8 +118,8 @@ input:checked + div svg {
}
.swiper-pagination-bullet {
width: 16px;
height: 16px;
width: 12px;
height: 12px;
}
.swiper-pagination-bullet-active {
+5
View File
@@ -1163,6 +1163,11 @@ react-use-measure@^2.1.1:
dependencies:
debounce "^1.2.1"
react-yandex-metrika@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/react-yandex-metrika/-/react-yandex-metrika-2.6.0.tgz#9c935c8c7ea5505e34391b9b3e86deb6d50053c9"
integrity sha512-8K4wExsNZtY3DTxh1G8a+zWH9Pg8fw23MJcoJ4I/562qrHRnh7L5nteq3lnNL58dnNQbuuHIRoGgMjIo+r1GjA==
react@^18.2.0:
version "18.2.0"
resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
+11 -3
View File
@@ -85,9 +85,17 @@ app.post("/mail", async (req, res) => {
let info = await transporter.sendMail({
from: `${email}`, // sender address
to: "info@graff.tech", // list of receivers
subject: fullname, // Subject line
text: `${company} ${phone}`, // plain text body
html: `${company} ${phone}`, // html body
subject: 'Заявка с сайта estate.graff.tech', // Subject line
text: `
Имя Фамилия: ${fullname}
Телефон: ${phone}
Компания: ${company}
`, // plain text body
html: `<div>
<p>Имя Фамилия: ${fullname}</p>
<p>Телефон: ${phone}</p>
<p>Компания: ${company}</p>
</div>`, // html body
});
console.log(info);