From 4adbf791ead5e59dfa37dc730abe5cd77e2a6fa4 Mon Sep 17 00:00:00 2001 From: Madara0330E Date: Wed, 16 Jul 2025 23:16:00 +0500 Subject: [PATCH] Centralize and update contact information across app Introduced a centralized contacts configuration in src/config/contacts.js, added a useContacts hook, and a reusable ContactInfo component. Updated Header, Footer, Home, Office, Services, About, Objects, and Apartament pages to use the new contact data source. Added documentation in CONTACTS_CONFIG.md and included the AlmaVid logo asset. --- CONTACTS_CONFIG.md | 122 +++++ package-lock.json | 8 +- src/assets/images/logo/AlmaVid-Logo.svg | 11 + src/components/ContactInfo/ContactInfo.jsx | 78 +++ src/components/ContactInfo/ContactInfo.scss | 72 +++ src/components/Footer/Footer.jsx | 80 ++-- src/components/Form/Form.jsx | 276 ++++++++--- src/components/Header/Header.jsx | 167 ++++--- src/components/Header/Header.scss | 505 ++++++++++---------- src/components/Object/Object.scss | 49 ++ src/components/Sliders/SliderObjects.jsx | 114 +++-- src/config/contacts.js | 70 +++ src/hooks/useContacts.js | 15 + src/pages/About/About.jsx | 79 +-- src/pages/Apartament/Apartament.jsx | 433 ++++++++++++----- src/pages/Home/Home.jsx | 139 +++++- src/pages/Home/Home.scss | 27 +- src/pages/Objects/Objects.jsx | 39 +- src/pages/Office/Office.jsx | 297 +++++++++--- src/pages/Services/Services.jsx | 264 +++++----- 20 files changed, 2007 insertions(+), 838 deletions(-) create mode 100644 CONTACTS_CONFIG.md create mode 100644 src/assets/images/logo/AlmaVid-Logo.svg create mode 100644 src/components/ContactInfo/ContactInfo.jsx create mode 100644 src/components/ContactInfo/ContactInfo.scss create mode 100644 src/config/contacts.js create mode 100644 src/hooks/useContacts.js diff --git a/CONTACTS_CONFIG.md b/CONTACTS_CONFIG.md new file mode 100644 index 0000000..351f844 --- /dev/null +++ b/CONTACTS_CONFIG.md @@ -0,0 +1,122 @@ +# Конфигурация контактной информации + +Этот файл содержит всю контактную информацию агентства недвижимости АЛМА-ВИД в централизованном виде. + +## Расположение файла + +`src/config/contacts.js` + +## Структура конфигурации + +### Основные контакты + +- `phone` - номер телефона с отображаемым текстом и ссылкой для набора +- `email` - электронная почта с отображаемым текстом и ссылкой mailto + +### Адреса + +- `address.main` - основной адрес офиса (для карт и контактов) +- `address.office` - рабочий адрес офиса (для страницы "Наш офис") + +### Координаты + +- `coordinates` - координаты для отображения на Яндекс.Картах + +### Социальные сети + +- `social.vk` - ВКонтакте +- `social.telegram` - Telegram +- `social.instagram` - Instagram + +### Название компании + +- `companyName` - полное название +- `companyNameShort` - сокращенное название + +## Использование + +### В компонентах + +```jsx +import { CONTACTS } from '../../config/contacts'; + +// Использование телефона +{CONTACTS.phone.display} + +// Использование email +{CONTACTS.email.display} + +// Использование соцсетей +{CONTACTS.social.vk.name} + +// Координаты для карт + + + +``` + +### С помощью хука useContacts + +```jsx +import { useContacts } from "../../hooks/useContacts"; + +function MyComponent() { + const contacts = useContacts(); + + return ( +
+ {contacts.phone.display} + {contacts.getSocialName("vk")} +
+ ); +} +``` + +### Компонент ContactInfo + +```jsx +import { ContactInfo } from '../../components/ContactInfo/ContactInfo'; + +// Базовый вариант + + +// Inline вариант + + +// Полный вариант со всей информацией + +``` + +## Обновленные страницы + +Все следующие страницы теперь используют централизованную конфигурацию: + +1. **Header** - номер телефона и социальные сети +2. **Footer** - социальные сети +3. **Home** - заголовок страницы, карта с координатами и balloon +4. **Office** - заголовок страницы, контактная информация, карта +5. **Services** - заголовок страницы, ссылка на ВКонтакте +6. **About** - заголовок страницы, название компании +7. **Objects** - заголовок страницы +8. **Apartament** - карта с координатами и balloon + +## Преимущества централизованной конфигурации + +1. **Единое место для изменений** - все контакты изменяются в одном файле +2. **Консистентность** - одинаковое отображение на всех страницах +3. **Легкость обслуживания** - не нужно искать и изменять контакты по всему проекту +4. **Типизация** - структурированные данные с понятным API +5. **Переиспользование** - легко добавлять контакты на новые страницы + +## Как изменить контактную информацию + +1. Откройте файл `src/config/contacts.js` +2. Измените нужные значения +3. Сохраните файл +4. Изменения автоматически применятся ко всем страницам diff --git a/package-lock.json b/package-lock.json index 9f5094e..dfac003 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17219,16 +17219,16 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/unbox-primitive": { diff --git a/src/assets/images/logo/AlmaVid-Logo.svg b/src/assets/images/logo/AlmaVid-Logo.svg new file mode 100644 index 0000000..6434cb7 --- /dev/null +++ b/src/assets/images/logo/AlmaVid-Logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/components/ContactInfo/ContactInfo.jsx b/src/components/ContactInfo/ContactInfo.jsx new file mode 100644 index 0000000..76d9cf9 --- /dev/null +++ b/src/components/ContactInfo/ContactInfo.jsx @@ -0,0 +1,78 @@ +import { CONTACTS } from "../../config/contacts"; +import "./ContactInfo.scss"; + +function ContactInfo({ variant = "default" }) { + if (variant === "inline") { + return ( +
+ + {CONTACTS.phone.display} + + + {CONTACTS.email.display} + +
+ ); + } + + if (variant === "full") { + return ( +
+
+ Телефон: + + {CONTACTS.phone.display} + +
+ +
+ Адрес: + + {CONTACTS.address.main.full} + +
+ +
+ ); + } + + return ( +
+ + {CONTACTS.phone.display} + + + {CONTACTS.email.display} + +
+ ); +} + +export { ContactInfo }; diff --git a/src/components/ContactInfo/ContactInfo.scss b/src/components/ContactInfo/ContactInfo.scss new file mode 100644 index 0000000..1b62060 --- /dev/null +++ b/src/components/ContactInfo/ContactInfo.scss @@ -0,0 +1,72 @@ +.contact-info { + display: flex; + gap: 1rem; + align-items: center; + + &__phone, + &__email { + text-decoration: none; + color: inherit; + font-weight: 600; + transition: color 0.3s ease; + + &:hover { + color: #007bff; + } + } +} + +.contact-info-inline { + display: flex; + gap: 2rem; + align-items: center; + + .contact-info__phone, + .contact-info__email { + a { + text-decoration: none; + color: inherit; + font-weight: 600; + transition: color 0.3s ease; + + &:hover { + color: #007bff; + } + } + } +} + +.contact-info-full { + .contact-info__item { + margin-bottom: 1rem; + + .contact-info__label { + font-weight: bold; + margin-right: 0.5rem; + } + + .contact-info__value { + text-decoration: none; + color: inherit; + + &:hover { + color: #007bff; + } + } + } + + .contact-info__social { + display: flex; + gap: 1rem; + + &-link { + text-decoration: none; + color: inherit; + transition: color 0.3s ease; + + &:hover { + color: #007bff; + } + } + } +} diff --git a/src/components/Footer/Footer.jsx b/src/components/Footer/Footer.jsx index aa6b7f7..b006f10 100644 --- a/src/components/Footer/Footer.jsx +++ b/src/components/Footer/Footer.jsx @@ -1,38 +1,58 @@ -import './Footer.scss'; -import { Link, useNavigate } from 'react-router-dom'; +import "./Footer.scss"; +import { Link, useNavigate } from "react-router-dom"; +import { CONTACTS } from "../../config/contacts"; function Footer() { - const navigate = useNavigate(); + const navigate = useNavigate(); - const handleClick = () => { - window.scrollTo({ - top: 0, - behavior: 'smooth' - }); - }; + const handleClick = () => { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + }; - const handlePageChange = (path) => { - handleClick(); - navigate(path); - }; + const handlePageChange = (path) => { + handleClick(); + navigate(path); + }; - return ( -
- handlePageChange('/')}>OOO "ALMA-VID" -
- handlePageChange('/objects')}>Объекты - handlePageChange('/services')}>Услуги - handlePageChange('/about')}>О компании - handlePageChange('/office')}>Контакты -
-
- - - -
-
- ); + return ( +
+ handlePageChange("/")} + > + OOO "ALMA-VID" + +
+ handlePageChange("/objects")}> + Объекты + + handlePageChange("/services")}> + Услуги + + handlePageChange("/about")}> + О компании + + handlePageChange("/office")}> + Контакты + +
+
+ + + +
+
+ ); } export { Footer }; diff --git a/src/components/Form/Form.jsx b/src/components/Form/Form.jsx index 58f28cc..c7a4d80 100644 --- a/src/components/Form/Form.jsx +++ b/src/components/Form/Form.jsx @@ -1,89 +1,213 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect } from "react"; -import './Form.scss'; -import PencilIcon from '../../assets/images/icons/pencil.svg'; +import "./Form.scss"; +import PencilIcon from "../../assets/images/icons/pencil.svg"; +import { API_CONFIG } from "../../config/contacts"; function Form({ scrolledThreshold }) { - const [name, setName] = useState(''); - const [phone, setPhone] = useState(''); - const [isVisible, setVisible] = useState(false); + const [name, setName] = useState(""); + const [phone, setPhone] = useState(""); + const [isVisible, setVisible] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [submitStatus, setSubmitStatus] = useState(null); // 'success', 'error', null - const checkVisible = () => { - const scrolled = document.documentElement.scrollTop; - if (scrolled > scrolledThreshold){ // вы можете настроить это значение - if(!isVisible) setVisible(true); - } else{ - if(isVisible) setVisible(false); - } - }; + const checkVisible = () => { + const scrolled = document.documentElement.scrollTop; + if (scrolled > scrolledThreshold) { + // вы можете настроить это значение + if (!isVisible) setVisible(true); + } else { + if (isVisible) setVisible(false); + } + }; - useEffect(() => { - window.addEventListener('scroll', checkVisible); - return () => window.removeEventListener('scroll', checkVisible); - }); + useEffect(() => { + window.addEventListener("scroll", checkVisible); + return () => window.removeEventListener("scroll", checkVisible); + }); - const handleSubmit = (e) => { - e.preventDefault(); - // в этой функции можно добавить логику для отправки формы на сервер - console.log(`${name}, ваш номер телефона: ${phone}`); - setName(''); - setPhone(''); - }; + // Автоматически скрываем сообщение об успехе через 5 секунд + useEffect(() => { + if (submitStatus === "success") { + const timer = setTimeout(() => { + setSubmitStatus(null); + }, 5000); + return () => clearTimeout(timer); + } + }, [submitStatus]); - function scrollToTop() { - window.scrollTo({ - top: 0, - behavior: 'smooth' - }); + // Функция для форматирования номера телефона + const formatPhoneNumber = (value) => { + // Убираем все нецифровые символы + const phoneNumber = value.replace(/\D/g, ""); + + // Ограничиваем длину номера + if (phoneNumber.length > 11) { + return phone; // возвращаем предыдущее значение, если превышена длина } - return ( -
-
-

- Решили купить или продать квартиру? - Закажите бесплатную консультацию -

-
-

Заполните форму ниже

-

Мы позвоним вам в ближайшее время

-
- setName(e.target.value)} - /> - setPhone(e.target.value)} - /> - -
-

