mirror of
https://github.com/danilt2000/Alma-vid.git
synced 2025-12-08 19:29:26 +02:00
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.
This commit is contained in:
122
CONTACTS_CONFIG.md
Normal file
122
CONTACTS_CONFIG.md
Normal file
@@ -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';
|
||||
|
||||
// Использование телефона
|
||||
<a href={CONTACTS.phone.link}>{CONTACTS.phone.display}</a>
|
||||
|
||||
// Использование email
|
||||
<a href={CONTACTS.email.link}>{CONTACTS.email.display}</a>
|
||||
|
||||
// Использование соцсетей
|
||||
<a href={CONTACTS.social.vk.url}>{CONTACTS.social.vk.name}</a>
|
||||
|
||||
// Координаты для карт
|
||||
<Map defaultState={{ center: CONTACTS.coordinates, zoom: 12 }}>
|
||||
<Placemark
|
||||
geometry={CONTACTS.coordinates}
|
||||
properties={{
|
||||
balloonContent: CONTACTS.getBalloonContent(),
|
||||
hintContent: `Офис ${CONTACTS.companyNameShort}`
|
||||
}}
|
||||
/>
|
||||
</Map>
|
||||
```
|
||||
|
||||
### С помощью хука useContacts
|
||||
|
||||
```jsx
|
||||
import { useContacts } from "../../hooks/useContacts";
|
||||
|
||||
function MyComponent() {
|
||||
const contacts = useContacts();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<a href={contacts.getPhoneLink()}>{contacts.phone.display}</a>
|
||||
<a href={contacts.getSocialLink("vk")}>{contacts.getSocialName("vk")}</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Компонент ContactInfo
|
||||
|
||||
```jsx
|
||||
import { ContactInfo } from '../../components/ContactInfo/ContactInfo';
|
||||
|
||||
// Базовый вариант
|
||||
<ContactInfo />
|
||||
|
||||
// Inline вариант
|
||||
<ContactInfo variant="inline" />
|
||||
|
||||
// Полный вариант со всей информацией
|
||||
<ContactInfo variant="full" />
|
||||
```
|
||||
|
||||
## Обновленные страницы
|
||||
|
||||
Все следующие страницы теперь используют централизованную конфигурацию:
|
||||
|
||||
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. Изменения автоматически применятся ко всем страницам
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -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": {
|
||||
|
||||
11
src/assets/images/logo/AlmaVid-Logo.svg
Normal file
11
src/assets/images/logo/AlmaVid-Logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 50 KiB |
78
src/components/ContactInfo/ContactInfo.jsx
Normal file
78
src/components/ContactInfo/ContactInfo.jsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { CONTACTS } from "../../config/contacts";
|
||||
import "./ContactInfo.scss";
|
||||
|
||||
function ContactInfo({ variant = "default" }) {
|
||||
if (variant === "inline") {
|
||||
return (
|
||||
<div className="contact-info-inline">
|
||||
<span className="contact-info__phone">
|
||||
<a href={CONTACTS.phone.link}>{CONTACTS.phone.display}</a>
|
||||
</span>
|
||||
<span className="contact-info__email">
|
||||
<a href={CONTACTS.email.link}>{CONTACTS.email.display}</a>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (variant === "full") {
|
||||
return (
|
||||
<div className="contact-info-full">
|
||||
<div className="contact-info__item">
|
||||
<span className="contact-info__label">Телефон:</span>
|
||||
<a href={CONTACTS.phone.link} className="contact-info__value">
|
||||
{CONTACTS.phone.display}
|
||||
</a>
|
||||
</div>
|
||||
<div className="contact-info__item">
|
||||
<span className="contact-info__label">E-mail:</span>
|
||||
<a href={CONTACTS.email.link} className="contact-info__value">
|
||||
{CONTACTS.email.display}
|
||||
</a>
|
||||
</div>
|
||||
<div className="contact-info__item">
|
||||
<span className="contact-info__label">Адрес:</span>
|
||||
<span className="contact-info__value">
|
||||
{CONTACTS.address.main.full}
|
||||
</span>
|
||||
</div>
|
||||
<div className="contact-info__item">
|
||||
<span className="contact-info__label">Соц.сети:</span>
|
||||
<div className="contact-info__social">
|
||||
<a
|
||||
href={CONTACTS.social.vk.url}
|
||||
className="contact-info__social-link"
|
||||
>
|
||||
{CONTACTS.social.vk.name}
|
||||
</a>
|
||||
<a
|
||||
href={CONTACTS.social.telegram.url}
|
||||
className="contact-info__social-link"
|
||||
>
|
||||
{CONTACTS.social.telegram.name}
|
||||
</a>
|
||||
<a
|
||||
href={CONTACTS.social.instagram.url}
|
||||
className="contact-info__social-link"
|
||||
>
|
||||
{CONTACTS.social.instagram.name}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="contact-info">
|
||||
<a href={CONTACTS.phone.link} className="contact-info__phone">
|
||||
{CONTACTS.phone.display}
|
||||
</a>
|
||||
<a href={CONTACTS.email.link} className="contact-info__email">
|
||||
{CONTACTS.email.display}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { ContactInfo };
|
||||
72
src/components/ContactInfo/ContactInfo.scss
Normal file
72
src/components/ContactInfo/ContactInfo.scss
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<footer className="footer">
|
||||
<Link to="/" className="footer__logo font-inter-regular"
|
||||
onClick={() => handlePageChange('/')}>OOO "ALMA-VID"</Link>
|
||||
<div className="footer-info font-inter-bold">
|
||||
<Link to="/objects" onClick={() => handlePageChange('/objects')}>Объекты</Link>
|
||||
<Link to="/services" onClick={() => handlePageChange('/services')}>Услуги</Link>
|
||||
<Link to="/about" onClick={() => handlePageChange('/about')}>О компании</Link>
|
||||
<Link to="/office" onClick={() => handlePageChange('/office')}>Контакты</Link>
|
||||
</div>
|
||||
<div className="footer-socials">
|
||||
<a className="footer-socials__tg" href="#"></a>
|
||||
<a className="footer-socials__inst" href="#"></a>
|
||||
<a className="footer-socials__vk" href="#"></a>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
return (
|
||||
<footer className="footer">
|
||||
<Link
|
||||
to="/"
|
||||
className="footer__logo font-inter-regular"
|
||||
onClick={() => handlePageChange("/")}
|
||||
>
|
||||
OOO "ALMA-VID"
|
||||
</Link>
|
||||
<div className="footer-info font-inter-bold">
|
||||
<Link to="/objects" onClick={() => handlePageChange("/objects")}>
|
||||
Объекты
|
||||
</Link>
|
||||
<Link to="/services" onClick={() => handlePageChange("/services")}>
|
||||
Услуги
|
||||
</Link>
|
||||
<Link to="/about" onClick={() => handlePageChange("/about")}>
|
||||
О компании
|
||||
</Link>
|
||||
<Link to="/office" onClick={() => handlePageChange("/office")}>
|
||||
Контакты
|
||||
</Link>
|
||||
</div>
|
||||
<div className="footer-socials">
|
||||
<a
|
||||
className="footer-socials__tg"
|
||||
href={CONTACTS.social.telegram.url}
|
||||
></a>
|
||||
<a
|
||||
className="footer-socials__inst"
|
||||
href={CONTACTS.social.instagram.url}
|
||||
></a>
|
||||
<a className="footer-socials__vk" href={CONTACTS.social.vk.url}></a>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
export { Footer };
|
||||
|
||||
@@ -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 (
|
||||
<section className="consultation-form font-inter-regular">
|
||||
<div className="consultation-form__info form-info">
|
||||
<h2 className="form-info__title font-inter-bold">
|
||||
Решили купить или продать квартиру?
|
||||
<span>Закажите бесплатную консультацию</span>
|
||||
</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<p className="form__title">Заполните форму ниже</p>
|
||||
<p>Мы позвоним вам в ближайшее время</p>
|
||||
<div>
|
||||
<input
|
||||
className="font-inter-regular"
|
||||
type="text"
|
||||
value={name}
|
||||
placeholder="Ваше имя"
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
className="font-inter-regular"
|
||||
type="tel"
|
||||
value={phone}
|
||||
placeholder="+7(800)555-35-35"
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
className="form-btn font-inter-regular"
|
||||
type="submit"
|
||||
>
|
||||
Записаться на консультацию
|
||||
</button>
|
||||
</div>
|
||||
<p>
|
||||
Заполняя форму, вы соглашаетесь с политикой
|
||||
конфиденциальности
|
||||
</p>
|
||||
</form>
|
||||
<div className="que-form">
|
||||
<div className={`arrow ${isVisible ? 'arrow__visible' : ''}`} onClick={scrollToTop}></div>
|
||||
<p className="font-inter-bold">
|
||||
У вас остались вопросы? <span>Напишите нам, мы онлайн!</span>
|
||||
</p>
|
||||
<img className="que__img" src={PencilIcon} alt="pencil" />
|
||||
</div>
|
||||
// Форматируем номер
|
||||
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 (
|
||||
<section className="consultation-form font-inter-regular">
|
||||
<div className="consultation-form__info form-info">
|
||||
<h2 className="form-info__title font-inter-bold">
|
||||
Решили купить или продать квартиру?
|
||||
<span>Закажите бесплатную консультацию</span>
|
||||
</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<p className="form__title">Заполните форму ниже</p>
|
||||
<p>Мы позвоним вам в ближайшее время</p>
|
||||
|
||||
{submitStatus === "success" && (
|
||||
<div
|
||||
style={{
|
||||
color: "green",
|
||||
marginBottom: "15px",
|
||||
padding: "10px",
|
||||
backgroundColor: "#d4edda",
|
||||
border: "1px solid #c3e6cb",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
✅ Спасибо! Ваша заявка отправлена. Мы свяжемся с вами в ближайшее
|
||||
время.
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
)}
|
||||
|
||||
{submitStatus === "error" && (
|
||||
<div
|
||||
style={{
|
||||
color: "red",
|
||||
marginBottom: "15px",
|
||||
padding: "10px",
|
||||
backgroundColor: "#f8d7da",
|
||||
border: "1px solid #f5c6cb",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
❌ Ошибка при отправке. Пожалуйста, проверьте данные и попробуйте
|
||||
снова.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<input
|
||||
className="font-inter-regular"
|
||||
type="text"
|
||||
value={name}
|
||||
placeholder="Ваше имя"
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<input
|
||||
className="font-inter-regular"
|
||||
type="tel"
|
||||
value={phone}
|
||||
placeholder="+7(800)555-35-35"
|
||||
onChange={handlePhoneChange}
|
||||
required
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<button
|
||||
className="form-btn font-inter-regular"
|
||||
type="submit"
|
||||
disabled={isLoading || !name.trim() || !phone.trim()}
|
||||
style={{
|
||||
opacity: isLoading ? 0.7 : 1,
|
||||
cursor: isLoading ? "not-allowed" : "pointer",
|
||||
}}
|
||||
>
|
||||
{isLoading ? "Отправляем..." : "Записаться на консультацию"}
|
||||
</button>
|
||||
</div>
|
||||
<p>Заполняя форму, вы соглашаетесь с политикой конфиденциальности</p>
|
||||
</form>
|
||||
<div className="que-form">
|
||||
<div
|
||||
className={`arrow ${isVisible ? "arrow__visible" : ""}`}
|
||||
onClick={scrollToTop}
|
||||
></div>
|
||||
<p className="font-inter-bold">
|
||||
У вас остались вопросы? <span>Напишите нам, мы онлайн!</span>
|
||||
</p>
|
||||
<img className="que__img" src={PencilIcon} alt="pencil" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export { Form };
|
||||
|
||||
@@ -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 (
|
||||
<header className={`header font-inter-bold ${isGradientBackground ? 'header_gradient' : ''}`}>
|
||||
<Link className="header__logo" to="/"></Link>
|
||||
{isHomePage && <div className="header__text"></div>}
|
||||
<div className="burgerMenu">
|
||||
<a href="tel:+73512170074" className="header-info__call">8(351)217-00-74</a>
|
||||
<div className="burger-item">
|
||||
<input id="toggle" type="checkbox" checked={isOpen} onChange={handleToggle}></input>
|
||||
<label for="toggle" className={`hamburger ${isOpen ? 'open' : ''}`}>
|
||||
<div class="top-bun"></div>
|
||||
<div class="meat"></div>
|
||||
<div class="bottom-bun"></div>
|
||||
</label>
|
||||
<div class="nav">
|
||||
<div class="nav-wrapper">
|
||||
<nav className="nav-container">
|
||||
<Link to="/objects" className="header__link">
|
||||
Объекты
|
||||
</Link>
|
||||
<Link to="/services" className="header__link">
|
||||
Услуги
|
||||
</Link>
|
||||
<Link to="/about" className="header__link">
|
||||
О компании
|
||||
</Link>
|
||||
<Link to="/office" className="header__link" href="#">
|
||||
Наш офис
|
||||
</Link>
|
||||
<p>Челябинск</p>
|
||||
<div className="header-socials__links">
|
||||
<a className="header-socials__tg" href="#"></a>
|
||||
<a className="header-socials__inst" href="#"></a>
|
||||
<a className="header-socials__vk" href="#"></a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav className="header__nav">
|
||||
const location = useLocation();
|
||||
const isGradientBackground = location.pathname === "/apartament";
|
||||
const isHomePage = location.pathname === "/";
|
||||
return (
|
||||
<header
|
||||
className={`header font-inter-bold ${
|
||||
isGradientBackground ? "header_gradient" : ""
|
||||
}`}
|
||||
>
|
||||
<Link className="header__logo" to="/"></Link>
|
||||
{isHomePage && <div className="header__text"></div>}
|
||||
<div className="burgerMenu">
|
||||
<a href={CONTACTS.phone.link} className="header-info__call">
|
||||
{CONTACTS.phone.display}
|
||||
</a>
|
||||
<div className="burger-item">
|
||||
<input
|
||||
id="toggle"
|
||||
type="checkbox"
|
||||
checked={isOpen}
|
||||
onChange={handleToggle}
|
||||
></input>
|
||||
<label for="toggle" className={`hamburger ${isOpen ? "open" : ""}`}>
|
||||
<div class="top-bun"></div>
|
||||
<div class="meat"></div>
|
||||
<div class="bottom-bun"></div>
|
||||
</label>
|
||||
<div class="nav">
|
||||
<div class="nav-wrapper">
|
||||
<nav className="nav-container">
|
||||
<Link to="/objects" className="header__link">
|
||||
Объекты
|
||||
Объекты
|
||||
</Link>
|
||||
<Link to="/services" className="header__link">
|
||||
Услуги
|
||||
Услуги
|
||||
</Link>
|
||||
<Link to="/about" className="header__link">
|
||||
О компании
|
||||
О компании
|
||||
</Link>
|
||||
<Link to="/office" className="header__link" href="#">
|
||||
Наш офис
|
||||
Наш офис
|
||||
</Link>
|
||||
</nav>
|
||||
<div className="header-info">
|
||||
<a href="tel:+73512170074" className="header-info__call">8(351)217-00-74</a>
|
||||
<button className="header-info__btn" type="button">
|
||||
Обратный звонок
|
||||
</button>
|
||||
</div>
|
||||
<div className="header-socials">
|
||||
<span className="header-socials__location">Челябинск</span>
|
||||
<p>{CONTACTS.address.main.city}</p>
|
||||
<div className="header-socials__links">
|
||||
<a className="header-socials__tg" href="#"></a>
|
||||
<a className="header-socials__inst" href="#"></a>
|
||||
<a className="header-socials__vk" href="#"></a>
|
||||
<a
|
||||
className="header-socials__tg"
|
||||
href={CONTACTS.social.telegram.url}
|
||||
></a>
|
||||
<a
|
||||
className="header-socials__inst"
|
||||
href={CONTACTS.social.instagram.url}
|
||||
></a>
|
||||
<a
|
||||
className="header-socials__vk"
|
||||
href={CONTACTS.social.vk.url}
|
||||
></a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav className="header__nav">
|
||||
<Link to="/objects" className="header__link">
|
||||
Объекты
|
||||
</Link>
|
||||
<Link to="/services" className="header__link">
|
||||
Услуги
|
||||
</Link>
|
||||
<Link to="/about" className="header__link">
|
||||
О компании
|
||||
</Link>
|
||||
<Link to="/office" className="header__link" href="#">
|
||||
Наш офис
|
||||
</Link>
|
||||
</nav>
|
||||
<div className="header-info">
|
||||
<a href={CONTACTS.phone.link} className="header-info__call">
|
||||
{CONTACTS.phone.display}
|
||||
</a>
|
||||
<button className="header-info__btn" type="button">
|
||||
Обратный звонок
|
||||
</button>
|
||||
</div>
|
||||
<div className="header-socials">
|
||||
<span className="header-socials__location">
|
||||
{CONTACTS.address.main.city}
|
||||
</span>
|
||||
<div className="header-socials__links">
|
||||
<a
|
||||
className="header-socials__tg"
|
||||
href={CONTACTS.social.telegram.url}
|
||||
></a>
|
||||
<a
|
||||
className="header-socials__inst"
|
||||
href={CONTACTS.social.instagram.url}
|
||||
></a>
|
||||
<a className="header-socials__vk" href={CONTACTS.social.vk.url}></a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export { Header };
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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: <PrevArrow />,
|
||||
};
|
||||
|
||||
// Если загружаем данные, показываем статические объекты как fallback
|
||||
if (loading || objects.length === 0) {
|
||||
return (
|
||||
<div className="slider-objects">
|
||||
<Slider {...{...settings, dots: false, infinite: true}}>
|
||||
<Link className="objects-link" to="/apartament">
|
||||
<Object
|
||||
image={objectPicOne}
|
||||
price="1 234 567 ₽"
|
||||
desc="1-комн. кв. 34 м"
|
||||
address="Ул. Луначарского, Ленинский район"
|
||||
/>
|
||||
</Link>
|
||||
<Object
|
||||
image={objectPicTwo}
|
||||
price="1 234 567 ₽"
|
||||
desc="1-комн. кв. 34 м"
|
||||
address="Ул. Зари, Вагонка"
|
||||
/>
|
||||
<Object
|
||||
image={objectPicThree}
|
||||
price="1 234 567 ₽"
|
||||
desc="1-комн. кв. 34 м"
|
||||
address="Ул. Солнечная, Заречный район"
|
||||
/>
|
||||
</Slider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="slider-objects">
|
||||
<Slider {...settings}>
|
||||
<Link className="objects-link" to="/apartament">
|
||||
<Object
|
||||
image={objectPicOne}
|
||||
price="1 234 567 ₽"
|
||||
desc="1-комн. кв. 34 м"
|
||||
address="Ул. Луначарского, Ленинский район"
|
||||
/></Link>
|
||||
<Object
|
||||
image={objectPicTwo}
|
||||
price="1 234 567 ₽"
|
||||
desc="1-комн. кв. 34 м"
|
||||
address="Ул. Зари, Вагонка"
|
||||
/>
|
||||
<Object
|
||||
image={objectPicThree}
|
||||
price="1 234 567 ₽"
|
||||
desc="1-комн. кв. 34 м"
|
||||
address="Ул. Солнечная, Заречный район"
|
||||
/>
|
||||
<Object
|
||||
image={objectPicOne}
|
||||
price="1 234 567 ₽"
|
||||
desc="1-комн. кв. 34 м"
|
||||
address="Ул. Луначарского, Ленинский район"
|
||||
/>
|
||||
<Object
|
||||
image={objectPicTwo}
|
||||
price="1 234 567 ₽"
|
||||
desc="1-комн. кв. 34 м"
|
||||
address="Ул. Зари, Вагонка"
|
||||
/>
|
||||
<Object
|
||||
image={objectPicThree}
|
||||
price="1 234 567 ₽"
|
||||
desc="1-комн. кв. 34 м"
|
||||
address="Ул. Солнечная, Заречный район"
|
||||
/>
|
||||
{objects.map((object) => (
|
||||
<Link key={object.id} className="objects-link" to={`/apartament/${object.id}`}>
|
||||
<Object
|
||||
image={object.photoUrl}
|
||||
price={object.price}
|
||||
desc={object.title}
|
||||
address={object.address}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
</Slider>
|
||||
</div>
|
||||
);
|
||||
|
||||
70
src/config/contacts.js
Normal file
70
src/config/contacts.js
Normal file
@@ -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 `
|
||||
<div>
|
||||
<strong>${CONTACTS.companyName}</strong><br/>
|
||||
Адрес: ${CONTACTS.address.main.street}<br/>
|
||||
${CONTACTS.address.main.city}, ${CONTACTS.address.main.postalCode}<br/>
|
||||
Тел: ${CONTACTS.phone.display}
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
};
|
||||
|
||||
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}`;
|
||||
},
|
||||
};
|
||||
15
src/hooks/useContacts.js
Normal file
15
src/hooks/useContacts.js
Normal file
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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 (
|
||||
<>
|
||||
<div className="wrapper-about">
|
||||
<Header />
|
||||
<section className="about">
|
||||
<div className="about-inner">
|
||||
<div className="about__info font-inter-bold">
|
||||
<p>Каждый из нас хоть раз сталкивается с квартирным вопросом - покупка, продажа,
|
||||
обмен квартиры или дома - эти процессы требуют серьезного профессионального подхода
|
||||
и юридической грамотности.</p>
|
||||
<p>Агентство недвижимости АЛМА-ВИД существует с 2000 года — за этот период мы обрели
|
||||
доверие и уважение наших многочисленных клиентов.</p>
|
||||
<p>Мы находимся в постоянном развитии и оттачиваем профессионализм наших сотрудников,
|
||||
обладая серьезной материальной базой и налаженными коммуникациями с крупными банками.
|
||||
Благодаря этому мы имеем возможность предоставлять услуги
|
||||
в сфере недвижимости высокого качества.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="about__title font-inter-semibold">Об Агентстве недвижимости <span>АЛМА-Вид</span></div>
|
||||
</section>
|
||||
return (
|
||||
<>
|
||||
<div className="wrapper-about">
|
||||
<Header />
|
||||
<section className="about">
|
||||
<div className="about-inner">
|
||||
<div className="about__info font-inter-bold">
|
||||
<p>
|
||||
Каждый из нас хоть раз сталкивается с квартирным вопросом -
|
||||
покупка, продажа, обмен квартиры или дома - эти процессы требуют
|
||||
серьезного профессионального подхода и юридической грамотности.
|
||||
</p>
|
||||
<p>
|
||||
Агентство недвижимости АЛМА-ВИД существует с 2000 года — за этот
|
||||
период мы обрели доверие и уважение наших многочисленных
|
||||
клиентов.
|
||||
</p>
|
||||
<p>
|
||||
Мы находимся в постоянном развитии и оттачиваем профессионализм
|
||||
наших сотрудников, обладая серьезной материальной базой и
|
||||
налаженными коммуникациями с крупными банками. Благодаря этому
|
||||
мы имеем возможность предоставлять услуги в сфере недвижимости
|
||||
высокого качества.
|
||||
</p>
|
||||
</div>
|
||||
<section className="certificates">
|
||||
<SliderComponent/>
|
||||
</section>
|
||||
<Form scrolledThreshold={1650}/>
|
||||
</>
|
||||
);
|
||||
</div>
|
||||
<div className="about__title font-inter-semibold">
|
||||
Об {CONTACTS.companyName} <span>{CONTACTS.companyNameShort}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<section className="certificates">
|
||||
<SliderComponent />
|
||||
</section>
|
||||
<Form scrolledThreshold={1650} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { About };
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<Header />
|
||||
<section className="apartament">
|
||||
<h2 className="apartament__title font-inter-bold">1-комн. кв. 34 м</h2>
|
||||
<p className="apartament__para font-inter-regular">Ул. Луначарского, Ленинский район</p>
|
||||
<div className="apartament__container">
|
||||
<div className="apartament__info">
|
||||
<Gallery/>
|
||||
<div className="apartament__icons icons">
|
||||
<div className="icons__item">
|
||||
<div className="icons__pic icons__pic_area"></div>
|
||||
<div className="icons__info">
|
||||
<p className="icons__desc font-inter-regular ">Общая площадь</p>
|
||||
<p className="icons__num font-inter-semibold">72М</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icons__item">
|
||||
<div className="icons__pic icons__pic_living"></div>
|
||||
<div className="icons__info">
|
||||
<p className="icons__desc font-inter-regular ">Жилая площадь</p>
|
||||
<p className="icons__num font-inter-semibold">68М</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icons__item">
|
||||
<div className="icons__pic icons__pic_kitchen"></div>
|
||||
<div className="icons__info">
|
||||
<p className="icons__desc font-inter-regular ">Площадь кухни</p>
|
||||
<p className="icons__num font-inter-semibold">11М</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icons__item">
|
||||
<div className="icons__pic icons__pic_year"></div>
|
||||
<div className="icons__info">
|
||||
<p className="icons__desc font-inter-regular ">Год постройки</p>
|
||||
<p className="icons__num font-inter-semibold">2003</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icons__item">
|
||||
<div className="icons__pic icons__pic__floor"></div>
|
||||
<div className="icons__info">
|
||||
<p className="icons__desc font-inter-regular ">Этаж</p>
|
||||
<p className="icons__num font-inter-semibold">7/12</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apartament__text font-inter-regular">
|
||||
<p>Описани хаты рандомный текст 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.</p>
|
||||
</div>
|
||||
<div className="apartament__desc font-inter-regular">
|
||||
<div className="apartament__flat flat">
|
||||
<p className="flat__title font-inter-bold">О квартире</p>
|
||||
<ul>
|
||||
<li><span className="title-color">Тип жилья:</span> вторичный</li>
|
||||
<li><span className="title-color">Общая площадь:</span> 31.9 м²</li>
|
||||
<li><span className="title-color">Площадь кухни:</span> 4 м²</li>
|
||||
<li><span className="title-color">Жилая площадь:</span> 21 м²</li>
|
||||
<li><span className="title-color">Этаж:</span> 4 из 5</li>
|
||||
<li><span className="title-color">Балкон или лоджия:</span> лоджия</li>
|
||||
<li><span className="title-color">Высота потолков:</span> 2.5 м</li>
|
||||
<li><span className="title-color">Санузел:</span> совмещенный</li>
|
||||
<li><span className="title-color">Вид из окон:</span> во двор</li>
|
||||
<li><span className="title-color">Ремонт:</span> косметический</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="apartament__home home">
|
||||
<p className="home__title font-inter-bold">О доме</p>
|
||||
<ul>
|
||||
<li><span className="title-color">Год постройки:</span> 2003</li>
|
||||
<li><span className="title-color">Тип дома:</span> панельный</li>
|
||||
<li><span className="title-color">Тип перекрытий:</span> железобетонный</li>
|
||||
<li><span className="title-color">Подъезды:</span> 5</li>
|
||||
<li><span className="title-color">Отопление:</span> центральное</li>
|
||||
<li><span className="title-color">Аварийность:</span> нет</li>
|
||||
<li><span className="title-color">Газоснобжение:</span> центральное</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apartament__price price font-inter-regular">
|
||||
<div className="price__container">
|
||||
<p className="price__total font-inter-bold">1 234 567 ₽</p>
|
||||
<div className="price__logo"></div>
|
||||
</div>
|
||||
<p className="price__desc">В ипотеку от 6546823 ₽/мес</p>
|
||||
<p className="price__meter">Цена за метр............................................................45656 ₽/м</p>
|
||||
<p className="price__conditions">Условия сделки......................................свободная продажа</p>
|
||||
<p className="price__mortgage">Ипотека......................................................................возможна</p>
|
||||
<button className="apartament__btn_number font-inter-bold" type="button">Показать телефон</button>
|
||||
<button className="apartament__btn_write font-inter-bold" type="button">Написать</button>
|
||||
</div>
|
||||
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 (
|
||||
<>
|
||||
<Header />
|
||||
<section className="apartament">
|
||||
<h2 className="apartament__title font-inter-bold">1-комн. кв. 34 м</h2>
|
||||
<p className="apartament__para font-inter-regular">
|
||||
Ул. Луначарского, Ленинский район
|
||||
</p>
|
||||
<div className="apartament__container">
|
||||
<div className="apartament__info">
|
||||
<Gallery />
|
||||
<div className="apartament__icons icons">
|
||||
<div className="icons__item">
|
||||
<div className="icons__pic icons__pic_area"></div>
|
||||
<div className="icons__info">
|
||||
<p className="icons__desc font-inter-regular ">
|
||||
Общая площадь
|
||||
</p>
|
||||
<p className="icons__num font-inter-semibold">72М</p>
|
||||
</div>
|
||||
<YMaps>
|
||||
<Map className="map-apartament" defaultState={{ center: [55.16, 61.4], zoom: 15 }} />
|
||||
</YMaps>
|
||||
</section>
|
||||
<Form scrolledThreshold={2350}/>
|
||||
</>
|
||||
);
|
||||
</div>
|
||||
<div className="icons__item">
|
||||
<div className="icons__pic icons__pic_living"></div>
|
||||
<div className="icons__info">
|
||||
<p className="icons__desc font-inter-regular ">
|
||||
Жилая площадь
|
||||
</p>
|
||||
<p className="icons__num font-inter-semibold">68М</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icons__item">
|
||||
<div className="icons__pic icons__pic_kitchen"></div>
|
||||
<div className="icons__info">
|
||||
<p className="icons__desc font-inter-regular ">
|
||||
Площадь кухни
|
||||
</p>
|
||||
<p className="icons__num font-inter-semibold">11М</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icons__item">
|
||||
<div className="icons__pic icons__pic_year"></div>
|
||||
<div className="icons__info">
|
||||
<p className="icons__desc font-inter-regular ">
|
||||
Год постройки
|
||||
</p>
|
||||
<p className="icons__num font-inter-semibold">2003</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icons__item">
|
||||
<div className="icons__pic icons__pic__floor"></div>
|
||||
<div className="icons__info">
|
||||
<p className="icons__desc font-inter-regular ">Этаж</p>
|
||||
<p className="icons__num font-inter-semibold">7/12</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apartament__text font-inter-regular">
|
||||
<p>
|
||||
Описани хаты рандомный текст 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.
|
||||
</p>
|
||||
</div>
|
||||
<div className="apartament__desc font-inter-regular">
|
||||
<div className="apartament__flat flat">
|
||||
<p className="flat__title font-inter-bold">О квартире</p>
|
||||
<ul>
|
||||
<li>
|
||||
<span className="title-color">Тип жилья:</span> вторичный
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Общая площадь:</span> 31.9 м²
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Площадь кухни:</span> 4 м²
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Жилая площадь:</span> 21 м²
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Этаж:</span> 4 из 5
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Балкон или лоджия:</span>{" "}
|
||||
лоджия
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Высота потолков:</span> 2.5 м
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Санузел:</span> совмещенный
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Вид из окон:</span> во двор
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Ремонт:</span> косметический
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="apartament__home home">
|
||||
<p className="home__title font-inter-bold">О доме</p>
|
||||
<ul>
|
||||
<li>
|
||||
<span className="title-color">Год постройки:</span> 2003
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Тип дома:</span> панельный
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Тип перекрытий:</span>{" "}
|
||||
железобетонный
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Подъезды:</span> 5
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Отопление:</span> центральное
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Аварийность:</span> нет
|
||||
</li>
|
||||
<li>
|
||||
<span className="title-color">Газоснобжение:</span>{" "}
|
||||
центральное
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apartament__price price font-inter-regular">
|
||||
<div className="price__container">
|
||||
<p className="price__total font-inter-bold">1 234 567 ₽</p>
|
||||
<div className="price__logo"></div>
|
||||
</div>
|
||||
<p className="price__desc">В ипотеку от 6546823 ₽/мес</p>
|
||||
<p className="price__meter">
|
||||
Цена за
|
||||
метр............................................................45656
|
||||
₽/м
|
||||
</p>
|
||||
<p className="price__conditions">
|
||||
Условия сделки......................................свободная
|
||||
продажа
|
||||
</p>
|
||||
<p className="price__mortgage">
|
||||
Ипотека......................................................................возможна
|
||||
</p>
|
||||
<button
|
||||
className="apartament__btn_number font-inter-bold"
|
||||
type="button"
|
||||
>
|
||||
Показать телефон
|
||||
</button>
|
||||
<button
|
||||
className="apartament__btn_write font-inter-bold"
|
||||
type="button"
|
||||
>
|
||||
Написать
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ position: "relative" }}>
|
||||
<YMaps>
|
||||
<Map
|
||||
className="map-apartament"
|
||||
defaultState={{
|
||||
center: CONTACTS.coordinates,
|
||||
zoom: 15,
|
||||
}}
|
||||
options={{
|
||||
scrollZoom: false,
|
||||
drag: false,
|
||||
suppressMapOpenBlock: true,
|
||||
}}
|
||||
instanceRef={(ref) => {
|
||||
window.apartamentMap = ref;
|
||||
}}
|
||||
>
|
||||
<Placemark
|
||||
geometry={CONTACTS.coordinates}
|
||||
properties={{
|
||||
balloonContent: CONTACTS.getBalloonContent(),
|
||||
hintContent: `Офис ${CONTACTS.companyNameShort}`,
|
||||
}}
|
||||
options={{
|
||||
preset: "islands#redDotIcon",
|
||||
}}
|
||||
/>
|
||||
</Map>
|
||||
|
||||
{/* Блокирующий оверлей, когда карта неактивна */}
|
||||
{!isMapActive && (
|
||||
<div
|
||||
onClick={handleActivateMap}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
||||
zIndex: 999,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Кнопка управления картой в центре */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "85%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
{!isMapActive ? (
|
||||
<button
|
||||
onClick={handleActivateMap}
|
||||
style={{
|
||||
background: "rgba(0, 123, 255, 0.9)",
|
||||
border: "none",
|
||||
borderRadius: "8px",
|
||||
padding: "15px 25px",
|
||||
fontSize: "16px",
|
||||
fontFamily: "Inter, Arial, sans-serif",
|
||||
cursor: "pointer",
|
||||
boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
minWidth: "140px",
|
||||
textAlign: "center",
|
||||
transition: "all 0.3s ease",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.target.style.background = "rgba(0, 123, 255, 1)";
|
||||
e.target.style.transform = "scale(1.05)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.target.style.background = "rgba(0, 123, 255, 0.9)";
|
||||
e.target.style.transform = "scale(1)";
|
||||
}}
|
||||
>
|
||||
📍 Посмотреть карту
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleDeactivateMap}
|
||||
style={{
|
||||
background: "rgba(220, 53, 69, 0.9)",
|
||||
border: "none",
|
||||
borderRadius: "6px",
|
||||
padding: "10px 20px",
|
||||
fontSize: "14px",
|
||||
fontFamily: "Inter, Arial, sans-serif",
|
||||
cursor: "pointer",
|
||||
boxShadow: "0 3px 8px rgba(0,0,0,0.3)",
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
minWidth: "120px",
|
||||
textAlign: "center",
|
||||
transition: "all 0.3s ease",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.target.style.background = "rgba(220, 53, 69, 1)";
|
||||
e.target.style.transform = "scale(1.05)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.target.style.background = "rgba(220, 53, 69, 0.9)";
|
||||
e.target.style.transform = "scale(1)";
|
||||
}}
|
||||
>
|
||||
✕ Закрыть карту
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</YMaps>
|
||||
</div>
|
||||
</section>
|
||||
<Form scrolledThreshold={2350} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { Apartament };
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<div className="wrapper">
|
||||
@@ -90,8 +109,118 @@ function Home() {
|
||||
<YMaps>
|
||||
<Map
|
||||
className="map"
|
||||
defaultState={{ center: [55.16, 61.4], zoom: 12 }}
|
||||
/>
|
||||
defaultState={{
|
||||
center: CONTACTS.coordinates,
|
||||
zoom: 12,
|
||||
}}
|
||||
options={{
|
||||
scrollZoom: false,
|
||||
drag: false,
|
||||
suppressMapOpenBlock: true,
|
||||
}}
|
||||
instanceRef={(ref) => {
|
||||
window.homeMap = ref;
|
||||
}}
|
||||
>
|
||||
<Placemark
|
||||
geometry={CONTACTS.coordinates}
|
||||
properties={{
|
||||
balloonContent: CONTACTS.getBalloonContent(),
|
||||
hintContent: `Офис ${CONTACTS.companyNameShort}`,
|
||||
}}
|
||||
options={{
|
||||
preset: "islands#redDotIcon",
|
||||
}}
|
||||
/>
|
||||
</Map>
|
||||
|
||||
{/* Блокирующий оверлей когда карта неактивна */}
|
||||
{!isMapActive && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
||||
zIndex: 999,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={handleActivateMap}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Кнопка управления картой в центре */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "85%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
{!isMapActive ? (
|
||||
<button
|
||||
onClick={handleActivateMap}
|
||||
style={{
|
||||
background: "rgba(0, 123, 255, 0.9)",
|
||||
border: "none",
|
||||
borderRadius: "8px",
|
||||
padding: "15px 25px",
|
||||
fontSize: "16px",
|
||||
fontFamily: "Inter, Arial, sans-serif",
|
||||
cursor: "pointer",
|
||||
boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
minWidth: "140px",
|
||||
textAlign: "center",
|
||||
transition: "all 0.3s ease",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.target.style.background = "rgba(0, 123, 255, 1)";
|
||||
e.target.style.transform = "scale(1.05)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.target.style.background = "rgba(0, 123, 255, 0.9)";
|
||||
e.target.style.transform = "scale(1)";
|
||||
}}
|
||||
>
|
||||
📍 Посмотреть карту
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleDeactivateMap}
|
||||
style={{
|
||||
background: "rgba(220, 53, 69, 0.9)",
|
||||
border: "none",
|
||||
borderRadius: "6px",
|
||||
padding: "10px 20px",
|
||||
fontSize: "14px",
|
||||
fontFamily: "Inter, Arial, sans-serif",
|
||||
cursor: "pointer",
|
||||
boxShadow: "0 3px 8px rgba(0,0,0,0.3)",
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
minWidth: "120px",
|
||||
textAlign: "center",
|
||||
transition: "all 0.3s ease",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.target.style.background = "rgba(220, 53, 69, 1)";
|
||||
e.target.style.transform = "scale(1.05)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.target.style.background = "rgba(220, 53, 69, 0.9)";
|
||||
e.target.style.transform = "scale(1)";
|
||||
}}
|
||||
>
|
||||
✕ Закрыть карту
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</YMaps>
|
||||
</div>
|
||||
<Form scrolledThreshold={2750} />
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<div className="wrapper-objects">
|
||||
<Header />
|
||||
<section className="objects">
|
||||
<SliderComponent/>
|
||||
</section>
|
||||
</div>
|
||||
<Form scrolledThreshold={690}/>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<div className="wrapper-objects">
|
||||
<Header />
|
||||
<section className="objects">
|
||||
<SliderComponent />
|
||||
</section>
|
||||
</div>
|
||||
<Form scrolledThreshold={690} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { Objects };
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<div className="wrapper-office">
|
||||
<Header />
|
||||
<section className="office">
|
||||
<div className="office__title font-inter-semibold">АЛМА–ВИД<span>агентство недвижимости</span></div>
|
||||
<div className="office__separator"></div>
|
||||
<div className="office-inner">
|
||||
<div className="office-container">
|
||||
<div className="office__item">
|
||||
<div className="office__pic office__pic_purchase"></div>
|
||||
<div className="office__text">
|
||||
<p className="office__desc font-inter-bold">Покупка, продажа,
|
||||
<span>обмен квартир и комнат</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="office__item">
|
||||
<div className="office__pic office__pic_rent"></div>
|
||||
<div className="office__text">
|
||||
<p className="office__desc font-inter-bold">Аренда квартир
|
||||
<span>и комнат</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="office__item">
|
||||
<div className="office__pic office__pic_buildings"></div>
|
||||
<div className="office__text">
|
||||
<p className="office__desc font-inter-bold">Новостройки</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="office__item">
|
||||
<div className="office__pic office__pic_country"></div>
|
||||
<div className="office__text">
|
||||
<p className="office__desc font-inter-bold">Загородная
|
||||
<span>недвижимость</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="office__item">
|
||||
<div className="office__pic office__pic_commercial"></div>
|
||||
<div className="office__text">
|
||||
<p className="office__desc font-inter-bold">Коммерческая
|
||||
<span>недвижимость</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<section className="office-location font-inter-bold">
|
||||
<h2 className="office-location__title">Приходите в наш офис</h2>
|
||||
<div className="office-info">
|
||||
<p className="office-info__para_address"><span>Адрес:</span>Ленина, д. 60 В, оф. 701
|
||||
<span className="office-info__desc">Вход в офис со двора</span></p>
|
||||
<p className="office-info__para"><span>Телефон:</span>+7 (351) 217-00-74→ Заказать звонок</p>
|
||||
<p className="office-info__para"><span>E-mail:</span>sdelka.74@yandex.ru</p>
|
||||
<p className="office-info__para"><span>Соц.сети:</span><a href="#">ВКонтакте</a></p>
|
||||
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 (
|
||||
<>
|
||||
<div className="wrapper-office">
|
||||
<Header />
|
||||
<section className="office">
|
||||
<div className="office__title font-inter-semibold">
|
||||
{CONTACTS.companyNameShort}–ВИД<span>агентство недвижимости</span>
|
||||
</div>
|
||||
<div className="office__separator"></div>
|
||||
<div className="office-inner">
|
||||
<div className="office-container">
|
||||
<div className="office__item">
|
||||
<div className="office__pic office__pic_purchase"></div>
|
||||
<div className="office__text">
|
||||
<p className="office__desc font-inter-bold">
|
||||
Покупка, продажа,
|
||||
<span>обмен квартир и комнат</span>
|
||||
</p>
|
||||
</div>
|
||||
<YMaps>
|
||||
<Map className="map-office" defaultState={{ center: [55.16, 61.4], zoom: 12 }} />
|
||||
</YMaps>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
</div>
|
||||
<div className="office__item">
|
||||
<div className="office__pic office__pic_rent"></div>
|
||||
<div className="office__text">
|
||||
<p className="office__desc font-inter-bold">
|
||||
Аренда квартир
|
||||
<span>и комнат</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="office__item">
|
||||
<div className="office__pic office__pic_buildings"></div>
|
||||
<div className="office__text">
|
||||
<p className="office__desc font-inter-bold">Новостройки</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="office__item">
|
||||
<div className="office__pic office__pic_country"></div>
|
||||
<div className="office__text">
|
||||
<p className="office__desc font-inter-bold">
|
||||
Загородная
|
||||
<span>недвижимость</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="office__item">
|
||||
<div className="office__pic office__pic_commercial"></div>
|
||||
<div className="office__text">
|
||||
<p className="office__desc font-inter-bold">
|
||||
Коммерческая
|
||||
<span>недвижимость</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<section className="office-location font-inter-bold">
|
||||
<h2 className="office-location__title">Приходите в наш офис</h2>
|
||||
<div className="office-info">
|
||||
<p className="office-info__para_address">
|
||||
<span>Адрес:</span>
|
||||
{CONTACTS.address.office.full}
|
||||
<span className="office-info__desc">
|
||||
{CONTACTS.address.office.description}
|
||||
</span>
|
||||
</p>
|
||||
<p className="office-info__para">
|
||||
<span>Телефон:</span>
|
||||
<a href={CONTACTS.phone.link}>{CONTACTS.phone.display}</a> →
|
||||
Заказать звонок
|
||||
</p>
|
||||
<p className="office-info__para">
|
||||
<span>E-mail:</span>
|
||||
<a href={CONTACTS.email.link}>{CONTACTS.email.display}</a>
|
||||
</p>
|
||||
<p className="office-info__para">
|
||||
<span>Соц.сети:</span>
|
||||
<a href={CONTACTS.social.vk.url}>{CONTACTS.social.vk.name}</a>
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ position: "relative" }}>
|
||||
<YMaps>
|
||||
<Map
|
||||
className="map-office"
|
||||
defaultState={{
|
||||
center: CONTACTS.coordinates,
|
||||
zoom: 16,
|
||||
}}
|
||||
options={{
|
||||
scrollZoom: false,
|
||||
drag: false,
|
||||
suppressMapOpenBlock: true,
|
||||
}}
|
||||
instanceRef={(ref) => {
|
||||
window.officeMap = ref;
|
||||
}}
|
||||
>
|
||||
<Placemark
|
||||
geometry={CONTACTS.coordinates}
|
||||
properties={{
|
||||
balloonContent: CONTACTS.getBalloonContent(),
|
||||
hintContent: `Офис ${CONTACTS.companyNameShort}`,
|
||||
}}
|
||||
options={{
|
||||
preset: "islands#redDotIcon",
|
||||
}}
|
||||
/>
|
||||
</Map>
|
||||
|
||||
{/* Блокирующий оверлей, когда карта неактивна */}
|
||||
{!isMapActive && (
|
||||
<div
|
||||
onClick={handleActivateMap}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
||||
zIndex: 999,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Кнопка управления картой в центре */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "85%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
{!isMapActive ? (
|
||||
<button
|
||||
onClick={handleActivateMap}
|
||||
style={{
|
||||
background: "rgba(0, 123, 255, 0.9)",
|
||||
border: "none",
|
||||
borderRadius: "8px",
|
||||
padding: "15px 25px",
|
||||
fontSize: "16px",
|
||||
fontFamily: "Inter, Arial, sans-serif",
|
||||
cursor: "pointer",
|
||||
boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
minWidth: "140px",
|
||||
textAlign: "center",
|
||||
transition: "all 0.3s ease",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.target.style.background = "rgba(0, 123, 255, 1)";
|
||||
e.target.style.transform = "scale(1.05)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.target.style.background = "rgba(0, 123, 255, 0.9)";
|
||||
e.target.style.transform = "scale(1)";
|
||||
}}
|
||||
>
|
||||
📍 Посмотреть карту
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleDeactivateMap}
|
||||
style={{
|
||||
background: "rgba(220, 53, 69, 0.9)",
|
||||
border: "none",
|
||||
borderRadius: "6px",
|
||||
padding: "10px 20px",
|
||||
fontSize: "14px",
|
||||
fontFamily: "Inter, Arial, sans-serif",
|
||||
cursor: "pointer",
|
||||
boxShadow: "0 3px 8px rgba(0,0,0,0.3)",
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
minWidth: "120px",
|
||||
textAlign: "center",
|
||||
transition: "all 0.3s ease",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.target.style.background = "rgba(220, 53, 69, 1)";
|
||||
e.target.style.transform = "scale(1.05)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.target.style.background = "rgba(220, 53, 69, 0.9)";
|
||||
e.target.style.transform = "scale(1)";
|
||||
}}
|
||||
>
|
||||
✕ Закрыть карту
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</YMaps>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { Office };
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<div className="wrapper-services">
|
||||
<Header />
|
||||
<div className="services__text">
|
||||
<p className="services__title font-inter-bold">Мы работаем с 2000 года и помогаем
|
||||
продать и купить жилую и коммерческую недвижимость.</p>
|
||||
<p className="services__subtitle font-inter-bold">Получите бесплатную консультацию
|
||||
по покупке или продаже вашей
|
||||
недвижимости: <a className="services__link" href="#">Вконтакте</a></p>
|
||||
</div>
|
||||
return (
|
||||
<>
|
||||
<div className="wrapper-services">
|
||||
<Header />
|
||||
<div className="services__text">
|
||||
<p className="services__title font-inter-bold">
|
||||
Мы работаем с 2000 года и помогаем продать и купить жилую и
|
||||
коммерческую недвижимость.
|
||||
</p>
|
||||
<p className="services__subtitle font-inter-bold">
|
||||
Получите бесплатную консультацию по покупке или продаже вашей
|
||||
недвижимости:{" "}
|
||||
<a className="services__link" href={CONTACTS.social.vk.url}>
|
||||
{CONTACTS.social.vk.name}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<section className="services-info font-inter-bold">
|
||||
<h2 className="services-info__title">
|
||||
Почему более 3 000 семей доверили нам свою недвижимость:
|
||||
</h2>
|
||||
<div className="services-info__container">
|
||||
<div className="services-info__desc">
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">
|
||||
1. Высокое качество работы
|
||||
</p>
|
||||
<p className="services-info__text font-inter-regular">
|
||||
Наша деятельность проверена и признана соответствующей
|
||||
национальному стандарту добровольной сертификации услуг на рынке
|
||||
недвижимости Российской Федерации.
|
||||
</p>
|
||||
</div>
|
||||
<section className="services-info font-inter-bold">
|
||||
<h2 className="services-info__title">Почему более 3 000 семей доверили нам свою недвижимость:</h2>
|
||||
<div className="services-info__container">
|
||||
<div className="services-info__desc">
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">1. Высокое качество работы</p>
|
||||
<p className="services-info__text font-inter-regular">Наша деятельность проверена и признана
|
||||
соответствующей национальному
|
||||
стандарту добровольной сертификации услуг на рынке недвижимости
|
||||
Российской Федерации.</p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">2. Работаем в соответствии
|
||||
с Законодательством РФ</p>
|
||||
<p className="services-info__text font-inter-regular">Юридическая проверка всех сделок
|
||||
и соблюдение закона о защите
|
||||
персональных данных делают сделки безопасными для наших клиентов.</p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">3. Эффективность</p>
|
||||
<p className="services-info__text font-inter-regular">Широкая база объектов недвижимости,
|
||||
профессиональная продажа и подбор, собственные рекламные алгоритмы, и огромный опыт
|
||||
в проведении сделок любой сложности позволяют нам гарантировать,
|
||||
что вы получите максимальную выгоду от работы с нами.</p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">4. Выгодная процентная ставка по ипотеке</p>
|
||||
<p className="services-info__text font-inter-regular">Мы постоянно развиваем
|
||||
партнерские отношения с банками, за счет чего
|
||||
имеем хорошую скидку для клиентов
|
||||
и высокую степень одобрения заявок.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="services-info__desc">
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">5. Квалифицированные сотрудники</p>
|
||||
<p className="services-info__text font-inter-regular">Специалисты по недвижимости
|
||||
нашей компании проходят обучение и регулярную аттестацию, а также постоянно повышают свой
|
||||
профессиональный уровень. Это — обязательное условие для каждого из нас.
|
||||
А значит, вы можете решить любой жилищный вопрос, воспользовавшись помощью
|
||||
профессиональных риэлторов, юристов и специалистов по ипотечному кредитованию.</p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">6. Индивидуальный подход</p>
|
||||
<p className="services-info__text font-inter-regular">Мы слышим и понимаем вас, делаем все,
|
||||
чтобы в результате совместной работы задача, поставленная клиентом,
|
||||
была решена, и он рекомендовал нас в дальнейшем.</p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">7. Гарантии</p>
|
||||
<p className="services-info__text font-inter-regular">Мы даем своим клиентам гарантии
|
||||
в юридической проверке сделок. </p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__text">
|
||||
<span className="services-info__accent">Хотите купить,
|
||||
продать недвижимость или задать вопрос юристу, оставьте заявку наши специалисты
|
||||
проконсультируют вас.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="services font-inter-bold">
|
||||
<h3 className="services-list__title">Наши услуги</h3>
|
||||
<div className="services__container">
|
||||
<div className="services__list">
|
||||
<ul className="services__list_left">
|
||||
<li>продажа недвижимости</li>
|
||||
<li>покупка недвижимости</li>
|
||||
<li>ипотека</li>
|
||||
<li>подбор объектов <span>недвижимости</span></li>
|
||||
<li>помощь в оформлении <span>наследства</span></li>
|
||||
<li>консультации по вопросам недвижимости</li>
|
||||
<li>оформление приватизации</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="services__separator"></div>
|
||||
<div className="services__list">
|
||||
<ul className="services__list_right">
|
||||
<li>юридическое сопровождение сделок</li>
|
||||
<li>узаконивание перепланировок</li>
|
||||
<li>оформление права собственности на дома и земельные участки</li>
|
||||
<li>работа с жилищными сертификатами и субсидиями, в том числе материнским капиталом</li>
|
||||
<li>составление (подготовка) юридической экспертизы на продаваемый/покупаемый
|
||||
объект недвижимости</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<Form scrolledThreshold={2700}/>
|
||||
</>
|
||||
);
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">
|
||||
2. Работаем в соответствии с Законодательством РФ
|
||||
</p>
|
||||
<p className="services-info__text font-inter-regular">
|
||||
Юридическая проверка всех сделок и соблюдение закона о защите
|
||||
персональных данных делают сделки безопасными для наших
|
||||
клиентов.
|
||||
</p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">3. Эффективность</p>
|
||||
<p className="services-info__text font-inter-regular">
|
||||
Широкая база объектов недвижимости, профессиональная продажа и
|
||||
подбор, собственные рекламные алгоритмы, и огромный опыт в
|
||||
проведении сделок любой сложности позволяют нам гарантировать,
|
||||
что вы получите максимальную выгоду от работы с нами.
|
||||
</p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">
|
||||
4. Выгодная процентная ставка по ипотеке
|
||||
</p>
|
||||
<p className="services-info__text font-inter-regular">
|
||||
Мы постоянно развиваем партнерские отношения с банками, за счет
|
||||
чего имеем хорошую скидку для клиентов и высокую степень
|
||||
одобрения заявок.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="services-info__desc">
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">
|
||||
5. Квалифицированные сотрудники
|
||||
</p>
|
||||
<p className="services-info__text font-inter-regular">
|
||||
Специалисты по недвижимости нашей компании проходят обучение и
|
||||
регулярную аттестацию, а также постоянно повышают свой
|
||||
профессиональный уровень. Это — обязательное условие для каждого
|
||||
из нас. А значит, вы можете решить любой жилищный вопрос,
|
||||
воспользовавшись помощью профессиональных риэлторов, юристов и
|
||||
специалистов по ипотечному кредитованию.
|
||||
</p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">
|
||||
6. Индивидуальный подход
|
||||
</p>
|
||||
<p className="services-info__text font-inter-regular">
|
||||
Мы слышим и понимаем вас, делаем все, чтобы в результате
|
||||
совместной работы задача, поставленная клиентом, была решена, и
|
||||
он рекомендовал нас в дальнейшем.
|
||||
</p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__subtitle">7. Гарантии</p>
|
||||
<p className="services-info__text font-inter-regular">
|
||||
Мы даем своим клиентам гарантии в юридической проверке сделок.{" "}
|
||||
</p>
|
||||
</div>
|
||||
<div className="services-info__item">
|
||||
<p className="services-info__text">
|
||||
<span className="services-info__accent">
|
||||
Хотите купить, продать недвижимость или задать вопрос юристу,
|
||||
оставьте заявку наши специалисты проконсультируют вас.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="services font-inter-bold">
|
||||
<h3 className="services-list__title">Наши услуги</h3>
|
||||
<div className="services__container">
|
||||
<div className="services__list">
|
||||
<ul className="services__list_left">
|
||||
<li>продажа недвижимости</li>
|
||||
<li>покупка недвижимости</li>
|
||||
<li>ипотека</li>
|
||||
<li>
|
||||
подбор объектов <span>недвижимости</span>
|
||||
</li>
|
||||
<li>
|
||||
помощь в оформлении <span>наследства</span>
|
||||
</li>
|
||||
<li>консультации по вопросам недвижимости</li>
|
||||
<li>оформление приватизации</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="services__separator"></div>
|
||||
<div className="services__list">
|
||||
<ul className="services__list_right">
|
||||
<li>юридическое сопровождение сделок</li>
|
||||
<li>узаконивание перепланировок</li>
|
||||
<li>
|
||||
оформление права собственности на дома и земельные участки
|
||||
</li>
|
||||
<li>
|
||||
работа с жилищными сертификатами и субсидиями, в том числе
|
||||
материнским капиталом
|
||||
</li>
|
||||
<li>
|
||||
составление (подготовка) юридической экспертизы на
|
||||
продаваемый/покупаемый объект недвижимости
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<Form scrolledThreshold={2700} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { Services };
|
||||
|
||||
Reference in New Issue
Block a user