refactoring

This commit is contained in:
2024-04-03 12:20:03 +05:00
parent 2c0edf6f6a
commit 4300cdfe91
12 changed files with 200 additions and 213 deletions
+5 -77
View File
@@ -4,102 +4,31 @@
import "react-rangeslider/lib/index.css";
import "../components/RangeSlider.css";
import api from "@utils/api";
import { useEffect, useState, useRef, Fragment } from "react";
import { useState } from "react";
import FeatureItem from "@components/FeatureItem";
import StreamButton from "@components/StreamButton";
import ProjectCard from "@components/ProjectCard";
import ExampleCard from "@components/ExampleCard";
import FeatureVideoViewBox from "@components/FeatureVideoViewBox";
import Button from "@components/Button";
import Calc from "@components/Calc";
import Heading2 from "@components/Headings/Heading2";
import VideoSliderMobile from "@components/VideoSliderMobile";
// import { isMobile } from "react-device-detect";
import FeedbackForm from "@components/FeedbackForm";
import useModalStore from "@stores/useModalStore";
import FeedbackModal from "@components/modals/FeedbackModal";
import ModalContainer from "@components/ModalContainer";
import MoreProjectButton from "@components/MoreProjectButton";
import MailIcon from "@components/icons/MailIcon";
import PhoneIcon from "@components/icons/PhoneIcon";
import VKIcon from "@components/icons/VKIcon";
import YouTubeIcon from "@components/icons/YouTubeIcon";
import TelegramIcon from "@components/icons/TelegramIcon";
import { IProject } from "../types/IProject";
import { SortedProject } from "../types/SortedProject";
import Winners from "@components/Winners";
import { getTime, getDate, getYear } from "date-fns";
import ProjectsSection from "@components/ProjectsSection";
import { motion } from "framer-motion";
import { Video } from "../types/Video";
import Reviews from "@components/Reviews";
import Histories from "@components/Histories";
import Projects from "../Projects/Projects";
import Clients from "@components/Clients";
const VIDEOS_FEATURES: Video[] = [
{
id: "1",
value: "/videos/features/virtual_tour.mp4",
poster: "/images/posters/virtual_tour.jpg",
title: "Виртуальный тур по жилому комплексу",
desc: "Клиент лично оценит угол инсоляции, малые архитектурные формы и ландшафт, перемещаясь по комплексу с помощью тапа.",
},
{
id: "2",
value: "/videos/features/nks_infra.mp4",
poster: "/images/posters/nks_infra.jpg",
title: "Вся инфрастуктура на одном экране",
desc: "Возможность оценить инфраструктуру района покажет важные для клиента точки интереса и время, за которое он сможет до них дойти.",
},
{
id: "3",
value: "/videos/features/uralsky.mp4",
poster: "/images/posters/uralsky.jpg",
title: "Конфигуратор интерьера",
desc: "Клиент может свободно выбирать мебель и дизайн с помощью конфигуратора интерьера. Возможно выбрать стиль всей квартиры или изменить отдельные детали.",
},
{
id: "4",
value: "/videos/features/parametric.mp4",
poster: "/images/posters/parametric.jpg",
title: "Параметрический поиск квартир",
desc: "Фильтр позволит отметить конкретные преимущества, определить количество комнат, желаемый этаж, цену, и получить выборку подходящих вариантов.",
},
{
id: "5",
value: "/videos/features/render.mp4",
poster: "/images/posters/render.jpg",
title: "Любой рендер за несколько секунд",
desc: "Когда для рекламы вам понадобится любой объект с любого ракурса, просто сделайте фотографию внутри презентации.",
},
{
id: "6",
value: "/videos/features/wish.mp4",
poster: "/images/posters/wish.jpg",
title: "Формирование вишлиста",
desc: "Клиент может добавить варианты квартир в избранное, сравнить их между собой по основным параметрам и выбрать свою будущую квартиру.",
},
{
id: "7",
value: "/videos/features/integra_crm.mp4",
poster: "/images/posters/integra_crm.jpg",
title: "Интеграция с CRM-системой",
desc: "Приложение передает информацию о клиенте в CRM-систему застройщика и получает актуальную информацию по ценам и статусам квартир.",
},
{
id: "8",
value: "/videos/features/send.mp4",
poster: "/images/posters/send.jpg",
title: "Отправка коммерческого предложения",
desc: "Коммерческое предложение с выбранными квартирами может быть отправлено клиенту на почту или распечатано и отдано лично в руки.",
},
];
import { videosFeatures } from "../consts/videoFeatures";
export default function App() {
const [selectedVideo, setSelectedVideo] = useState<string>(
@@ -368,7 +297,7 @@ export default function App() {
</div>
<div className="pr-6 xl:block hidden">
{VIDEOS_FEATURES.map((video) => (
{videosFeatures.map((video) => (
<FeatureItem
key={video.id}
title={video.title}
@@ -382,11 +311,11 @@ export default function App() {
<FeatureVideoViewBox
isViewportEntered={isViewportEntered}
selectedVideo={selectedVideo}
videos={VIDEOS_FEATURES}
videos={videosFeatures}
/>
<VideoSliderMobile
isViewportEntered={isViewportEntered}
videos={VIDEOS_FEATURES}
videos={videosFeatures}
/>
</motion.div>
<Histories />
@@ -614,7 +543,6 @@ export default function App() {
<Winners />
<Reviews />
<Projects />
<Clients />
<div className="relative overflow-hidden pb-[50px]">
-1
View File
@@ -1,7 +1,6 @@
"use client";
import { useEffect, useState } from "react";
import Head from "next/head";
import api from "@utils/api";
import { IProject } from "../../types/IProject";
import ProjectCard from "@components/ProjectCard";
+1 -39
View File
@@ -1,44 +1,6 @@
import Image from "next/image";
import Heading2 from "./Headings/Heading2";
interface IClient {
id: string;
src: string;
}
const clients: IClient[] = [
{ id: "1", src: "/images/clients/1.png" },
{ id: "2", src: "/images/clients/2.png" },
{ id: "3", src: "/images/clients/3.png" },
{ id: "4", src: "/images/clients/4.png" },
{ id: "5", src: "/images/clients/5.png" },
{ id: "6", src: "/images/clients/6.png" },
{ id: "7", src: "/images/clients/7.png" },
{ id: "8", src: "/images/clients/8.png" },
{ id: "9", src: "/images/clients/9.png" },
{ id: "10", src: "/images/clients/10.png" },
{ id: "11", src: "/images/clients/11.png" },
{ id: "12", src: "/images/clients/12.png" },
{ id: "13", src: "/images/clients/13.png" },
{ id: "31", src: "/images/clients/14.png" },
{ id: "14", src: "/images/clients/15.png" },
{ id: "15", src: "/images/clients/16.png" },
{ id: "16", src: "/images/clients/17.png" },
{ id: "17", src: "/images/clients/18.png" },
{ id: "18", src: "/images/clients/19.png" },
{ id: "19", src: "/images/clients/20.png" },
{ id: "20", src: "/images/clients/21.png" },
{ id: "21", src: "/images/clients/22.png" },
{ id: "22", src: "/images/clients/23.png" },
{ id: "23", src: "/images/clients/24.png" },
{ id: "24", src: "/images/clients/25.png" },
{ id: "25", src: "/images/clients/26.png" },
{ id: "26", src: "/images/clients/27.png" },
{ id: "27", src: "/images/clients/28.png" },
{ id: "28", src: "/images/clients/29.png" },
{ id: "29", src: "/images/clients/30.png" },
{ id: "30", src: "/images/clients/31.png" },
];
import { clients } from "../consts/clients";
const Clients = () => {
return (
+9 -34
View File
@@ -2,38 +2,13 @@ import { createRef, useEffect, useState } from "react";
import { useInView } from "react-intersection-observer";
import { useSwipeable } from "react-swipeable";
import { Transition } from "react-transition-group";
import { Video } from "../types/Video";
import ArrowLeftIcon from "./icons/ArrowLeftIcon";
import ArrowRightIcon from "./icons/ArrowRightIcon";
const VIDEOS: Video[] = [
{
id: "1",
value: "/videos/histories/1.mp4",
title: "Интерактивный комплекс GRAFF.estate для ЖК Upside Towers, Москва",
desc: "",
poster: "/images/posters/histories/1.jpg",
},
{
id: "2",
value: "/videos/histories/2.mp4",
title: "Graff.estate на выставке 100+ TechnoBuild",
desc: "",
poster: "/images/posters/histories/2.jpg",
},
{
id: "3",
value: "/videos/histories/3.mp4",
title:
"Интерактивная инсталляция graff.estate для ЖК DNS City в г. Владивосток",
desc: "",
poster: "/images/posters/histories/3.jpg",
},
];
import { videos } from "../consts/videos";
function Histories() {
const [selectedVideoIndex, setSelectedVideoIndex] = useState(0);
const videoRefs = VIDEOS.map(() => createRef<HTMLVideoElement>());
const videoRefs = videos.map(() => createRef<HTMLVideoElement>());
const [videoWidth, setVideoWidth] = useState<number>(0);
const [videoHeigth, setVideoHeigth] = useState<number>(0);
const [videoProgesses, setVideoProgresses] = useState<number[]>([]);
@@ -54,7 +29,7 @@ function Histories() {
}, [videoRefs[0]]);
function handleEnded() {
if (selectedVideoIndex === VIDEOS.length - 1) {
if (selectedVideoIndex === videos.length - 1) {
setSelectedVideoIndex(0);
} else {
setSelectedVideoIndex((prev) => prev + 1);
@@ -67,12 +42,12 @@ function Histories() {
}
function next() {
if (selectedVideoIndex === VIDEOS.length - 1) return;
if (selectedVideoIndex === videos.length - 1) return;
setSelectedVideoIndex((prev) => prev + 1);
}
useEffect(() => {
const progresses = VIDEOS.map(() => 0);
const progresses = videos.map(() => 0);
setVideoProgresses(progresses);
}, []);
@@ -123,7 +98,7 @@ function Histories() {
<p>graff.estate</p>
</h2>
<div className="relative bg-white sm:block hidden">
{VIDEOS.map((item, index) => (
{videos.map((item, index) => (
<Transition
key={index}
in={Boolean(index === selectedVideoIndex)}
@@ -170,7 +145,7 @@ function Histories() {
clipPath: `rect(auto auto auto 0)`,
}}
>
{VIDEOS.map((video, index) => (
{videos.map((video, index) => (
<div
key={video.id}
className="relative aspect-[9/16] bg-black transition-transform duration-300 sm:h-auto sm:min-w-0 h-[546px] min-w-[100vw]"
@@ -215,7 +190,7 @@ function Histories() {
))}
</div>
<div className="relative block sm:hidden h-[100%] bg-[#212431] p-6">
{VIDEOS.map((item, index) => (
{videos.map((item, index) => (
<Transition
key={index}
in={Boolean(index === selectedVideoIndex)}
@@ -247,7 +222,7 @@ function Histories() {
style={{ width: `${videoProgesses[selectedVideoIndex]}%` }}
></div>
</div>
{VIDEOS.map((video, index) => (
{videos.map((video, index) => (
<div
key={video.id}
className="h-full w-2 rounded-full transition-all duration-300"
-1
View File
@@ -6,7 +6,6 @@ function ModalContainer() {
if (modal) {
return (
<div
// onClick={() => setModal(null)}
className={`fixed p-8 top-0 left-0 z-10 w-full h-full flex justify-center items-center bg-black bg-opacity-80 overflow-auto transition-opacity`}
>
<div onClick={(e) => e.stopPropagation()} className="cursor-default">
+12 -61
View File
@@ -1,64 +1,15 @@
/* eslint-disable @next/next/no-img-element */
// import { createRef, useEffect, useLayoutEffect, useRef, useState } from "react";
// import { useSwipeable } from "react-swipeable";
// import { motion } from "framer-motion";
// import { Transition } from "react-transition-group";
// import ArrowLeftIcon from "./icons/ArrowLeftIcon";
// import ArrowRightIcon from "./icons/ArrowRightIcon";
// import { Video } from "../types/Video";
import { createRef, useEffect, useLayoutEffect, useState } from "react";
import Image from "next/image";
import { createRef, useEffect, useState } from "react";
import { useInView } from "react-intersection-observer";
import { useSwipeable } from "react-swipeable";
import { Video } from "../types/Video";
import ArrowLeftIcon from "./icons/ArrowLeftIcon";
import ArrowRightIcon from "./icons/ArrowRightIcon";
import { Transition } from "react-transition-group";
import PlayerIcon from "./icons/PlayerIcon";
import Image from "next/image";
type Review = {
id: string;
avatar: string;
name: string;
position: string;
desc: string;
video: string;
poster: string;
};
const REVIEWS: Review[] = [
{
id: "1",
avatar: "/images/reviews/avatars/1.png",
name: "Егор бобров",
position: "Коммерческий директор авторского квартала «Машаров»",
desc: "Эффективность инструмента была подтверждена буквально в первый день после его внедрения. Например, один из клиентов, посетив офис и увидев свою будущую квартиру с помощью интерактивной панели, сразу решил купить недвижимость в этом проекте, отказавшись от других вариантов",
poster: "/images/reviews/1.png",
video: "",
},
{
id: "2",
avatar: "/images/reviews/avatars/2.png",
name: "Олег Бондорев",
position: "Ведущий менеджер компании «ЭНКО»",
desc: "Клиенты особенно ценят возможность легко выбрать квартиру с помощью 3D-модель жилого комплекса. Так же инструмент продаж позволяет клиенту увидеть расположение дома, в какое время свет будет попадать в окна, даже зайти в лифт и холл, а также оценить инфраструктуру района и «прочувствовать» его, прогулявшись по двору",
poster: "/images/reviews/1.png",
video: "",
},
{
id: "3",
avatar: "/images/reviews/avatars/3.png",
name: "Алина Веселова",
position: "Ведущий специалист отдела продаж",
desc: "Одним из преимуществ инструмента является возможность посмотреть 3D-модель квартиры с готовым дизайнерским ремонтом и оценить видовые характеристиками, изменяя время суток (день или ночь). Это важно для клиента, так как позволяет реалистично представить, как будет выглядеть будущее жилье.",
poster: "/images/reviews/2.png",
video: "",
},
];
import { reviews } from "../consts/reviews";
function Reviews() {
const [selectedImageIndex, setSelectedImageIndex] = useState(0);
const imageRefs = REVIEWS.map(() => createRef<HTMLImageElement>());
const imageRefs = reviews.map(() => createRef<HTMLImageElement>());
const [imageWidth, setImageWidth] = useState<number>(0);
const { ref, inView } = useInView();
const [isEntered, setIsEntered] = useState(false);
@@ -80,7 +31,7 @@ function Reviews() {
}
function next() {
if (selectedImageIndex === REVIEWS.length - 1) return;
if (selectedImageIndex === reviews.length - 1) return;
setSelectedImageIndex((prev) => prev + 1);
}
@@ -97,7 +48,7 @@ function Reviews() {
>
<div className="flex 2xl:h-[594px] xl:h-[484px] xl:border-b border-b-[#3D425C]">
<div className="mx-auto flex gap-2 min-h-2 sm:hidden mt-4">
{REVIEWS.map((video, index) => (
{reviews.map((video, index) => (
<div
key={video.id}
className="w-2 rounded-full transition-all duration-300 h-2"
@@ -125,24 +76,24 @@ function Reviews() {
<Image
width={72}
height={72}
src={REVIEWS[selectedImageIndex].avatar}
alt={REVIEWS[selectedImageIndex].name}
src={reviews[selectedImageIndex].avatar}
alt={reviews[selectedImageIndex].name}
className="rounded-full"
/>
<div>
<p className="font-semibold sm:mb-4 text-xl">
{" "}
{REVIEWS[selectedImageIndex].name}
{reviews[selectedImageIndex].name}
</p>
<p className="sm:mb-4 2xl:text-sm text-xs">
{REVIEWS[selectedImageIndex].position}
{reviews[selectedImageIndex].position}
</p>
</div>
</div>
<p
className={`font-normal 2xl:text-[16px] text-sm sm:block hidden w-1/2 xl:w-[363px]`}
>
{REVIEWS[selectedImageIndex].desc}
{reviews[selectedImageIndex].desc}
</p>
</div>
</div>
@@ -170,7 +121,7 @@ function Reviews() {
clipPath: `rect(auto auto auto 0)`,
}}
>
{REVIEWS.map((video, index) => (
{reviews.map((video, index) => (
<div
key={video.id}
className={`relative aspect-video bg-black transition-transform duration-300 h-full w-auto flex items-center justify-center`}
+35
View File
@@ -0,0 +1,35 @@
import { IClient } from "../types/Clients";
export const clients: IClient[] = [
{ id: "1", src: "/images/clients/1.png" },
{ id: "2", src: "/images/clients/2.png" },
{ id: "3", src: "/images/clients/3.png" },
{ id: "4", src: "/images/clients/4.png" },
{ id: "5", src: "/images/clients/5.png" },
{ id: "6", src: "/images/clients/6.png" },
{ id: "7", src: "/images/clients/7.png" },
{ id: "8", src: "/images/clients/8.png" },
{ id: "9", src: "/images/clients/9.png" },
{ id: "10", src: "/images/clients/10.png" },
{ id: "11", src: "/images/clients/11.png" },
{ id: "12", src: "/images/clients/12.png" },
{ id: "13", src: "/images/clients/13.png" },
{ id: "31", src: "/images/clients/14.png" },
{ id: "14", src: "/images/clients/15.png" },
{ id: "15", src: "/images/clients/16.png" },
{ id: "16", src: "/images/clients/17.png" },
{ id: "17", src: "/images/clients/18.png" },
{ id: "18", src: "/images/clients/19.png" },
{ id: "19", src: "/images/clients/20.png" },
{ id: "20", src: "/images/clients/21.png" },
{ id: "21", src: "/images/clients/22.png" },
{ id: "22", src: "/images/clients/23.png" },
{ id: "23", src: "/images/clients/24.png" },
{ id: "24", src: "/images/clients/25.png" },
{ id: "25", src: "/images/clients/26.png" },
{ id: "26", src: "/images/clients/27.png" },
{ id: "27", src: "/images/clients/28.png" },
{ id: "28", src: "/images/clients/29.png" },
{ id: "29", src: "/images/clients/30.png" },
{ id: "30", src: "/images/clients/31.png" },
];
+31
View File
@@ -0,0 +1,31 @@
import { IReview } from "../types/Review";
export const reviews: IReview[] = [
{
id: "1",
avatar: "/images/reviews/avatars/1.png",
name: "Егор бобров",
position: "Коммерческий директор авторского квартала «Машаров»",
desc: "Эффективность инструмента была подтверждена буквально в первый день после его внедрения. Например, один из клиентов, посетив офис и увидев свою будущую квартиру с помощью интерактивной панели, сразу решил купить недвижимость в этом проекте, отказавшись от других вариантов",
poster: "/images/reviews/1.png",
video: "",
},
{
id: "2",
avatar: "/images/reviews/avatars/2.png",
name: "Олег Бондорев",
position: "Ведущий менеджер компании «ЭНКО»",
desc: "Клиенты особенно ценят возможность легко выбрать квартиру с помощью 3D-модель жилого комплекса. Так же инструмент продаж позволяет клиенту увидеть расположение дома, в какое время свет будет попадать в окна, даже зайти в лифт и холл, а также оценить инфраструктуру района и «прочувствовать» его, прогулявшись по двору",
poster: "/images/reviews/1.png",
video: "",
},
{
id: "3",
avatar: "/images/reviews/avatars/3.png",
name: "Алина Веселова",
position: "Ведущий специалист отдела продаж",
desc: "Одним из преимуществ инструмента является возможность посмотреть 3D-модель квартиры с готовым дизайнерским ремонтом и оценить видовые характеристиками, изменяя время суток (день или ночь). Это важно для клиента, так как позволяет реалистично представить, как будет выглядеть будущее жилье.",
poster: "/images/reviews/2.png",
video: "",
},
];
+64
View File
@@ -0,0 +1,64 @@
import { Video } from "../types/Video";
export const videosFeatures: Video[] = [
{
id: "1",
value: "/videos/features/virtual_tour.mp4",
poster: "/images/posters/virtual_tour.jpg",
title: "Виртуальный тур по жилому комплексу",
desc: "Клиент лично оценит угол инсоляции, малые архитектурные формы и ландшафт, перемещаясь по комплексу с помощью тапа.",
},
{
id: "2",
value: "/videos/features/nks_infra.mp4",
poster: "/images/posters/nks_infra.jpg",
title: "Вся инфрастуктура на одном экране",
desc: "Возможность оценить инфраструктуру района покажет важные для клиента точки интереса и время, за которое он сможет до них дойти.",
},
{
id: "3",
value: "/videos/features/uralsky.mp4",
poster: "/images/posters/uralsky.jpg",
title: "Конфигуратор интерьера",
desc: "Клиент может свободно выбирать мебель и дизайн с помощью конфигуратора интерьера. Возможно выбрать стиль всей квартиры или изменить отдельные детали.",
},
{
id: "4",
value: "/videos/features/parametric.mp4",
poster: "/images/posters/parametric.jpg",
title: "Параметрический поиск квартир",
desc: "Фильтр позволит отметить конкретные преимущества, определить количество комнат, желаемый этаж, цену, и получить выборку подходящих вариантов.",
},
{
id: "5",
value: "/videos/features/render.mp4",
poster: "/images/posters/render.jpg",
title: "Любой рендер за несколько секунд",
desc: "Когда для рекламы вам понадобится любой объект с любого ракурса, просто сделайте фотографию внутри презентации.",
},
{
id: "6",
value: "/videos/features/wish.mp4",
poster: "/images/posters/wish.jpg",
title: "Формирование вишлиста",
desc: "Клиент может добавить варианты квартир в избранное, сравнить их между собой по основным параметрам и выбрать свою будущую квартиру.",
},
{
id: "7",
value: "/videos/features/integra_crm.mp4",
poster: "/images/posters/integra_crm.jpg",
title: "Интеграция с CRM-системой",
desc: "Приложение передает информацию о клиенте в CRM-систему застройщика и получает актуальную информацию по ценам и статусам квартир.",
},
{
id: "8",
value: "/videos/features/send.mp4",
poster: "/images/posters/send.jpg",
title: "Отправка коммерческого предложения",
desc: "Коммерческое предложение с выбранными квартирами может быть отправлено клиенту на почту или распечатано и отдано лично в руки.",
},
];
+26
View File
@@ -0,0 +1,26 @@
import { Video } from "../types/Video";
export const videos: Video[] = [
{
id: "1",
value: "/videos/histories/1.mp4",
title: "Интерактивный комплекс GRAFF.estate для ЖК Upside Towers, Москва",
desc: "",
poster: "/images/posters/histories/1.jpg",
},
{
id: "2",
value: "/videos/histories/2.mp4",
title: "Graff.estate на выставке 100+ TechnoBuild",
desc: "",
poster: "/images/posters/histories/2.jpg",
},
{
id: "3",
value: "/videos/histories/3.mp4",
title:
"Интерактивная инсталляция graff.estate для ЖК DNS City в г. Владивосток",
desc: "",
poster: "/images/posters/histories/3.jpg",
},
];
+6
View File
@@ -0,0 +1,6 @@
interface IClient {
id: string;
src: string;
}
export type { IClient };
+11
View File
@@ -0,0 +1,11 @@
interface IReview {
id: string;
avatar: string;
name: string;
position: string;
desc: string;
video: string;
poster: string;
}
export type { IReview };