- Заполняя форму, вы соглашаетесь с политикой - конфиденциальности -

-
-
-
-

- У вас остались вопросы? Напишите нам, мы онлайн! -

- pencil -
+ // Форматируем номер + if (phoneNumber.length === 0) return ""; + if (phoneNumber.length <= 1) return `+7`; + if (phoneNumber.length <= 4) return `+7(${phoneNumber.slice(1)}`; + if (phoneNumber.length <= 7) + return `+7(${phoneNumber.slice(1, 4)})${phoneNumber.slice(4)}`; + if (phoneNumber.length <= 9) + return `+7(${phoneNumber.slice(1, 4)})${phoneNumber.slice( + 4, + 7 + )}-${phoneNumber.slice(7)}`; + return `+7(${phoneNumber.slice(1, 4)})${phoneNumber.slice( + 4, + 7 + )}-${phoneNumber.slice(7, 9)}-${phoneNumber.slice(9, 11)}`; + }; + + const handlePhoneChange = (e) => { + const formattedPhone = formatPhoneNumber(e.target.value); + setPhone(formattedPhone); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + // Проверяем, что поля заполнены + if (!name.trim() || !phone.trim()) { + setSubmitStatus("error"); + return; + } + + setIsLoading(true); + setSubmitStatus(null); + + try { + const response = await fetch( + API_CONFIG.getFullURL(API_CONFIG.endpoints.feedback), + { + method: "POST", + headers: { + "Content-Type": "application/json", + accept: "*/*", + }, + body: JSON.stringify({ + firstName: name.trim(), + phoneNumber: phone.trim().replace(/\D/g, ""), // убираем все нецифровые символы + }), + } + ); + + if (response.ok) { + setSubmitStatus("success"); + setName(""); + setPhone(""); + console.log("Заявка успешно отправлена"); + } else { + throw new Error("Ошибка при отправке заявки"); + } + } catch (error) { + console.error("Ошибка при отправке формы:", error); + setSubmitStatus("error"); + } finally { + setIsLoading(false); + } + }; + + function scrollToTop() { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + } + + return ( +
+
+

+ Решили купить или продать квартиру? + Закажите бесплатную консультацию +

+
+

Заполните форму ниже

+

Мы позвоним вам в ближайшее время

+ + {submitStatus === "success" && ( +
+ ✅ Спасибо! Ваша заявка отправлена. Мы свяжемся с вами в ближайшее + время.
-
- ); + )} + + {submitStatus === "error" && ( +
+ ❌ Ошибка при отправке. Пожалуйста, проверьте данные и попробуйте + снова. +
+ )} + +
+ setName(e.target.value)} + required + disabled={isLoading} + /> + + +
+

Заполняя форму, вы соглашаетесь с политикой конфиденциальности

+ +
+
+

+ У вас остались вопросы? Напишите нам, мы онлайн! +

+ pencil +
+
+
+ ); } export { Form }; diff --git a/src/components/Header/Header.jsx b/src/components/Header/Header.jsx index 5ac5717..296f4d2 100644 --- a/src/components/Header/Header.jsx +++ b/src/components/Header/Header.jsx @@ -1,86 +1,117 @@ -import './Header.scss'; -import React, { useState } from 'react'; -import { Link, useLocation } from 'react-router-dom'; +import "./Header.scss"; +import React, { useState } from "react"; +import { Link, useLocation } from "react-router-dom"; +import { CONTACTS } from "../../config/contacts"; function Header() { - const [isOpen, setIsOpen] = useState(false); + const [isOpen, setIsOpen] = useState(false); - const handleToggle = () => { - setIsOpen(!isOpen); - }; + const handleToggle = () => { + setIsOpen(!isOpen); + }; - const location = useLocation(); - const isGradientBackground = location.pathname === '/apartament'; - const isHomePage = location.pathname === '/'; - return ( -
- - {isHomePage &&
} -
- 8(351)217-00-74 -
- - - -
-
- -
- ); + + + + +
+ + {CONTACTS.phone.display} + + +
+
+ + {CONTACTS.address.main.city} + +
+ + + +
+
+ + ); } export { Header }; diff --git a/src/components/Header/Header.scss b/src/components/Header/Header.scss index 19f0f48..9bc900a 100644 --- a/src/components/Header/Header.scss +++ b/src/components/Header/Header.scss @@ -1,388 +1,391 @@ -@import '../../styles/vars.scss'; +@import "../../styles/vars.scss"; a { - color: white; - text-decoration: none; + color: white; + text-decoration: none; } .header { - display: grid; - grid-template-columns: repeat(3, 1fr); - grid-template-rows: repeat(2, 1fr); - padding: 45px; - color: white; + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-template-rows: repeat(2, 1fr); + padding: 45px; + color: white; } .header a:hover { - text-decoration: underline; + text-decoration: underline; } .header_gradient { - background: linear-gradient(180deg, #061A25 0%, rgba(18, 114, 170, 0.7) 150%); + background: linear-gradient(180deg, #061a25 0%, rgba(18, 114, 170, 0.7) 150%); } .header__logo { - grid-row: 1/3; - background-image: url('../../assets/images/logo/logo-png.png'); - width: 302px; - height: 69px; - background-size: cover; + grid-row: 1/3; + background-image: url("../../assets/images/logo/AlmaVid-Logo.svg"); + width: 320px; + height: 69px; + background-size: cover; } .header__nav { - display: flex; - align-items: center; - justify-content: center; - column-gap: 47px; - font-size: 17px; - line-height: 29px; + display: flex; + align-items: center; + justify-content: center; + column-gap: 47px; + font-size: 17px; + line-height: 29px; } .header-info { - display: flex; - grid-column: 3/3; - align-items: center; - justify-self: end; - column-gap: 23.5px; + display: flex; + grid-column: 3/3; + align-items: center; + justify-self: end; + column-gap: 23.5px; } .header-info__btn { - height: 38px; - width: 150px; - font-weight: 700; - font-size: 15px; - color: black; - background-color: rgb(255, 255, 255, 0.8); - border-radius: 16px; - border: none; - cursor: pointer; + height: 38px; + width: 150px; + font-weight: 700; + font-size: 15px; + color: black; + background-color: rgb(255, 255, 255, 0.8); + border-radius: 16px; + border: none; + cursor: pointer; } .header-info__btn:hover { - background-color: rgb(255, 255, 255, 0.9); + background-color: rgb(255, 255, 255, 0.9); } .header-socials { - grid-row: 2/2; - grid-column: 3/3; - display: flex; - align-items: center; - column-gap: 47px; - justify-self: end; - margin-right: 29px; - margin-top: 10px; + grid-row: 2/2; + grid-column: 3/3; + display: flex; + align-items: center; + column-gap: 47px; + justify-self: end; + margin-right: 29px; + margin-top: 10px; } .header-socials__links { - display: flex; - align-items: center; + display: flex; + align-items: center; } .header-socials__tg { - background-image: url(../../assets/images/icons/telegram.svg); - width: 22.5px; - height: 19px; - background-size: cover; + background-image: url(../../assets/images/icons/telegram.svg); + width: 22.5px; + height: 19px; + background-size: cover; } // ширину и высоту увеличили на 1px .header-socials__inst { - background-image: url(../../assets/images/icons/inst.svg); - width: 42px; - height: 24px; - background-size: cover; + background-image: url(../../assets/images/icons/inst.svg); + width: 42px; + height: 24px; + background-size: cover; } // ширину уменьшили на 1px .header-socials__vk { - background-image: url(../../assets/images/icons/vk.svg); - width: 32.5px; - height: 34px; - background-size: cover; + background-image: url(../../assets/images/icons/vk.svg); + width: 32.5px; + height: 34px; + background-size: cover; } -@media (min-width:1800px) { - .header__logo { - width: 372px; - height: 85px; - } +@media (min-width: 1800px) { + .header__logo { + width: 400px; + height: 85px; + } - .header__nav, - .header-info__call, - .header-info__btn, - .header-socials__location { - font-size: 21px; - } + .header__nav, + .header-info__call, + .header-info__btn, + .header-socials__location { + font-size: 21px; + } - .header-info__btn { - width: 210px; - } + .header-info__btn { + width: 210px; + } - .header-socials__tg { - width: 28.5px; - height: 24px; - } + .header-socials__tg { + width: 28.5px; + height: 24px; + } - .header-socials__inst { - width: 47px; - height: 27px; - } + .header-socials__inst { + width: 47px; + height: 27px; + } - .header-socials__vk { - width: 35.5px; - height: 37px; - } + .header-socials__vk { + width: 35.5px; + height: 37px; + } } @media (max-width: 1480px) { - .header__nav { - column-gap: 31px; - font-size: 16px; - } + .header__nav { + column-gap: 31px; + font-size: 16px; + } } @media (max-width: $desktopWidth) { + .header { + padding: 36px; + } - .header { - padding: 36px; - } + .header__logo { + width: 290px; + height: 62px; + } - .header__logo { - width: 270px; - height: 62px; - } + .header-info, + .header-info__btn { + font-size: 14px; + } - .header-info, - .header-info__btn { - font-size: 14px; - } + .header-info__btn { + height: 35px; + width: 137px; + } - .header-info__btn { - height: 35px; - width: 137px; - } + .header__nav { + column-gap: 17px; + font-size: 14px; + } - .header__nav { - column-gap: 17px; - font-size: 14px; - } - - .header-socials { - font-size: 14px; - } + .header-socials { + font-size: 14px; + } } @media (max-width: $laptopWidth) { - .header { - grid-template-columns: auto; - padding: 18px; - } + .header { + grid-template-columns: auto; + padding: 18px; + } - .header__nav { - column-gap: 14px; - font-size: 11px; - } + .header__nav { + column-gap: 14px; + font-size: 11px; + } - .header__logo { - width: 200px; - height: 46px; - } + .header__logo { + width: 220px; + height: 46px; + } - .header-info { - font-size: 11px; - } + .header-info { + font-size: 11px; + } - .header-info__btn { - font-size: 13px; - } + .header-info__btn { + font-size: 13px; + } - .header-socials { - margin-top: 5px; - font-size: 11px; - } + .header-socials { + margin-top: 5px; + font-size: 11px; + } +} +@media (max-width: 780px) { + .header__logo { + width: 210px; + height: 42px; + } } - h1 { - text-align: center; - letter-spacing: 1px; - word-spacing: 0.15em; - font-size: 3em; - line-height: 1.2; - transform: translateY(52%); + text-align: center; + letter-spacing: 1px; + word-spacing: 0.15em; + font-size: 3em; + line-height: 1.2; + transform: translateY(52%); } - #toggle { - display: none; + display: none; } /** Hamburger **/ .hamburger { - position: absolute; - top: 4em; - right: 7%; - margin-left: -2em; - margin-top: -45px; - width: 2em; - height: 45px; - z-index: 5; + position: absolute; + top: 4em; + right: 7%; + margin-left: -2em; + margin-top: -45px; + width: 2em; + height: 45px; + z-index: 5; } .open { - position: fixed; + position: fixed; } .hamburger div { - position: relative; - width: 3.1em; - height: 5px; - border-radius: 3px; - background-color: white; - margin-top: 8px; - transition: all 0.3s ease-in-out; + position: relative; + width: 3.1em; + height: 5px; + border-radius: 3px; + background-color: white; + margin-top: 8px; + transition: all 0.3s ease-in-out; } /** Nav Styles **/ .nav { - position: fixed; - width: 100%; - height: 100%; - background-color: #17628C; - top: -100%; - left: 0; - right: 0; - bottom: 0; - overflow: hidden; - transition: all 0.3s ease-in-out; - transform: scale(0); - z-index: 1; + position: fixed; + width: 100%; + height: 100%; + background-color: #17628c; + top: -100%; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; + transition: all 0.3s ease-in-out; + transform: scale(0); + z-index: 1; } .nav-wrapper { - position: relative; - overflow: hidden; - overflow-y: auto; - height: 100%; + position: relative; + overflow: hidden; + overflow-y: auto; + height: 100%; } .nav-container { - display: flex; - flex-direction: column; - text-align: left; - margin-left: 25%; - margin-top: 15%; + display: flex; + flex-direction: column; + text-align: left; + margin-left: 25%; + margin-top: 15%; } .nav-container a, .nav-container p { - position: relative; - text-decoration: none; - color: white; - font-size: 2em; - display: inline-block; - margin-top: 1.25em; - margin-bottom: 0; - transition: color 0.2s ease-in-out; - letter-spacing: 1px; + position: relative; + text-decoration: none; + color: white; + font-size: 2em; + display: inline-block; + margin-top: 1.25em; + margin-bottom: 0; + transition: color 0.2s ease-in-out; + letter-spacing: 1px; } .nav-container a:hover { - text-decoration: none; + text-decoration: none; } .nav-container .header__link:before { - content: ''; - height: 0; - position: absolute; - width: 0.25em; - background-color: white; - left: -0.5em; - transition: all 0.2s ease-in-out; + content: ""; + height: 0; + position: absolute; + width: 0.25em; + background-color: white; + left: -0.5em; + transition: all 0.2s ease-in-out; } .nav-container a:hover { - color: white; + color: white; } .nav-container a:hover:before { - height: 100%; + height: 100%; } /** Animations **/ -#toggle:checked+.hamburger .top-bun { - transform: rotate(-45deg); - margin-top: 25px; +#toggle:checked + .hamburger .top-bun { + transform: rotate(-45deg); + margin-top: 25px; } -#toggle:checked+.hamburger .bottom-bun { - opacity: 0; - transform: rotate(45deg); +#toggle:checked + .hamburger .bottom-bun { + opacity: 0; + transform: rotate(45deg); } -#toggle:checked+.hamburger .meat { - transform: rotate(45deg); - margin-top: -7px; +#toggle:checked + .hamburger .meat { + transform: rotate(45deg); + margin-top: -7px; } -#toggle:checked+.hamburger+.nav { - top: 0; - transform: scale(1); +#toggle:checked + .hamburger + .nav { + top: 0; + transform: scale(1); } @media (min-width: 769px) { - .burgerMenu { - display: none; - } + .burgerMenu { + display: none; + } - .header__text { - display: none; - } + .header__text { + display: none; + } } @media (max-width: $tabletWidth) { - .header { - display: flex; - justify-content: space-between; - align-items: center; - } + .header { + display: flex; + justify-content: space-between; + align-items: center; + } - .burgerMenu { - display: flex; - justify-content: flex-end; - } + .burgerMenu { + display: flex; + justify-content: flex-end; + } - .header-info__call { - margin-right: 100px; - } + .header-info__call { + margin-right: 100px; + } - .header__logo { - // grid-row: 1/3; - background-image: url('../../assets/images/logo/AlvaMid-Logo-Small.svg'); - width: 50px; - height: 50px; - } + .header__logo { + // grid-row: 1/3; + background-image: url("../../assets/images/logo/AlvaMid-Logo-Small.svg"); + width: 50px; + height: 50px; + } - .header__nav, - .header-info, - .header-socials { - display: none; - } + .header__nav, + .header-info, + .header-socials { + display: none; + } - .header__text { - position: absolute; - background-image: url('../../assets/images/logo/textLogo.svg'); - top: 40%; - left: 50%; - transform: translate(-50%, -50%); - width: 300px; - height: 113px; - background-size: contain; - background-repeat: no-repeat; - } -} \ No newline at end of file + .header__text { + position: absolute; + background-image: url("../../assets/images/logo/textLogo.svg"); + top: 40%; + left: 50%; + transform: translate(-50%, -50%); + width: 300px; + height: 113px; + background-size: contain; + background-repeat: no-repeat; + } +} diff --git a/src/components/Object/Object.scss b/src/components/Object/Object.scss index 5bc22ae..b2deba1 100644 --- a/src/components/Object/Object.scss +++ b/src/components/Object/Object.scss @@ -66,6 +66,55 @@ .item__image { border-top-right-radius: 15px; border-top-left-radius: 15px; + width: 100%; + height: 250px; + object-fit: cover; + object-position: center; + display: block; + + @media (max-width: 1300px) { + height: 230px; + } + + @media (max-width: 1200px) { + height: 210px; + } + + @media (max-width: 1100px) { + height: 190px; + } + + @media (max-width: $laptopWidth) { + height: 170px; + } + + @media (max-width: $tabletWidth) { + height: 300px; + } + + @media (max-width: 740px) { + height: 280px; + } + + @media (max-width: 690px) { + height: 250px; + } + + @media (max-width: 635px) { + height: 220px; + } + + @media (max-width: 570px) { + height: 190px; + } + + @media (max-width: $mobileWidth) { + height: 210px; + } + + @media (max-width: 420px) { + height: 190px; + } } .item__info { diff --git a/src/components/Sliders/SliderObjects.jsx b/src/components/Sliders/SliderObjects.jsx index 3b79fe7..fc04b00 100644 --- a/src/components/Sliders/SliderObjects.jsx +++ b/src/components/Sliders/SliderObjects.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import Slider from 'react-slick'; import { Link } from 'react-router-dom'; import { useMediaQuery } from 'react-responsive'; @@ -7,6 +7,7 @@ import 'slick-carousel/slick/slick.css'; import 'slick-carousel/slick/slick-theme.css'; import './SliderObjects.scss'; import { Object } from '../Object/Object'; +import { API_CONFIG } from '../../config/contacts'; import objectPicOne from '../../assets/images/apartaments/image-44.jpg'; import objectPicTwo from '../../assets/images/apartaments/image-45.jpg'; @@ -25,10 +26,40 @@ const PrevArrow = ({ onClick }) => { }; const SliderComponent = () => { + const [objects, setObjects] = useState([]); + const [loading, setLoading] = useState(true); + const isMobileResolution = useMediaQuery({ maxWidth: 768 }); + + // Загрузка данных с API + useEffect(() => { + const fetchObjects = async () => { + try { + const response = await fetch(API_CONFIG.getFullURL(API_CONFIG.endpoints.rental), { + method: 'GET', + headers: { + 'accept': '*/*' + } + }); + + if (response.ok) { + const data = await response.json(); + setObjects(data); + } + } catch (error) { + console.error('Ошибка при загрузке объектов:', error); + // В случае ошибки оставляем пустой массив, компонент не сломается + } finally { + setLoading(false); + } + }; + + fetchObjects(); + }, []); + const settings = { - dots: false, - infinite: true, + dots: objects.length > (isMobileResolution ? 1 : 3), // Показываем точки если объектов больше чем влезает + infinite: objects.length > (isMobileResolution ? 1 : 3), // Бесконечная прокрутка только если объектов достаточно speed: 500, slidesToShow: isMobileResolution ? 1 : 3, slidesToScroll: isMobileResolution ? 1 : 3, @@ -37,46 +68,49 @@ const SliderComponent = () => { prevArrow: , }; + // Если загружаем данные, показываем статические объекты как fallback + if (loading || objects.length === 0) { + return ( +
+ + + + + + + + + ); + } + return (
- - - - - - - + {objects.map((object) => ( + + + + ))} ); diff --git a/src/config/contacts.js b/src/config/contacts.js new file mode 100644 index 0000000..6dca52a --- /dev/null +++ b/src/config/contacts.js @@ -0,0 +1,70 @@ +export const CONTACTS = { + phone: { + display: "8(351)217-00-74", + link: "tel:+73512170074", + }, + email: { + display: "sdelka.74@yandex.ru", + link: "mailto:sdelka.74@yandex.ru", + }, + + address: { + main: { + street: "ул. Комаровского, 4А, офис 210", + city: "Челябинск", + postalCode: "454052", + full: "ул. Комаровского, 4А, офис 210, Челябинск, 454052", + }, + office: { + street: "Ленина, д. 60 В, оф. 701", + city: "Челябинск", + description: "Вход в офис со двора", + full: "Ленина, д. 60 В, оф. 701, Челябинск", + }, + }, + + coordinates: [55.242355, 61.37697], + + social: { + vk: { + url: "https://vk.com/almavid_74", + name: "ВКонтакте", + }, + telegram: { + url: "https://t.me/almavid_74", + name: "Telegram", + }, + instagram: { + url: "https://instagram.com/almavid_74", + name: "Instagram", + }, + }, + + companyName: "Агентство недвижимости АЛМА-ВИД", + companyNameShort: "АЛМА-ВИД", + + workingHours: "Пн-Пт: 9:00-18:00, Сб: 10:00-16:00", + + getBalloonContent: () => { + return ` +
+ ${CONTACTS.companyName}
+ Адрес: ${CONTACTS.address.main.street}
+ ${CONTACTS.address.main.city}, ${CONTACTS.address.main.postalCode}
+ Тел: ${CONTACTS.phone.display} +
+ `; + }, +}; + +export const API_CONFIG = { + baseURL: "https://almavid.ngr1.ru", + endpoints: { + feedback: "/api/feedback/send", + rental: "/api/rental/send", + }, + + getFullURL: (endpoint) => { + return `${API_CONFIG.baseURL}${endpoint}`; + }, +}; diff --git a/src/hooks/useContacts.js b/src/hooks/useContacts.js new file mode 100644 index 0000000..2482639 --- /dev/null +++ b/src/hooks/useContacts.js @@ -0,0 +1,15 @@ +import { CONTACTS } from "../config/contacts"; + +// Хук для использования контактной информации +export const useContacts = () => { + return { + ...CONTACTS, + // Дополнительные утилитарные функции + getPhoneLink: () => CONTACTS.phone.link, + getEmailLink: () => CONTACTS.email.link, + getFullAddress: (type = "main") => + CONTACTS.address[type]?.full || CONTACTS.address.main.full, + getSocialLink: (platform) => CONTACTS.social[platform]?.url || "#", + getSocialName: (platform) => CONTACTS.social[platform]?.name || platform, + }; +}; diff --git a/src/pages/About/About.jsx b/src/pages/About/About.jsx index 8095d53..ff852ef 100644 --- a/src/pages/About/About.jsx +++ b/src/pages/About/About.jsx @@ -1,41 +1,52 @@ -import { useEffect } from 'react'; +import { useEffect } from "react"; -import './About.scss'; -import { Header } from '../../components/Header/Header'; -import { Form } from '../../components/Form/Form'; -import { SliderComponent } from '../../components/Sliders/Slider'; +import "./About.scss"; +import { Header } from "../../components/Header/Header"; +import { Form } from "../../components/Form/Form"; +import { SliderComponent } from "../../components/Sliders/Slider"; +import { CONTACTS } from "../../config/contacts"; function About() { - useEffect(() => { - document.title = 'Об Агентстве недвижимости АЛМА-ВИД'; - }, []); + useEffect(() => { + document.title = `Об ${CONTACTS.companyName}`; + }, []); - return ( - <> -
-
-
-
-
-

Каждый из нас хоть раз сталкивается с квартирным вопросом - покупка, продажа, - обмен квартиры или дома - эти процессы требуют серьезного профессионального подхода - и юридической грамотности.

-

Агентство недвижимости АЛМА-ВИД существует с 2000 года — за этот период мы обрели - доверие и уважение наших многочисленных клиентов.

-

Мы находимся в постоянном развитии и оттачиваем профессионализм наших сотрудников, - обладая серьезной материальной базой и налаженными коммуникациями с крупными банками. - Благодаря этому мы имеем возможность предоставлять услуги - в сфере недвижимости высокого качества.

-
-
-
Об Агентстве недвижимости АЛМА-Вид
-
+ return ( + <> +
+
+
+
+
+

+ Каждый из нас хоть раз сталкивается с квартирным вопросом - + покупка, продажа, обмен квартиры или дома - эти процессы требуют + серьезного профессионального подхода и юридической грамотности. +

+

+ Агентство недвижимости АЛМА-ВИД существует с 2000 года — за этот + период мы обрели доверие и уважение наших многочисленных + клиентов. +

+

+ Мы находимся в постоянном развитии и оттачиваем профессионализм + наших сотрудников, обладая серьезной материальной базой и + налаженными коммуникациями с крупными банками. Благодаря этому + мы имеем возможность предоставлять услуги в сфере недвижимости + высокого качества. +

-
- -
-
- - ); +
+
+ Об {CONTACTS.companyName} {CONTACTS.companyNameShort} +
+
+
+
+ +
+ + + ); } export { About }; diff --git a/src/pages/Apartament/Apartament.jsx b/src/pages/Apartament/Apartament.jsx index ba30d39..fe978d9 100644 --- a/src/pages/Apartament/Apartament.jsx +++ b/src/pages/Apartament/Apartament.jsx @@ -1,123 +1,326 @@ -import { useEffect } from 'react'; -import './Apartament.scss'; -import { YMaps, Map, } from '@pbe/react-yandex-maps'; +import { useEffect, useState } from "react"; +import "./Apartament.scss"; +import { YMaps, Map, Placemark } from "@pbe/react-yandex-maps"; -import { Header } from '../../components/Header/Header'; -import { Form } from '../../components/Form/Form'; -import { Gallery } from '../../components/Gallery/Gallery'; +import { Header } from "../../components/Header/Header"; +import { Form } from "../../components/Form/Form"; +import { Gallery } from "../../components/Gallery/Gallery"; +import { CONTACTS } from "../../config/contacts"; function Apartament() { - useEffect(() => { - document.title = 'Апартаменты'; - }, []); + const [isMapActive, setIsMapActive] = useState(false); - return ( - <> -
-
-

1-комн. кв. 34 м

-

Ул. Луначарского, Ленинский район

-
-
- -
-
-
-
-

Общая площадь

-

72М

-
-
-
-
-
-

Жилая площадь

-

68М

-
-
-
-
-
-

Площадь кухни

-

11М

-
-
-
-
-
-

Год постройки

-

2003

-
-
-
-
-
-

Этаж

-

7/12

-
-
-
-
-

Описани хаты рандомный текст Lorem Ipsum is simply dummy text of - the printing and typesetting industry. Lorem Ipsum has been the - industry's standard dummy text ever since the 1500s, when an unknown - printer took a galley of type and scrambled it to make a type specimen book. - It has survived not only five centuries, but also the leap into electronic - typesetting, remaining essentially unchanged. It was popularised in the 1960s - with the release of Letraset sheets containing Lorem Ipsum passages, and more - recently with desktop publishing software like Aldus PageMaker including - versions of Lorem Ipsum.

-
-
-
-

О квартире

-
    -
  • Тип жилья: вторичный
  • -
  • Общая площадь: 31.9 м²
  • -
  • Площадь кухни: 4 м²
  • -
  • Жилая площадь: 21 м²
  • -
  • Этаж: 4 из 5
  • -
  • Балкон или лоджия: лоджия
  • -
  • Высота потолков: 2.5 м
  • -
  • Санузел: совмещенный
  • -
  • Вид из окон: во двор
  • -
  • Ремонт: косметический
  • -
-
-
-

О доме

-
    -
  • Год постройки: 2003
  • -
  • Тип дома: панельный
  • -
  • Тип перекрытий: железобетонный
  • -
  • Подъезды: 5
  • -
  • Отопление: центральное
  • -
  • Аварийность: нет
  • -
  • Газоснобжение: центральное
  • -
-
-
-
-
-
-

1 234 567 ₽

-
-
-

В ипотеку от 6546823 ₽/мес

-

Цена за метр............................................................45656 ₽/м

-

Условия сделки......................................свободная продажа

-

Ипотека......................................................................возможна

- - -
+ useEffect(() => { + document.title = "Апартаменты"; + }, []); + + const handleActivateMap = () => { + setIsMapActive(true); + if (window.apartamentMap) { + window.apartamentMap.behaviors.enable("scrollZoom"); + window.apartamentMap.behaviors.enable("drag"); + } + }; + + const handleDeactivateMap = () => { + setIsMapActive(false); + if (window.apartamentMap) { + window.apartamentMap.behaviors.disable("scrollZoom"); + window.apartamentMap.behaviors.disable("drag"); + } + }; + + return ( + <> +
+
+

1-комн. кв. 34 м

+

+ Ул. Луначарского, Ленинский район +

+
+
+ +
+
+
+
+

+ Общая площадь +

+

72М

- - - -
- - - ); +
+
+
+
+

+ Жилая площадь +

+

68М

+
+
+
+
+
+

+ Площадь кухни +

+

11М

+
+
+
+
+
+

+ Год постройки +

+

2003

+
+
+
+
+
+

Этаж

+

7/12

+
+
+
+
+

+ Описани хаты рандомный текст Lorem Ipsum is simply dummy text of + the printing and typesetting industry. Lorem Ipsum has been the + industry's standard dummy text ever since the 1500s, when an + unknown printer took a galley of type and scrambled it to make a + type specimen book. It has survived not only five centuries, but + also the leap into electronic typesetting, remaining essentially + unchanged. It was popularised in the 1960s with the release of + Letraset sheets containing Lorem Ipsum passages, and more + recently with desktop publishing software like Aldus PageMaker + including versions of Lorem Ipsum. +

+
+
+
+

О квартире

+
    +
  • + Тип жилья: вторичный +
  • +
  • + Общая площадь: 31.9 м² +
  • +
  • + Площадь кухни: 4 м² +
  • +
  • + Жилая площадь: 21 м² +
  • +
  • + Этаж: 4 из 5 +
  • +
  • + Балкон или лоджия:{" "} + лоджия +
  • +
  • + Высота потолков: 2.5 м +
  • +
  • + Санузел: совмещенный +
  • +
  • + Вид из окон: во двор +
  • +
  • + Ремонт: косметический +
  • +
+
+
+

О доме

+
    +
  • + Год постройки: 2003 +
  • +
  • + Тип дома: панельный +
  • +
  • + Тип перекрытий:{" "} + железобетонный +
  • +
  • + Подъезды: 5 +
  • +
  • + Отопление: центральное +
  • +
  • + Аварийность: нет +
  • +
  • + Газоснобжение:{" "} + центральное +
  • +
+
+
+ +
+
+

1 234 567 ₽

+
+
+

В ипотеку от 6546823 ₽/мес

+

+ Цена за + метр............................................................45656 + ₽/м +

+

+ Условия сделки......................................свободная + продажа +

+

+ Ипотека......................................................................возможна +

+ + +
+ +
+ + { + window.apartamentMap = ref; + }} + > + + + + {/* Блокирующий оверлей, когда карта неактивна */} + {!isMapActive && ( +
+ )} + + {/* Кнопка управления картой в центре */} +
+ {!isMapActive ? ( + + ) : ( + + )} +
+ +
+ + + + ); } export { Apartament }; diff --git a/src/pages/Home/Home.jsx b/src/pages/Home/Home.jsx index aedc438..fcfae9a 100644 --- a/src/pages/Home/Home.jsx +++ b/src/pages/Home/Home.jsx @@ -1,16 +1,35 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import "./Home.scss"; -import { YMaps, Map } from "@pbe/react-yandex-maps"; +import { YMaps, Map, Placemark } from "@pbe/react-yandex-maps"; import { Header } from "../../components/Header/Header"; import { Form } from "../../components/Form/Form"; import lawyer from "../../assets/images/lawyer/Mask-group.svg"; +import { CONTACTS } from "../../config/contacts"; function Home() { + const [isMapActive, setIsMapActive] = useState(false); + useEffect(() => { - document.title = "Агентство недвижимости АЛМА-ВИД"; + document.title = CONTACTS.companyName; }, []); + const handleActivateMap = () => { + setIsMapActive(true); + if (window.homeMap) { + window.homeMap.behaviors.enable("scrollZoom"); + window.homeMap.behaviors.enable("drag"); + } + }; + + const handleDeactivateMap = () => { + setIsMapActive(false); + if (window.homeMap) { + window.homeMap.behaviors.disable("scrollZoom"); + window.homeMap.behaviors.disable("drag"); + } + }; + return ( <>
@@ -90,8 +109,118 @@ function Home() { + defaultState={{ + center: CONTACTS.coordinates, + zoom: 12, + }} + options={{ + scrollZoom: false, + drag: false, + suppressMapOpenBlock: true, + }} + instanceRef={(ref) => { + window.homeMap = ref; + }} + > + + + + {/* Блокирующий оверлей когда карта неактивна */} + {!isMapActive && ( +
+ )} + + {/* Кнопка управления картой в центре */} +
+ {!isMapActive ? ( + + ) : ( + + )} +
diff --git a/src/pages/Home/Home.scss b/src/pages/Home/Home.scss index b104ee4..1f6f351 100644 --- a/src/pages/Home/Home.scss +++ b/src/pages/Home/Home.scss @@ -120,6 +120,7 @@ .map-container { padding: 0 146px 80px 146px; + position: relative; @media (max-width: $laptopWidth) { padding: 0px 50px 80px 50px; @@ -143,29 +144,26 @@ } } - .hero-tagline { height: 100%; display: flex; - justify-content: flex-end; - padding: 100px 60px 0; + justify-content: flex-end; + padding: 100px 60px 0; p { color: #ffffff; - font-family: "Inter", sans-serif; + font-family: "Inter", sans-serif; font-size: max(50px, 3.646vw); - font-weight: 500; + font-weight: 500; line-height: 1.5; text-align: right; max-width: max(600px, min(1150px, 59.896vw)); - text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7); + text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7); } - @media (max-width: $tabletWidth) { - padding: 15px 30px 0; p { font-size: 18px; @@ -174,19 +172,16 @@ } @media (max-width: $mobileWidth) { - - order: 1; - width: 100%; - margin-top: 20px; + order: 1; + width: 100%; + margin-top: 20px; - justify-content: center; - padding: 10px 15px 0; - + padding: 10px 15px 0; p { font-size: 16px; - text-align: center; + text-align: center; max-width: 100%; } } diff --git a/src/pages/Objects/Objects.jsx b/src/pages/Objects/Objects.jsx index d754da0..e3f9c49 100644 --- a/src/pages/Objects/Objects.jsx +++ b/src/pages/Objects/Objects.jsx @@ -1,26 +1,27 @@ -import { useEffect } from 'react'; -import './Objects.scss'; +import { useEffect } from "react"; +import "./Objects.scss"; -import { Header } from '../../components/Header/Header'; -import { Form } from '../../components/Form/Form'; -import { SliderComponent } from '../../components/Sliders/SliderObjects'; +import { Header } from "../../components/Header/Header"; +import { Form } from "../../components/Form/Form"; +import { SliderComponent } from "../../components/Sliders/SliderObjects"; +import { CONTACTS } from "../../config/contacts"; function Objects() { - useEffect(() => { - document.title = 'Объекты Агентства недвижимости АЛМА-ВИД'; - }, []); + useEffect(() => { + document.title = `Объекты ${CONTACTS.companyName}`; + }, []); - return ( - <> -
-
-
- -
-
- - - ); + return ( + <> +
+
+
+ +
+
+ + + ); } export { Objects }; diff --git a/src/pages/Office/Office.jsx b/src/pages/Office/Office.jsx index 6228144..1372afe 100644 --- a/src/pages/Office/Office.jsx +++ b/src/pages/Office/Office.jsx @@ -1,76 +1,235 @@ -import { useEffect } from 'react'; -import './Office.scss'; -import { YMaps, Map } from '@pbe/react-yandex-maps'; +import { useEffect, useState } from "react"; +import "./Office.scss"; +import { YMaps, Map, Placemark } from "@pbe/react-yandex-maps"; -import { Header } from '../../components/Header/Header'; +import { Header } from "../../components/Header/Header"; +import { CONTACTS } from "../../config/contacts"; function Office() { - useEffect(() => { - document.title = 'Контакты Агентства недвижимости АЛМА-ВИД'; - }, []); - - return ( - <> -
-
-
-
АЛМА–ВИДагентство недвижимости
-
-
-
-
-
-
-

Покупка, продажа, - обмен квартир и комнат

-
-
-
-
-
-

Аренда квартир - и комнат

-
-
-
-
-
-

Новостройки

-
-
-
-
-
-

Загородная - недвижимость

-
-
-
-
-
-

Коммерческая - недвижимость

-
-
-
-
-
-
-
-

Приходите в наш офис

-
-

Адрес:Ленина, д. 60 В, оф. 701 - Вход в офис со двора

-

Телефон:+7 (351) 217-00-74→ Заказать звонок

-

E-mail:sdelka.74@yandex.ru

-

Соц.сети:ВКонтакте

+ const [isMapActive, setIsMapActive] = useState(false); + + useEffect(() => { + document.title = `Контакты ${CONTACTS.companyName}`; + }, []); + + const handleActivateMap = () => { + setIsMapActive(true); + if (window.officeMap) { + window.officeMap.behaviors.enable("scrollZoom"); + window.officeMap.behaviors.enable("drag"); + } + }; + + const handleDeactivateMap = () => { + setIsMapActive(false); + if (window.officeMap) { + window.officeMap.behaviors.disable("scrollZoom"); + window.officeMap.behaviors.disable("drag"); + } + }; + + return ( + <> +
+
+
+
+ {CONTACTS.companyNameShort}–ВИДагентство недвижимости +
+
+
+
+
+
+
+

+ Покупка, продажа, + обмен квартир и комнат +

- - - -
- - ); +
+
+
+
+

+ Аренда квартир + и комнат +

+
+
+
+
+
+

Новостройки

+
+
+
+
+
+

+ Загородная + недвижимость +

+
+
+
+
+
+

+ Коммерческая + недвижимость +

+
+
+
+
+ +
+
+

Приходите в наш офис

+
+

+ Адрес: + {CONTACTS.address.office.full} + + {CONTACTS.address.office.description} + +

+

+ Телефон: + {CONTACTS.phone.display} → + Заказать звонок +

+

+ E-mail: + {CONTACTS.email.display} +

+

+ Соц.сети: + {CONTACTS.social.vk.name} +

+
+
+ + { + window.officeMap = ref; + }} + > + + + + {/* Блокирующий оверлей, когда карта неактивна */} + {!isMapActive && ( +
+ )} + + {/* Кнопка управления картой в центре */} +
+ {!isMapActive ? ( + + ) : ( + + )} +
+ +
+
+ + ); } export { Office }; diff --git a/src/pages/Services/Services.jsx b/src/pages/Services/Services.jsx index 5079b2d..e13a551 100644 --- a/src/pages/Services/Services.jsx +++ b/src/pages/Services/Services.jsx @@ -1,119 +1,161 @@ -import { useEffect } from 'react'; -import './Services.scss'; +import { useEffect } from "react"; +import "./Services.scss"; -import { Header } from '../../components/Header/Header'; -import { Form } from '../../components/Form/Form'; +import { Header } from "../../components/Header/Header"; +import { Form } from "../../components/Form/Form"; +import { CONTACTS } from "../../config/contacts"; function Services() { - useEffect(() => { - document.title = 'Услуги Агентства недвижимости АЛМА-ВИД'; - }, []); + useEffect(() => { + document.title = `Услуги ${CONTACTS.companyName}`; + }, []); - return ( - <> -
-
-
-

Мы работаем с 2000 года и помогаем - продать и купить жилую и коммерческую недвижимость.

-

Получите бесплатную консультацию - по покупке или продаже вашей - недвижимости: Вконтакте

-
+ return ( + <> +
+
+
+

+ Мы работаем с 2000 года и помогаем продать и купить жилую и + коммерческую недвижимость. +

+

+ Получите бесплатную консультацию по покупке или продаже вашей + недвижимости:{" "} + + {CONTACTS.social.vk.name} + +

+
+
+
+

+ Почему более 3 000 семей доверили нам свою недвижимость: +

+
+
+
+

+ 1. Высокое качество работы +

+

+ Наша деятельность проверена и признана соответствующей + национальному стандарту добровольной сертификации услуг на рынке + недвижимости Российской Федерации. +

-
-

Почему более 3 000 семей доверили нам свою недвижимость:

-
-
-
-

1. Высокое качество работы

-

Наша деятельность проверена и признана - соответствующей национальному - стандарту добровольной сертификации услуг на рынке недвижимости - Российской Федерации.

-
-
-

2. Работаем в соответствии - с Законодательством РФ

-

Юридическая проверка всех сделок - и соблюдение закона о защите - персональных данных делают сделки безопасными для наших клиентов.

-
-
-

3. Эффективность

-

Широкая база объектов недвижимости, - профессиональная продажа и подбор, собственные рекламные алгоритмы, и огромный опыт - в проведении сделок любой сложности позволяют нам гарантировать, - что вы получите максимальную выгоду от работы с нами.

-
-
-

4. Выгодная процентная ставка по ипотеке

-

Мы постоянно развиваем - партнерские отношения с банками, за счет чего - имеем хорошую скидку для клиентов - и высокую степень одобрения заявок.

-
-
-
-
-

5. Квалифицированные сотрудники

-

Специалисты по недвижимости - нашей компании проходят обучение и регулярную аттестацию, а также постоянно повышают свой - профессиональный уровень. Это — обязательное условие для каждого из нас. - А значит, вы можете решить любой жилищный вопрос, воспользовавшись помощью - профессиональных риэлторов, юристов и специалистов по ипотечному кредитованию.

-
-
-

6. Индивидуальный подход

-

Мы слышим и понимаем вас, делаем все, - чтобы в результате совместной работы задача, поставленная клиентом, - была решена, и он рекомендовал нас в дальнейшем.

-
-
-

7. Гарантии

-

Мы даем своим клиентам гарантии - в юридической проверке сделок.

-
-
-

- Хотите купить, - продать недвижимость или задать вопрос юристу, оставьте заявку наши специалисты - проконсультируют вас. -

-
-
-
-
-
-

Наши услуги

-
-
-
    -
  • продажа недвижимости
  • -
  • покупка недвижимости
  • -
  • ипотека
  • -
  • подбор объектов недвижимости
  • -
  • помощь в оформлении наследства
  • -
  • консультации по вопросам недвижимости
  • -
  • оформление приватизации
  • -
-
-
-
-
    -
  • юридическое сопровождение сделок
  • -
  • узаконивание перепланировок
  • -
  • оформление права собственности на дома и земельные участки
  • -
  • работа с жилищными сертификатами и субсидиями, в том числе материнским капиталом
  • -
  • составление (подготовка) юридической экспертизы на продаваемый/покупаемый - объект недвижимости
  • -
-
-
-
- - - ); +
+

+ 2. Работаем в соответствии с Законодательством РФ +

+

+ Юридическая проверка всех сделок и соблюдение закона о защите + персональных данных делают сделки безопасными для наших + клиентов. +

+
+
+

3. Эффективность

+

+ Широкая база объектов недвижимости, профессиональная продажа и + подбор, собственные рекламные алгоритмы, и огромный опыт в + проведении сделок любой сложности позволяют нам гарантировать, + что вы получите максимальную выгоду от работы с нами. +

+
+
+

+ 4. Выгодная процентная ставка по ипотеке +

+

+ Мы постоянно развиваем партнерские отношения с банками, за счет + чего имеем хорошую скидку для клиентов и высокую степень + одобрения заявок. +

+
+
+
+
+

+ 5. Квалифицированные сотрудники +

+

+ Специалисты по недвижимости нашей компании проходят обучение и + регулярную аттестацию, а также постоянно повышают свой + профессиональный уровень. Это — обязательное условие для каждого + из нас. А значит, вы можете решить любой жилищный вопрос, + воспользовавшись помощью профессиональных риэлторов, юристов и + специалистов по ипотечному кредитованию. +

+
+
+

+ 6. Индивидуальный подход +

+

+ Мы слышим и понимаем вас, делаем все, чтобы в результате + совместной работы задача, поставленная клиентом, была решена, и + он рекомендовал нас в дальнейшем. +

+
+
+

7. Гарантии

+

+ Мы даем своим клиентам гарантии в юридической проверке сделок.{" "} +

+
+
+

+ + Хотите купить, продать недвижимость или задать вопрос юристу, + оставьте заявку наши специалисты проконсультируют вас. + +

+
+
+
+
+
+

Наши услуги

+
+
+
    +
  • продажа недвижимости
  • +
  • покупка недвижимости
  • +
  • ипотека
  • +
  • + подбор объектов недвижимости +
  • +
  • + помощь в оформлении наследства +
  • +
  • консультации по вопросам недвижимости
  • +
  • оформление приватизации
  • +
+
+
+
+
    +
  • юридическое сопровождение сделок
  • +
  • узаконивание перепланировок
  • +
  • + оформление права собственности на дома и земельные участки +
  • +
  • + работа с жилищными сертификатами и субсидиями, в том числе + материнским капиталом +
  • +
  • + составление (подготовка) юридической экспертизы на + продаваемый/покупаемый объект недвижимости +
  • +
+
+
+
+ + + ); } export { Services };