feat: simplify navigation and add RU/EN home and contacts

- simplify main navigation and hide extra menu items
- make home page more sales-focused with updated hero, benefits and fleet teaser
- add RU/EN handling for home and contacts, including SEO defaults
- integrate basic Strapi homepage API client (no breaking changes)
- update contacts page with messenger buttons and dynamic footer year

Made-with: Cursor
This commit is contained in:
2026-03-13 19:41:07 +05:00
parent 575db0ac53
commit fde9609f9a
26 changed files with 1753 additions and 735 deletions

34
App.tsx
View File

@@ -38,23 +38,57 @@ const App: React.FC = () => {
<Navbar transparent={true} />
<main className="flex-grow">
<Routes>
{/* Главная */}
<Route path="/" element={<Home />} />
<Route path="/en" element={<Home />} />
{/* Услуги */}
<Route path="/services" element={<ServicesPage />} />
<Route path="/en/services" element={<ServicesPage />} />
<Route path="/services/surveying" element={<SurveyingPage />} />
<Route path="/en/services/surveying" element={<SurveyingPage />} />
<Route path="/services/design" element={<DesignPage />} />
<Route path="/en/services/design" element={<DesignPage />} />
<Route path="/services/construction" element={<ConstructionPage />} />
<Route path="/en/services/construction" element={<ConstructionPage />} />
<Route path="/services/soil-survey" element={<SoilSurveyPage />} />
<Route path="/en/services/soil-survey" element={<SoilSurveyPage />} />
<Route path="/services/building-survey" element={<BuildingSurveyPage />} />
<Route path="/en/services/building-survey" element={<BuildingSurveyPage />} />
<Route path="/services/land-survey" element={<LandSurveyPage />} />
<Route path="/en/services/land-survey" element={<LandSurveyPage />} />
<Route path="/services/technical-tasks" element={<TechnicalTasksPage />} />
<Route path="/en/services/technical-tasks" element={<TechnicalTasksPage />} />
{/* Проекты */}
<Route path="/projects" element={<ProjectsPage />} />
<Route path="/en/projects" element={<ProjectsPage />} />
{/* Автопарк */}
<Route path="/fleet" element={<FleetPage />} />
<Route path="/en/fleet" element={<FleetPage />} />
{/* Лаборатории */}
<Route path="/laboratories/soil" element={<SoilLabPage />} />
<Route path="/en/laboratories/soil" element={<SoilLabPage />} />
<Route path="/laboratories/radiation" element={<RadiationLabPage />} />
<Route path="/en/laboratories/radiation" element={<RadiationLabPage />} />
{/* Сертификаты */}
<Route path="/certificates" element={<CertificatesPage />} />
<Route path="/en/certificates" element={<CertificatesPage />} />
{/* О компании */}
<Route path="/about" element={<AboutPage />} />
<Route path="/en/about" element={<AboutPage />} />
{/* Контакты */}
<Route path="/contacts" element={<ContactsPage />} />
<Route path="/en/contacts" element={<ContactsPage />} />
{/* Политика конфиденциальности */}
<Route path="/privacy-policy" element={<PrivacyPolicyPage />} />
<Route path="/en/privacy-policy" element={<PrivacyPolicyPage />} />
</Routes>
</main>
<Footer />

View File

@@ -1,16 +1,46 @@
import React from 'react';
import { useLocation } from 'react-router-dom';
import { BENEFITS } from '../constants';
const Benefits: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const descriptions = BENEFITS.map((item) => {
if (!isEnglish) return item.description;
switch (item.title) {
case 'Надежность':
return 'A reliable company ranked among the top 10 in Russia.';
case 'Опыт':
return 'Experienced engineers with 10+ years of hands-on practice.';
case 'Поддержка':
return 'Direct communication and support from our specialists 24/7.';
case 'Условия':
return 'Tailored proposals and flexible terms of cooperation.';
default:
return item.description;
}
});
return (
<div className="py-24 bg-white">
<div className="container mx-auto px-6">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12">
<div className="lg:col-span-4">
<span className="text-sm text-gray-500 uppercase tracking-widest mb-2 block">Выбирая нас</span>
<h2 className="text-4xl font-bold text-gray-900 mb-6">Вы получаете</h2>
<p className="text-gray-500 text-sm">Нас выбирают люди</p>
<span className="text-sm text-gray-500 uppercase tracking-widest mb-2 block">
{isEnglish ? 'WHO WE WORK WITH' : 'Для кого мы работаем'}
</span>
<h2 className="text-4xl font-bold text-gray-900 mb-6">
{isEnglish
? 'For business and public sector clients'
: 'Для бизнеса и государственных заказчиков'}
</h2>
<p className="text-gray-500 text-sm">
{isEnglish
? 'We help developers, industrial companies and public authorities solve complex engineering challenges and take responsibility for the final result.'
: 'Мы работаем с девелоперами, промышленными компаниями и государственными организациями, беря на себя сложные инженерные задачи и ответственность за результат.'}
</p>
</div>
<div className="lg:col-span-8 grid grid-cols-1 md:grid-cols-2 gap-x-12 gap-y-16">
@@ -20,7 +50,7 @@ const Benefits: React.FC = () => {
<item.icon size={24} />
</div>
<p className="text-gray-700 leading-relaxed font-medium">
{item.description}
{descriptions[index]}
</p>
</div>
))}

View File

@@ -0,0 +1,59 @@
import React from 'react';
import { Link, useLocation } from 'react-router-dom';
const FleetTeaser: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
return (
<section className="py-20 bg-white">
<div className="container mx-auto px-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-10 items-center">
<div>
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
{isEnglish ? 'Own vehicle fleet and equipment' : 'Собственный автопарк и техника'}
</h2>
<p className="text-gray-600 mb-4">
{isEnglish
? 'We operate drilling rigs, off-road vehicles, special-purpose trucks and auxiliary equipment, allowing us to mobilise quickly and work in challenging conditions.'
: 'В нашем распоряжении буровые установки, вездеходы, спецтранспорт и вспомогательная техника, которые позволяют быстро выходить на объект и работать в сложных условиях.'}
</p>
<p className="text-gray-600 mb-6">
{isEnglish
? 'This shortens mobilisation time, reduces downtime risks and gives you confidence that the work will be completed on schedule.'
: 'Это сокращает сроки мобилизации, снижает риски простоев и даёт вам уверенность в том, что работы будут выполнены в оговоренные сроки.'}
</p>
<Link
to={`${prefix}/fleet`}
className="inline-flex items-center px-6 py-3 bg-brand-orange text-white font-bold rounded-lg hover:bg-orange-600 transition-colors"
>
{isEnglish ? 'View our fleet' : 'Посмотреть автопарк'}
</Link>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="rounded-2xl overflow-hidden shadow-lg">
<img
src="/media/cars/33725959_1920_q70.webp"
alt="Техника ГеоВектор"
className="w-full h-full object-cover"
loading="lazy"
/>
</div>
<div className="rounded-2xl overflow-hidden shadow-lg translate-y-6">
<img
src="/media/cars/33725970_1920_q70.webp"
alt="Спецтранспорт ГеоВектор"
className="w-full h-full object-cover"
loading="lazy"
/>
</div>
</div>
</div>
</div>
</section>
);
};
export default FleetTeaser;

View File

@@ -1,8 +1,13 @@
import React from 'react';
import { Send, MapPin, Mail, Phone } from 'lucide-react';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';
const Footer: React.FC = () => {
const currentYear = new Date().getFullYear();
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
return (
<footer className="bg-[#1a0f0f] text-white py-20 rounded-t-[3rem] mt-auto" id="contacts">
<div className="container mx-auto px-6">
@@ -10,9 +15,13 @@ const Footer: React.FC = () => {
{/* Contacts Section with Accent */}
<div className="lg:w-1/2">
<h2 className="text-4xl font-bold mb-8 text-brand-orange">Свяжитесь с нами</h2>
<h2 className="text-4xl font-bold mb-8 text-brand-orange">
{isEnglish ? 'Get in touch with us' : 'Свяжитесь с нами'}
</h2>
<p className="text-gray-300 text-lg mb-10 max-w-md">
Готовы ответить на ваши вопросы и предложить лучшие решения для вашего проекта
{isEnglish
? 'We are ready to answer your questions and offer the best solutions for your project.'
: 'Готовы ответить на ваши вопросы и предложить лучшие решения для вашего проекта'}
</p>
<div className="space-y-6 max-w-md">
@@ -22,9 +31,13 @@ const Footer: React.FC = () => {
>
<Phone size={28} className="text-brand-orange group-hover:text-white flex-shrink-0" />
<div>
<p className="text-xs text-gray-400 mb-1 group-hover:text-white/80">Телефон</p>
<p className="text-xs text-gray-400 mb-1 group-hover:text-white/80">
{isEnglish ? 'Phone' : 'Телефон'}
</p>
<p className="text-xl font-bold text-white">8 (347) 292 73 70</p>
<p className="text-sm text-gray-400 mt-1 group-hover:text-white/70">Звоните с 9:00 до 18:00</p>
<p className="text-sm text-gray-400 mt-1 group-hover:text-white/70">
{isEnglish ? 'Call us from 9:00 to 18:00' : 'Звоните с 9:00 до 18:00'}
</p>
</div>
</a>
@@ -36,16 +49,24 @@ const Footer: React.FC = () => {
<div>
<p className="text-xs text-gray-400 mb-1 group-hover:text-white/80">Email</p>
<p className="text-xl font-bold text-white">gw@geowektor.ru</p>
<p className="text-sm text-gray-400 mt-1 group-hover:text-white/70">Ответим в течение часа</p>
<p className="text-sm text-gray-400 mt-1 group-hover:text-white/70">
{isEnglish ? 'We typically respond within one hour' : 'Ответим в течение часа'}
</p>
</div>
</a>
<div className="flex items-start gap-4 p-6 bg-brand-orange/10 border border-brand-orange/30 rounded-2xl">
<MapPin size={28} className="text-brand-orange flex-shrink-0" />
<div>
<p className="text-xs text-gray-400 mb-1">Адрес</p>
<p className="text-lg font-bold text-white">450001, РБ, г. Уфа</p>
<p className="text-white/90">ул. Комсомольская 19/1</p>
<p className="text-xs text-gray-400 mb-1">
{isEnglish ? 'Address' : 'Адрес'}
</p>
<p className="text-lg font-bold text-white">
{isEnglish ? '450001, RB, Ufa' : '450001, РБ, г. Уфа'}
</p>
<p className="text-white/90">
{isEnglish ? '19/1 Komsomolskaya St.' : 'ул. Комсомольская 19/1'}
</p>
</div>
</div>
</div>
@@ -55,40 +76,108 @@ const Footer: React.FC = () => {
<div className="lg:w-1/2 grid grid-cols-1 md:grid-cols-3 gap-8">
{/* Основная навигация */}
<div>
<h4 className="font-bold mb-6 text-lg">Компания</h4>
<h4 className="font-bold mb-6 text-lg">
{isEnglish ? 'Company' : 'Компания'}
</h4>
<ul className="space-y-3 text-sm text-gray-400">
<li><Link to="/" className="hover:text-brand-orange transition-colors">Главная</Link></li>
<li><Link to="/about" className="hover:text-brand-orange transition-colors">О компании</Link></li>
<li><Link to="/projects" className="hover:text-brand-orange transition-colors">Проекты</Link></li>
<li><Link to="/fleet" className="hover:text-brand-orange transition-colors">Автопарк</Link></li>
<li><Link to="/certificates" className="hover:text-brand-orange transition-colors">Сертификаты</Link></li>
<li><Link to="/contacts" className="hover:text-brand-orange transition-colors">Контакты</Link></li>
<li>
<Link to={isEnglish ? '/en' : '/'} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Home' : 'Главная'}
</Link>
</li>
<li>
<Link to={`${prefix}/about`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'About' : 'О компании'}
</Link>
</li>
<li>
<Link to={`${prefix}/projects`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Projects' : 'Проекты'}
</Link>
</li>
<li>
<Link to={`${prefix}/fleet`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Fleet' : 'Автопарк'}
</Link>
</li>
<li>
<Link to={`${prefix}/certificates`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Certificates' : 'Сертификаты'}
</Link>
</li>
<li>
<Link to={`${prefix}/contacts`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Contacts' : 'Контакты'}
</Link>
</li>
</ul>
</div>
{/* Услуги */}
<div>
<h4 className="font-bold mb-6 text-lg">Услуги</h4>
<h4 className="font-bold mb-6 text-lg">
{isEnglish ? 'Services' : 'Услуги'}
</h4>
<ul className="space-y-3 text-sm text-gray-400">
<li><Link to="/services" className="hover:text-brand-orange transition-colors">Все услуги</Link></li>
<li><Link to="/services/surveying" className="hover:text-brand-orange transition-colors">Инженерные изыскания</Link></li>
<li><Link to="/services/design" className="hover:text-brand-orange transition-colors">Проектирование</Link></li>
<li><Link to="/services/construction" className="hover:text-brand-orange transition-colors">Строительство</Link></li>
<li><Link to="/services/soil-survey" className="hover:text-brand-orange transition-colors">Обследование грунтов</Link></li>
<li><Link to="/services/building-survey" className="hover:text-brand-orange transition-colors">Обследование зданий</Link></li>
<li><Link to="/services/land-survey" className="hover:text-brand-orange transition-colors">Кадастровые работы</Link></li>
<li>
<Link to={`${prefix}/services`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'All services' : 'Все услуги'}
</Link>
</li>
<li>
<Link to={`${prefix}/services/surveying`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Engineering surveys' : 'Инженерные изыскания'}
</Link>
</li>
<li>
<Link to={`${prefix}/services/design`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Design' : 'Проектирование'}
</Link>
</li>
<li>
<Link to={`${prefix}/services/construction`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Construction' : 'Строительство'}
</Link>
</li>
<li>
<Link to={`${prefix}/services/soil-survey`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Soil investigation' : 'Обследование грунтов'}
</Link>
</li>
<li>
<Link to={`${prefix}/services/building-survey`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Building survey' : 'Обследование зданий'}
</Link>
</li>
<li>
<Link to={`${prefix}/services/land-survey`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Cadastral works' : 'Кадастровые работы'}
</Link>
</li>
</ul>
</div>
{/* Лаборатории и соцсети */}
<div>
<h4 className="font-bold mb-6 text-lg">Лаборатории</h4>
<h4 className="font-bold mb-6 text-lg">
{isEnglish ? 'Laboratories' : 'Лаборатории'}
</h4>
<ul className="space-y-3 text-sm text-gray-400 mb-8">
<li><Link to="/laboratories/soil" className="hover:text-brand-orange transition-colors">Грунтовая лаборатория</Link></li>
<li><Link to="/laboratories/radiation" className="hover:text-brand-orange transition-colors">Радиационная лаборатория</Link></li>
<li>
<Link to={`${prefix}/laboratories/soil`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Soil testing laboratory' : 'Грунтовая лаборатория'}
</Link>
</li>
<li>
<Link to={`${prefix}/laboratories/radiation`} className="hover:text-brand-orange transition-colors">
{isEnglish ? 'Radiation laboratory' : 'Радиационная лаборатория'}
</Link>
</li>
</ul>
<h4 className="font-bold mb-4 text-lg">Мы в интернете</h4>
<h4 className="font-bold mb-4 text-lg">
{isEnglish ? 'Find us online' : 'Мы в интернете'}
</h4>
<div className="flex gap-4 mb-8">
<a
href="https://t.me/ooo_geo_wektor"
@@ -112,16 +201,16 @@ const Footer: React.FC = () => {
<div className="space-y-3 text-sm">
<Link
to="/privacy-policy"
to={`${prefix}/privacy-policy`}
className="text-gray-400 hover:text-brand-orange transition-colors block"
>
Политика конфиденциальности
{isEnglish ? 'Privacy policy' : 'Политика конфиденциальности'}
</Link>
</div>
<div className="mt-8 text-xs text-gray-600">
©2025 ООО «ГеоВектор».<br />
Все права защищены.
©{currentYear} ООО «ГеоВектор».<br />
{isEnglish ? 'All rights reserved.' : 'Все права защищены.'}
</div>
</div>
</div>

View File

@@ -1,8 +1,17 @@
import React from 'react';
import { STATS } from '../constants';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';
interface HeroProps {
title?: string;
subtitle?: string;
}
const Hero: React.FC<HeroProps> = ({ title, subtitle }) => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
const Hero: React.FC = () => {
return (
<div className="relative w-full min-h-screen bg-brand-dark text-white flex flex-col">
{/* Background Image Overlay */}
@@ -20,40 +29,62 @@ const Hero: React.FC = () => {
<div className="relative z-10 container mx-auto px-6 flex-grow flex flex-col justify-center py-32 md:py-20">
<div className="max-w-5xl mx-auto text-center mt-10 md:mt-0">
<h1 className="text-4xl md:text-5xl lg:text-7xl font-bold leading-tight mb-8">
Инженерные изыскания,
{title || (
<>
Инженерные изыскания и проектирование
<br />
<span className="text-brand-orange">проектирование</span> и
<br />
<span className="text-brand-orange">строительство</span>
для сложных и ответственных объектов
</>
)}
</h1>
<p className="text-gray-300 text-lg md:text-xl mb-12 max-w-3xl mx-auto leading-relaxed">
ООО «ГеоВектор» профессиональные решения для вашего проекта от изысканий до сдачи объекта.
Современное оборудование, опытные специалисты и соблюдение всех норм и стандартов.
{subtitle || (
<>
ООО «ГеоВектор» полный комплекс инженерных изысканий и проектных решений
для девелоперов, промышленных предприятий и госзаказчиков в России и других странах.
</>
)}
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
<Link
to="/contacts"
to={`${prefix}/contacts`}
className="inline-flex items-center justify-center bg-brand-orange text-white font-bold py-4 px-8 rounded-xl hover:bg-orange-600 transition-all duration-300 shadow-lg hover:shadow-xl hover:scale-105"
>
Рассчитать стоимость проекта
{isEnglish ? 'Request a project quote' : 'Рассчитать стоимость проекта'}
</Link>
<Link
to="/services"
to={`${prefix}/services`}
className="inline-flex items-center justify-center bg-white/10 backdrop-blur-sm text-white font-bold py-4 px-8 rounded-xl hover:bg-white/20 transition-all duration-300 border-2 border-white/30"
>
Наши услуги
{isEnglish ? 'Our services' : 'Наши услуги'}
</Link>
</div>
</div>
{/* Stats */}
<div className="mt-24 grid grid-cols-1 md:grid-cols-3 gap-8 border-t border-white/10 pt-12 max-w-5xl mx-auto w-full">
{STATS.map((stat, idx) => (
{STATS.map((stat, idx) => {
const label = isEnglish
? idx === 0
? 'For over 10 years we have been helping organisations deliver projects of any complexity.'
: idx === 1
? 'More than 20 major companies trust us with their projects.'
: idx === 2
? 'Over 30 successfully completed projects in the last 3 years.'
: stat.label
: stat.label;
return (
<div key={idx} className="flex flex-col items-center text-center">
<span className="text-4xl md:text-5xl font-bold text-brand-orange mb-2">{stat.value}</span>
<p className="text-gray-400 text-sm md:text-base leading-relaxed">{stat.label}</p>
<span className="text-4xl md:text-5xl font-bold text-brand-orange mb-2">
{stat.value}
</span>
<p className="text-gray-400 text-sm md:text-base leading-relaxed">
{label}
</p>
</div>
))}
);
})}
</div>
</div>
</div>

View File

@@ -1,8 +1,12 @@
import React from 'react';
import { Microscope, Activity, ArrowRight, CheckCircle2, Shield } from 'lucide-react';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';
const Laboratories: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
return (
<section className="py-20 bg-gradient-to-b from-white to-gray-50">
<div className="container mx-auto px-6">
@@ -11,11 +15,12 @@ const Laboratories: React.FC = () => {
<Microscope className="text-brand-orange" size={32} />
</div>
<h2 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
Наши лаборатории
{isEnglish ? 'Our laboratories' : 'Наши лаборатории'}
</h2>
<p className="text-gray-600 text-lg max-w-2xl mx-auto">
Современное оборудование и квалифицированные специалисты для проведения
комплексных исследований
{isEnglish
? 'State-of-the-art equipment and qualified specialists for comprehensive testing and research.'
: 'Современное оборудование и квалифицированные специалисты для проведения комплексных исследований'}
</p>
</div>
@@ -26,7 +31,7 @@ const Laboratories: React.FC = () => {
<div className="relative h-64 overflow-hidden">
<img
src="/media/images/services/soil-survey.png"
alt="Грунтовая лаборатория"
alt={isEnglish ? 'Soil testing laboratory' : 'Грунтовая лаборатория'}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
loading="lazy"
/>
@@ -42,10 +47,12 @@ const Laboratories: React.FC = () => {
{/* Заголовок на изображении */}
<div className="absolute bottom-6 left-6 right-6">
<h3 className="text-2xl font-bold text-white mb-2">
Грунтовая лаборатория
{isEnglish ? 'Soil testing laboratory' : 'Грунтовая лаборатория'}
</h3>
<p className="text-white/90 text-sm">
Исследования физических, механических и химических свойств
{isEnglish
? 'Testing of physical, mechanical and chemical soil properties.'
: 'Исследования физических, механических и химических свойств'}
</p>
</div>
</div>
@@ -56,34 +63,46 @@ const Laboratories: React.FC = () => {
<div className="flex items-start gap-3">
<CheckCircle2 className="text-brand-orange flex-shrink-0 mt-1" size={20} />
<div>
<p className="text-gray-700 font-medium">Физические свойства грунтов</p>
<p className="text-sm text-gray-500">Влажность, плотность, гранулометрический состав</p>
<p className="text-gray-700 font-medium">
{isEnglish ? 'Physical properties of soils' : 'Физические свойства грунтов'}
</p>
<p className="text-sm text-gray-500">
{isEnglish ? 'Moisture content, density, grain-size distribution.' : 'Влажность, плотность, гранулометрический состав'}
</p>
</div>
</div>
<div className="flex items-start gap-3">
<CheckCircle2 className="text-brand-orange flex-shrink-0 mt-1" size={20} />
<div>
<p className="text-gray-700 font-medium">Механические характеристики</p>
<p className="text-sm text-gray-500">Прочность, деформируемость, сжимаемость</p>
<p className="text-gray-700 font-medium">
{isEnglish ? 'Mechanical characteristics' : 'Механические характеристики'}
</p>
<p className="text-sm text-gray-500">
{isEnglish ? 'Strength, deformability, compressibility.' : 'Прочность, деформируемость, сжимаемость'}
</p>
</div>
</div>
<div className="flex items-start gap-3">
<CheckCircle2 className="text-brand-orange flex-shrink-0 mt-1" size={20} />
<div>
<p className="text-gray-700 font-medium">Химический анализ</p>
<p className="text-sm text-gray-500">Агрессивность грунтов, анализ воды</p>
<p className="text-gray-700 font-medium">
{isEnglish ? 'Chemical analysis' : 'Химический анализ'}
</p>
<p className="text-sm text-gray-500">
{isEnglish ? 'Soil aggressiveness, water analysis.' : 'Агрессивность грунтов, анализ воды'}
</p>
</div>
</div>
</div>
{/* Кнопка */}
<Link
to="/laboratories/soil"
to={`${prefix}/laboratories/soil`}
className="flex items-center justify-center gap-2 w-full py-4 bg-gradient-to-r from-brand-orange to-orange-600 text-white font-bold rounded-xl hover:from-orange-600 hover:to-brand-orange transition-all duration-300 group/btn"
>
Подробнее о лаборатории
{isEnglish ? 'More about the laboratory' : 'Подробнее о лаборатории'}
<ArrowRight className="group-hover/btn:translate-x-1 transition-transform" size={20} />
</Link>
</div>
@@ -95,7 +114,7 @@ const Laboratories: React.FC = () => {
<div className="relative h-64 overflow-hidden">
<img
src="/media/images/services/engineering-surveys.png"
alt="Радиационная лаборатория"
alt={isEnglish ? 'Radiation laboratory' : 'Радиационная лаборатория'}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
loading="lazy"
/>
@@ -111,10 +130,12 @@ const Laboratories: React.FC = () => {
{/* Заголовок на изображении */}
<div className="absolute bottom-6 left-6 right-6">
<h3 className="text-2xl font-bold text-white mb-2">
Радиационная лаборатория
{isEnglish ? 'Radiation laboratory' : 'Радиационная лаборатория'}
</h3>
<p className="text-white/90 text-sm">
Профессиональные исследования радиационной безопасности
{isEnglish
? 'Professional radiation safety and exposure assessments.'
: 'Профессиональные исследования радиационной безопасности'}
</p>
</div>
</div>
@@ -125,23 +146,33 @@ const Laboratories: React.FC = () => {
<div className="flex items-start gap-3">
<Shield className="text-brand-orange flex-shrink-0 mt-1" size={20} />
<div>
<p className="text-gray-700 font-medium">Радиационный контроль</p>
<p className="text-sm text-gray-500">Измерение уровня радона и гамма-излучения</p>
<p className="text-gray-700 font-medium">
{isEnglish ? 'Radiation monitoring' : 'Радиационный контроль'}
</p>
<p className="text-sm text-gray-500">
{isEnglish ? 'Measurement of radon and gamma radiation levels.' : 'Измерение уровня радона и гамма-излучения'}
</p>
</div>
</div>
<div className="flex items-start gap-3">
<Shield className="text-brand-orange flex-shrink-0 mt-1" size={20} />
<div>
<p className="text-gray-700 font-medium">Аккредитованная лаборатория</p>
<p className="text-sm text-gray-500">Техническая компетентность и независимость</p>
<p className="text-gray-700 font-medium">
{isEnglish ? 'Accredited laboratory' : 'Аккредитованная лаборатория'}
</p>
<p className="text-sm text-gray-500">
{isEnglish ? 'Technical competence and independence.' : 'Техническая компетентность и независимость'}
</p>
</div>
</div>
<div className="flex items-start gap-3">
<Shield className="text-brand-orange flex-shrink-0 mt-1" size={20} />
<div>
<p className="text-gray-700 font-medium">Современное оборудование</p>
<p className="text-gray-700 font-medium">
{isEnglish ? 'Modern equipment' : 'Современное оборудование'}
</p>
<p className="text-sm text-gray-500">КАМЕРА-01, ДКГ-02У, ДРБП-03</p>
</div>
</div>
@@ -149,10 +180,10 @@ const Laboratories: React.FC = () => {
{/* Кнопка */}
<Link
to="/laboratories/radiation"
to={`${prefix}/laboratories/radiation`}
className="flex items-center justify-center gap-2 w-full py-4 bg-gradient-to-r from-brand-orange to-orange-600 text-white font-bold rounded-xl hover:from-orange-600 hover:to-brand-orange transition-all duration-300 group/btn"
>
Подробнее о лаборатории
{isEnglish ? 'More about the laboratory' : 'Подробнее о лаборатории'}
<ArrowRight className="group-hover/btn:translate-x-1 transition-transform" size={20} />
</Link>
</div>
@@ -163,16 +194,20 @@ const Laboratories: React.FC = () => {
<div className="mt-16 max-w-4xl mx-auto">
<div className="bg-gradient-to-br from-gray-900 to-gray-800 text-white rounded-2xl p-8 md:p-12 text-center">
<h3 className="text-2xl md:text-3xl font-bold mb-4">
Нужна консультация по лабораторным исследованиям?
{isEnglish
? 'Need a consultation on laboratory testing?'
: 'Нужна консультация по лабораторным исследованиям?'}
</h3>
<p className="text-gray-300 mb-6 max-w-2xl mx-auto">
Наши специалисты помогут подобрать оптимальный комплекс исследований для вашего проекта
{isEnglish
? 'Our specialists will help you select the optimal set of tests for your project.'
: 'Наши специалисты помогут подобрать оптимальный комплекс исследований для вашего проекта'}
</p>
<Link
to="/contacts"
to={`${prefix}/contacts`}
className="inline-flex items-center gap-2 px-8 py-4 bg-brand-orange text-white font-bold rounded-xl hover:bg-orange-600 transition-colors"
>
Связаться с нами
{isEnglish ? 'Contact us' : 'Связаться с нами'}
<ArrowRight size={20} />
</Link>
</div>

View File

@@ -10,11 +10,12 @@ interface NavbarProps {
const Navbar: React.FC<NavbarProps> = ({ transparent = false }) => {
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isMobileServicesMenuOpen, setIsMobileServicesMenuOpen] = useState(false);
const [isMobileLabMenuOpen, setIsMobileLabMenuOpen] = useState(false);
const [imgError, setImgError] = useState(false);
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 50);
@@ -24,7 +25,7 @@ const Navbar: React.FC<NavbarProps> = ({ transparent = false }) => {
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const isHome = location.pathname === '/';
const isHome = location.pathname === '/' || location.pathname === '/en';
// Use transparent background only on Home and when not scrolled, if requested
const isTransparent = transparent && isHome && !isScrolled;
@@ -37,7 +38,7 @@ const Navbar: React.FC<NavbarProps> = ({ transparent = false }) => {
<div className="container mx-auto px-6 flex items-center justify-between text-white">
{/* Logo */}
<Link to="/" className="flex items-center gap-2 group">
<Link to={isEnglish ? '/en' : '/'} className="flex items-center gap-2 group">
{!imgError ? (
<img
src="/media/geo-logo.webp"
@@ -57,177 +58,105 @@ const Navbar: React.FC<NavbarProps> = ({ transparent = false }) => {
{/* Главная */}
<li>
<Link
to="/"
to={isEnglish ? '/en' : '/'}
className={`transition-colors hover:text-brand-orange ${
location.pathname === '/' ? 'text-brand-orange' : 'text-gray-300'
(location.pathname === '/' || location.pathname === '/en')
? 'text-brand-orange'
: 'text-gray-300'
}`}
>
Главная
{isEnglish ? 'Home' : 'Главная'}
</Link>
</li>
{/* Услуги */}
<li>
<Link
to={`${prefix}/services`}
className={`transition-colors hover:text-brand-orange ${
location.pathname.replace('/en', '').startsWith('/services')
? 'text-brand-orange'
: 'text-gray-300'
}`}
>
{isEnglish ? 'Services' : 'Услуги'}
</Link>
</li>
{/* Проекты */}
<li>
<Link
to={`${prefix}/projects`}
className={`transition-colors hover:text-brand-orange ${
location.pathname.replace('/en', '') === '/projects'
? 'text-brand-orange'
: 'text-gray-300'
}`}
>
{isEnglish ? 'Projects' : 'Проекты'}
</Link>
</li>
{/* О компании */}
<li>
<Link
to="/about"
to={`${prefix}/about`}
className={`transition-colors hover:text-brand-orange ${
location.pathname === '/about' ? 'text-brand-orange' : 'text-gray-300'
location.pathname.replace('/en', '') === '/about'
? 'text-brand-orange'
: 'text-gray-300'
}`}
>
О компании
{isEnglish ? 'About' : 'О компании'}
</Link>
</li>
{/* Услуги с выпадающим меню */}
<li className="relative group">
<button
className={`flex items-center gap-1 transition-colors hover:text-brand-orange ${
location.pathname.startsWith('/services') ? 'text-brand-orange' : 'text-gray-300'
}`}
>
Услуги
<ChevronDown size={16} className={`transition-transform group-hover:rotate-180`} />
</button>
{/* Выпадающее меню */}
<div className="absolute top-full left-0 pt-2 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200">
<div className="w-72 bg-brand-dark border border-gray-700 rounded-lg shadow-xl overflow-hidden">
<Link
to="/services"
className="block px-6 py-3 text-gray-300 hover:bg-brand-orange hover:text-white transition-colors font-semibold border-b border-gray-700"
>
Все услуги
</Link>
<Link
to="/services/surveying"
className="block px-6 py-3 text-gray-300 hover:bg-brand-orange hover:text-white transition-colors"
>
Инженерные изыскания
</Link>
<Link
to="/services/design"
className="block px-6 py-3 text-gray-300 hover:bg-brand-orange hover:text-white transition-colors"
>
Проектирование
</Link>
<Link
to="/services/construction"
className="block px-6 py-3 text-gray-300 hover:bg-brand-orange hover:text-white transition-colors"
>
Строительство
</Link>
<Link
to="/services/soil-survey"
className="block px-6 py-3 text-gray-300 hover:bg-brand-orange hover:text-white transition-colors"
>
Обследование грунтов
</Link>
<Link
to="/services/building-survey"
className="block px-6 py-3 text-gray-300 hover:bg-brand-orange hover:text-white transition-colors"
>
Обследование здания
</Link>
<Link
to="/services/land-survey"
className="block px-6 py-3 text-gray-300 hover:bg-brand-orange hover:text-white transition-colors"
>
Кадастровые работы
</Link>
<Link
to="/services/technical-tasks"
className="block px-6 py-3 text-gray-300 hover:bg-brand-orange hover:text-white transition-colors border-t border-gray-700"
>
Образцы технических заданий
</Link>
</div>
</div>
</li>
{/* Проекты */}
<li>
<Link
to="/projects"
className={`transition-colors hover:text-brand-orange ${
location.pathname === '/projects' ? 'text-brand-orange' : 'text-gray-300'
}`}
>
Проекты
</Link>
</li>
{/* Автопарк */}
<li>
<Link
to="/fleet"
className={`transition-colors hover:text-brand-orange ${
location.pathname === '/fleet' ? 'text-brand-orange' : 'text-gray-300'
}`}
>
Автопарк
</Link>
</li>
{/* Сертификаты */}
<li>
<Link
to="/certificates"
className={`transition-colors hover:text-brand-orange ${
location.pathname === '/certificates' ? 'text-brand-orange' : 'text-gray-300'
}`}
>
Сертификаты
</Link>
</li>
{/* Лаборатории с выпадающим меню */}
<li className="relative group">
<button
className={`flex items-center gap-1 transition-colors hover:text-brand-orange ${
location.pathname.startsWith('/laboratories') ? 'text-brand-orange' : 'text-gray-300'
}`}
>
Лаборатории
<ChevronDown size={16} className={`transition-transform group-hover:rotate-180`} />
</button>
{/* Выпадающее меню */}
<div className="absolute top-full left-0 pt-2 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200">
<div className="w-64 bg-brand-dark border border-gray-700 rounded-lg shadow-xl overflow-hidden">
<Link
to="/laboratories/soil"
className="block px-6 py-3 text-gray-300 hover:bg-brand-orange hover:text-white transition-colors"
>
Грунтовая лаборатория
</Link>
<Link
to="/laboratories/radiation"
className="block px-6 py-3 text-gray-300 hover:bg-brand-orange hover:text-white transition-colors"
>
Радиационная лаборатория
</Link>
</div>
</div>
</li>
{/* Контакты */}
<li>
<Link
to="/contacts"
to={`${prefix}/contacts`}
className={`transition-colors hover:text-brand-orange ${
location.pathname === '/contacts' ? 'text-brand-orange' : 'text-gray-300'
location.pathname.replace('/en', '') === '/contacts'
? 'text-brand-orange'
: 'text-gray-300'
}`}
>
Контакты
{isEnglish ? 'Contacts' : 'Контакты'}
</Link>
</li>
</ul>
{/* CTA & Mobile Toggle */}
{/* Language switcher, CTA & Mobile Toggle */}
<div className="flex items-center gap-4">
<Link to="/contacts" className="hidden md:flex items-center gap-2 text-sm font-medium hover:text-brand-orange transition-colors">
{/* Языковой переключатель */}
<div className="hidden md:flex items-center gap-1 text-sm font-medium">
<button
onClick={() => {
if (location.pathname.startsWith('/en')) {
const ruPath = location.pathname.replace('/en', '') || '/';
window.location.hash = `#${ruPath}`;
}
}}
className={location.pathname.startsWith('/en') ? 'text-gray-400 hover:text-white transition-colors' : 'text-white font-semibold'}
>
RU
</button>
<span className="text-gray-500">/</span>
<button
onClick={() => {
if (!location.pathname.startsWith('/en')) {
const basePath = location.pathname === '/' ? '' : location.pathname;
window.location.hash = `#/en${basePath}`;
}
}}
className={location.pathname.startsWith('/en') ? 'text-white font-semibold' : 'text-gray-400 hover:text-white transition-colors'}
>
EN
</button>
</div>
<Link to={`${prefix}/contacts`} className="hidden md:flex items-center gap-2 text-sm font-medium hover:text-brand-orange transition-colors">
<Phone className="w-4 h-4 text-brand-orange" />
<span>Связаться</span>
<span>{isEnglish ? 'Contact us' : 'Связаться'}</span>
</Link>
<button
@@ -244,166 +173,86 @@ const Navbar: React.FC<NavbarProps> = ({ transparent = false }) => {
<div className="absolute top-full left-0 w-full bg-brand-dark border-t border-gray-800 p-6 flex flex-col gap-6 md:hidden shadow-xl">
{/* Главная */}
<Link
to="/"
to={isEnglish ? '/en' : '/'}
className="text-lg font-medium text-gray-300 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Главная
{isEnglish ? 'Home' : 'Главная'}
</Link>
{/* О компании */}
<Link
to="/about"
to={`${prefix}/about`}
className="text-lg font-medium text-gray-300 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
О компании
{isEnglish ? 'About' : 'О компании'}
</Link>
{/* Услуги в мобильном меню */}
<div>
<button
className="flex items-center justify-between w-full text-lg font-medium text-gray-300 hover:text-brand-orange"
onClick={() => setIsMobileServicesMenuOpen(!isMobileServicesMenuOpen)}
>
Услуги
<ChevronDown size={20} className={`transition-transform ${isMobileServicesMenuOpen ? 'rotate-180' : ''}`} />
</button>
{isMobileServicesMenuOpen && (
<div className="ml-4 mt-3 flex flex-col gap-3">
<Link
to="/services"
className="text-gray-400 hover:text-brand-orange font-semibold"
to={`${prefix}/services`}
className="text-lg font-medium text-gray-300 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Все услуги
{isEnglish ? 'Services' : 'Услуги'}
</Link>
<Link
to="/services/surveying"
className="text-gray-400 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Инженерные изыскания
</Link>
<Link
to="/services/design"
className="text-gray-400 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Проектирование
</Link>
<Link
to="/services/construction"
className="text-gray-400 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Строительство
</Link>
<Link
to="/services/soil-survey"
className="text-gray-400 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Обследование грунтов
</Link>
<Link
to="/services/building-survey"
className="text-gray-400 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Обследование здания
</Link>
<Link
to="/services/land-survey"
className="text-gray-400 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Кадастровые работы
</Link>
<Link
to="/services/technical-tasks"
className="text-gray-400 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Образцы ТЗ
</Link>
</div>
)}
</div>
{/* Проекты */}
<Link
to="/projects"
to={`${prefix}/projects`}
className="text-lg font-medium text-gray-300 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Проекты
{isEnglish ? 'Projects' : 'Проекты'}
</Link>
{/* Автопарк */}
<Link
to="/fleet"
className="text-lg font-medium text-gray-300 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Автопарк
</Link>
{/* Сертификаты */}
<Link
to="/certificates"
className="text-lg font-medium text-gray-300 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Сертификаты
</Link>
{/* Лаборатории в мобильном меню */}
<div>
<button
className="flex items-center justify-between w-full text-lg font-medium text-gray-300 hover:text-brand-orange"
onClick={() => setIsMobileLabMenuOpen(!isMobileLabMenuOpen)}
>
Лаборатории
<ChevronDown size={20} className={`transition-transform ${isMobileLabMenuOpen ? 'rotate-180' : ''}`} />
</button>
{isMobileLabMenuOpen && (
<div className="ml-4 mt-3 flex flex-col gap-3">
<Link
to="/laboratories/soil"
className="text-gray-400 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Грунтовая лаборатория
</Link>
<Link
to="/laboratories/radiation"
className="text-gray-400 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Радиационная лаборатория
</Link>
</div>
)}
</div>
{/* Автопарк, сертификаты и лаборатории убраны из основного мобильного меню */}
{/* Контакты */}
<Link
to="/contacts"
to={`${prefix}/contacts`}
className="text-lg font-medium text-gray-300 hover:text-brand-orange"
onClick={() => setIsMobileMenuOpen(false)}
>
Контакты
{isEnglish ? 'Contacts' : 'Контакты'}
</Link>
{/* Языковой переключатель (мобильный) */}
<div className="flex items-center gap-2 text-sm font-medium text-gray-300 pt-4 border-t border-gray-800">
<button
onClick={() => {
setIsMobileMenuOpen(false);
if (location.pathname.startsWith('/en')) {
const ruPath = location.pathname.replace('/en', '') || '/';
window.location.hash = `#${ruPath}`;
}
}}
className={location.pathname.startsWith('/en') ? 'text-gray-500' : 'text-white'}
>
RU
</button>
<span className="text-gray-500">/</span>
<button
onClick={() => {
setIsMobileMenuOpen(false);
if (!location.pathname.startsWith('/en')) {
const basePath = location.pathname === '/' ? '' : location.pathname;
window.location.hash = `#/en${basePath}`;
}
}}
className={location.pathname.startsWith('/en') ? 'text-white' : 'text-gray-500'}
>
EN
</button>
</div>
<Link
to="/contacts"
to={`${prefix}/contacts`}
className="flex items-center gap-2 text-brand-orange font-bold mt-4"
onClick={() => setIsMobileMenuOpen(false)}
>
<Phone className="w-5 h-5" /> Связаться
<Phone className="w-5 h-5" /> {isEnglish ? 'Contact us' : 'Связаться'}
</Link>
</div>
)}

View File

@@ -4,12 +4,14 @@ interface PageHeaderProps {
title: string;
description?: string;
image?: string;
children?: React.ReactNode;
}
const PageHeader: React.FC<PageHeaderProps> = ({
title,
description,
image = "/media/images/headers/header-about.png"
image = "/media/images/headers/header-about.png",
children,
}) => {
return (
<div className="relative w-full h-[400px] md:h-[500px] bg-brand-dark text-white flex flex-col justify-center items-center text-center overflow-hidden">
@@ -22,13 +24,14 @@ const PageHeader: React.FC<PageHeaderProps> = ({
<div className="absolute inset-0 bg-gradient-to-b from-brand-dark/80 to-brand-dark/40" />
</div>
<div className="relative z-10 container mx-auto px-6 mt-16">
<div className="relative z-10 container mx-auto px-6 mt-16 flex flex-col items-center">
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold mb-6">{title}</h1>
{description && (
<p className="text-gray-300 text-lg max-w-2xl mx-auto leading-relaxed">
<p className="text-gray-300 text-lg max-w-2xl mx-auto leading-relaxed mb-6">
{description}
</p>
)}
{children}
</div>
</div>
);

View File

@@ -1,18 +1,48 @@
import React from 'react';
import { STEPS } from '../constants';
import { useLocation } from 'react-router-dom';
const Process: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const steps = STEPS.map((step) => ({
...step,
description: isEnglish
? (() => {
switch (step.title) {
case 'Звонок':
return 'We get in touch with you, clarify all the details and agree on the price.';
case 'ТЗ':
return 'We prepare a technical assignment for our specialists.';
case 'Договор':
return 'We prepare all required documents and sign the contract.';
case 'Работа':
return 'We carry out on-site work and test collected samples.';
case 'Отчет':
return 'We prepare a technical report and hand over the full documentation package.';
case 'Финал':
return 'We present the report and provide recommendations.';
default:
return step.description;
}
})()
: step.description,
}));
return (
<div className="py-20 bg-brand-light">
<div className="container mx-auto px-6">
<h2 className="text-4xl font-bold text-gray-900 mb-16 text-center">Как мы работаем</h2>
<h2 className="text-4xl font-bold text-gray-900 mb-16 text-center">
{isEnglish ? 'How we work' : 'Как мы работаем'}
</h2>
<div className="relative max-w-6xl mx-auto">
{/* Линия связи между шагами (только на больших экранах) */}
<div className="hidden lg:block absolute top-9 left-0 right-0 h-1 bg-gradient-to-r from-brand-orange via-brand-orange/50 to-brand-orange" style={{ zIndex: 0 }} />
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 relative" style={{ zIndex: 1 }}>
{STEPS.map((step, idx) => (
{steps.map((step, idx) => (
<div key={idx} className="relative">
{/* Номер шага */}
<div className="absolute -top-4 -left-4 w-10 h-10 bg-brand-orange text-white rounded-full flex items-center justify-center font-bold text-lg shadow-lg z-10 border-4 border-brand-light">

View File

@@ -1,9 +1,13 @@
import React, { useState } from 'react';
import { groupProjectsByCategory } from '../constants';
import { MapPin, ChevronDown } from 'lucide-react';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';
const Projects: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
const categories = groupProjectsByCategory().slice(0, 3); // Показываем только 3 категории
const [openCategories, setOpenCategories] = useState<{ [key: string]: boolean }>({
[categories[0]?.name]: true // Открываем первую категорию по умолчанию
@@ -21,16 +25,33 @@ const Projects: React.FC = () => {
<div className="container mx-auto px-6">
<div className="flex flex-col md:flex-row justify-between items-start md:items-end mb-16 gap-8">
<h2 className="text-4xl font-bold text-gray-900 max-w-xs leading-tight">
Наши недавние проекты
{isEnglish ? 'Our recent projects' : 'Наши недавние проекты'}
</h2>
<p className="text-gray-600 max-w-md text-sm leading-relaxed">
Наша команда всегда ответственно относится к проектам, которые вы нам доверили.
Спасибо, что вы рядом.
{isEnglish
? 'Our team treats every project with great responsibility. Thank you for your trust.'
: 'Наша команда всегда ответственно относится к проектам, которые вы нам доверили. Спасибо, что вы рядом.'}
</p>
</div>
<div className="space-y-4 mb-8">
{categories.map((category) => (
{categories.map((category) => {
const categoryName = isEnglish
? (() => {
switch (category.name) {
case 'Нефтегазовая промышленность':
return 'Oil & gas industry';
case 'Коммерческая недвижимость и туризм':
return 'Commercial real estate and tourism';
case 'Прочие объекты':
return 'Other facilities';
default:
return category.name;
}
})()
: category.name;
return (
<div key={category.name} className="border border-gray-200 rounded-xl overflow-hidden">
{/* Заголовок категории */}
<button
@@ -41,7 +62,7 @@ const Projects: React.FC = () => {
<div className="flex-shrink-0 w-12 h-12 bg-brand-orange text-white rounded-lg flex items-center justify-center font-bold text-lg">
{category.projects.length}
</div>
<h3 className="text-xl font-bold text-gray-900">{category.name}</h3>
<h3 className="text-xl font-bold text-gray-900">{categoryName}</h3>
</div>
<ChevronDown
size={24}
@@ -60,7 +81,61 @@ const Projects: React.FC = () => {
}`}
>
<div className="p-4 space-y-3 bg-white">
{category.projects.slice(0, 3).map((project, index) => (
{category.projects.slice(0, 3).map((project, index) => {
const title = isEnglish
? (() => {
switch (project.title) {
case 'ОАО «Газпромнефть-ОНПЗ»':
return '“GazpromneftONPZ” OJSC';
case 'ООО «Петон»':
return '“Peton” LLC';
case 'ОАО «Гипротрубопровод»':
return '“Giprotuboprovod” OJSC';
default:
return project.title;
}
})()
: project.title;
const description = isEnglish
? (() => {
switch (project.title) {
case 'ОАО «Газпромнефть-ОНПЗ»':
return 'Modernisation of unit 19/3 at GazpromneftONPZ in Omsk.';
case 'ООО «Петон»':
return 'L24/9. Technical upgrade. Heat recovery of diesel fuel hydrotreating unit (design and working documentation) at the GazpromneftONPZ site.';
case 'ОАО «Гипротрубопровод»':
return 'Reconstruction of the production base in Omsk.';
// Commercial real estate and tourism (examples)
case 'ООО «СУ-1 ОАО «Госстрой»':
return 'Fastfood restaurant “KFC” at the intersection of Marshala Zhukova and Akademika Korolyova streets in the Oktyabrsky district of Ufa.';
case 'ООО «Башкирский птицеводческий комплекс имени М. Гафури»':
return 'Architectural civil facility “Administrative and utility building” for the Bashkir Poultry Complex named after M. Gafuri.';
case 'ООО «Регион-Ресурс»':
return 'Construction of an ecotourism centre in the Kirovsky district of Ufa, Republic of Bashkortostan, including development of design documentation, support through expert review and approvals.';
case 'МБУ «Управление пожарной охраны ГО г. Уфа Республики Башкортостан':
return 'Fire station building with four bays located in the Leninsky district, settlement 8 Marta, Republic of Bashkortostan.';
case 'ООО «Управление капитального строительства «Монолитстрой»':
return 'Administrative, retail and entertainment complex in the Dema9 neighbourhood, Demsky district of Ufa, Republic of Bashkortostan.';
case 'ОАО «Уфимский хлопчатобумажный комбинат»':
return '“Ferris wheel58” on the site of the Ufa Cotton Mill at 137 Mendeleeva Street, Ufa.';
case 'ЗАО «Штрабаг»':
return 'Technology park of energyefficient technologies on a land plot within the administrative boundaries of the Ufimsky municipal district, Republic of Bashkortostan.';
case 'ООО «Уралстройсервис»':
return 'Underground garage with public service premises on the roof, at Rossiyskaya Street and Davletkildeyev Boulevard intersection in the Oktyabrsky district of Ufa.';
case 'ООО «РаушБиер»':
return 'Twostorey restaurant complex located at 8 Tukaeva Street, Tuymazy, Republic of Bashkortostan.';
case 'ООО СОК «Трамплин»':
return 'Ski tow lift in the Oktyabrsky district of Ufa, Republic of Bashkortostan.';
default:
return project.description;
}
})()
: project.description;
return (
<div
key={project.id}
className="group cursor-pointer bg-gray-50 hover:bg-gray-100 rounded-lg p-5 transition-all duration-300 border border-gray-200 hover:border-brand-orange"
@@ -71,28 +146,28 @@ const Projects: React.FC = () => {
</div>
<div className="flex-1">
<h4 className="text-base font-bold text-gray-900 mb-2 group-hover:text-brand-orange transition-colors">
{project.title}
{title}
</h4>
<div className="flex items-start gap-2 text-gray-600 text-sm">
<MapPin size={14} className="mt-1 flex-shrink-0 text-brand-orange" />
<span className="leading-relaxed line-clamp-2">{project.description}</span>
<span className="leading-relaxed line-clamp-2">{description}</span>
</div>
</div>
</div>
</div>
))}
)})}
</div>
</div>
</div>
))}
)})}
</div>
<div className="text-center">
<Link
to="/projects"
to={`${prefix}/projects`}
className="inline-block px-8 py-3 bg-brand-orange text-white font-bold rounded-lg hover:bg-orange-600 transition-colors"
>
Смотреть все проекты
{isEnglish ? 'View all projects' : 'Смотреть все проекты'}
</Link>
</div>
</div>

21
components/Seo.tsx Normal file
View File

@@ -0,0 +1,21 @@
import React from 'react';
import { Helmet } from 'react-helmet';
interface SeoProps {
title: string;
description?: string;
}
const Seo: React.FC<SeoProps> = ({ title, description }) => {
const fullTitle = `${title} | ГеоВектор`;
return (
<Helmet>
<title>{fullTitle}</title>
{description && <meta name="description" content={description} />}
</Helmet>
);
};
export default Seo;

View File

@@ -2,8 +2,13 @@ import React from 'react';
import { ArrowRight } from 'lucide-react';
import { Link } from 'react-router-dom';
import { SERVICES } from '../constants';
import { useLocation } from 'react-router-dom';
const Services: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
// Exclude Technical Tasks and show first 3 actual services
const actualServices = SERVICES.filter(service => service.title !== 'Технические задания');
const displayedServices = actualServices.slice(0, 3);
@@ -13,7 +18,6 @@ const Services: React.FC = () => {
const urlMap: { [key: string]: string } = {
'Инженерные изыскания': '/services/surveying',
'Проектирование': '/services/design',
'Строительство': '/services/construction',
'Обследование грунтов': '/services/soil-survey',
'Обследование здания': '/services/building-survey',
'Землестроительный и Кадастровые работы': '/services/land-survey'
@@ -25,9 +29,14 @@ const Services: React.FC = () => {
<div className="py-20 bg-gray-50" id="services">
<div className="container mx-auto px-6">
<div className="flex justify-between items-end mb-12">
<h2 className="text-4xl font-bold text-gray-900">Услуги</h2>
<Link to="/services" className="flex items-center gap-2 text-sm font-medium hover:text-brand-orange transition-colors">
Показать все <ArrowRight size={16} />
<h2 className="text-4xl font-bold text-gray-900">
{isEnglish ? 'Services' : 'Услуги'}
</h2>
<Link
to={`${prefix}/services`}
className="flex items-center gap-2 text-sm font-medium hover:text-brand-orange transition-colors"
>
{isEnglish ? 'Show all' : 'Показать все'} <ArrowRight size={16} />
</Link>
</div>
@@ -35,6 +44,42 @@ const Services: React.FC = () => {
{displayedServices.map((service, idx) => {
const detailUrl = getServiceUrl(service.title);
const title =
isEnglish
? (() => {
switch (service.title) {
case 'Инженерные изыскания':
return 'Engineering surveys';
case 'Проектирование':
return 'Design';
case 'Обследование грунтов':
return 'Soil investigation';
case 'Обследование здания':
return 'Building survey';
default:
return service.title;
}
})()
: service.title;
const description =
isEnglish
? (() => {
switch (service.title) {
case 'Инженерные изыскания':
return 'Comprehensive investigation of construction site conditions: engineering-geodetic, geological, hydrometeorological and environmental surveys.';
case 'Проектирование':
return 'Development of design and working documentation for civil and industrial facilities with architectural and structural solutions.';
case 'Обследование грунтов':
return 'Laboratory and field testing of soils. Determination of physical and mechanical properties for designing foundations and subgrades.';
case 'Обследование здания':
return 'Technical inspection of buildings and structures, assessment of load-bearing elements and recommendations for strengthening.';
default:
return service.description;
}
})()
: service.description;
return (
<div
key={idx}
@@ -42,32 +87,32 @@ const Services: React.FC = () => {
>
<img
src={service.image}
alt={service.title}
alt={title}
className="absolute inset-0 w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
loading="lazy"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/40 to-transparent" />
<div className="absolute bottom-0 left-0 p-8 w-full">
<h3 className="text-2xl font-bold text-white mb-3">{service.title}</h3>
<h3 className="text-2xl font-bold text-white mb-3">{title}</h3>
<p className="text-gray-300 text-sm mb-6 line-clamp-3 opacity-90">
{service.description}
{description}
</p>
<div className="flex flex-col gap-3">
{detailUrl && (
<Link
to={detailUrl}
to={`${prefix}${detailUrl}`}
className="inline-flex items-center justify-center gap-2 bg-brand-orange text-white font-bold px-5 py-2.5 rounded-lg hover:bg-orange-600 transition-colors"
>
Подробнее <ArrowRight size={18} />
{isEnglish ? 'Learn more' : 'Подробнее'} <ArrowRight size={18} />
</Link>
)}
<Link
to="/contacts"
to={`${prefix}/contacts`}
className="flex items-center gap-2 text-brand-orange text-sm font-medium group-hover:gap-4 transition-all"
>
Рассчитать стоимость <ArrowRight size={16} />
{isEnglish ? 'Request a quote' : 'Рассчитать стоимость'} <ArrowRight size={16} />
</Link>
</div>
</div>

View File

@@ -6,8 +6,6 @@ export const NAV_LINKS = [
{ label: 'О компании', href: '/about' },
{ label: 'Услуги', href: '/services' },
{ label: 'Проекты', href: '/projects' },
{ label: 'Автопарк', href: '/fleet' },
{ label: 'Сертификаты', href: '/certificates' },
{ label: 'Контакты', href: '/contacts' },
];
@@ -21,7 +19,7 @@ export const BENEFITS: BenefitItem[] = [
{
icon: ShieldCheck,
title: 'Надежность',
description: 'Надежную компанию, которая входит в ТОП-3 по Республике Башкортостан'
description: 'Надежную компанию, которая входит в ТОП-10 по России'
},
{
icon: Users,
@@ -56,11 +54,6 @@ export const SERVICES: ServiceItem[] = [
description: 'Разработка проектной и рабочей документации (ПД и РД) для объектов гражданского и промышленного назначения. Архитектурные и конструктивные решения.',
image: '/media/images/services/design.png'
},
{
title: 'Строительство',
description: 'Выполнение полного цикла строительно-монтажных работ. Возведение зданий, сооружений, ангаров, реконструкция и капитальный ремонт.',
image: '/media/images/services/construction.png'
},
{
title: 'Обследование грунтов',
description: 'Лабораторные и полевые испытания грунтов. Определение физико-механических свойств для расчета фундаментов и оснований.',

102
package-lock.json generated
View File

@@ -12,6 +12,7 @@
"lucide-react": "^0.555.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-helmet": "^6.1.0",
"react-router": "^6.22.3",
"react-router-dom": "^6.22.3"
},
@@ -56,6 +57,7 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -800,9 +802,9 @@
}
},
"node_modules/@remix-run/router": {
"version": "1.23.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.1.tgz",
"integrity": "sha512-vDbaOzF7yT2Qs4vO6XV1MHcJv+3dgR1sT+l3B8xxOVhUC336prMvqrvsLL/9Dnw2xr6Qhz4J0dmS0llNAbnUmQ==",
"version": "1.23.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
"integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
@@ -1181,6 +1183,7 @@
"integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
@@ -1274,6 +1277,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.25",
"caniuse-lite": "^1.0.30001754",
@@ -1454,7 +1458,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/jsesc": {
@@ -1483,6 +1486,18 @@
"node": ">=6"
}
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -1545,6 +1560,15 @@
"node": ">=0.10.0"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -1558,6 +1582,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -1585,6 +1610,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -1601,11 +1627,23 @@
"dev": true,
"license": "MIT"
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/react": {
"version": "19.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -1615,6 +1653,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -1622,6 +1661,42 @@
"react": "^19.2.0"
}
},
"node_modules/react-fast-compare": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
"license": "MIT"
},
"node_modules/react-helmet": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz",
"integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==",
"license": "MIT",
"dependencies": {
"object-assign": "^4.1.1",
"prop-types": "^15.7.2",
"react-fast-compare": "^3.1.1",
"react-side-effect": "^2.1.0"
},
"peerDependencies": {
"react": ">=16.3.0"
}
},
"node_modules/react-helmet/node_modules/react-side-effect": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz",
"integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==",
"license": "MIT",
"peerDependencies": {
"react": "^16.3.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/react-refresh": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
@@ -1633,12 +1708,12 @@
}
},
"node_modules/react-router": {
"version": "6.30.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.2.tgz",
"integrity": "sha512-H2Bm38Zu1bm8KUE5NVWRMzuIyAV8p/JrOaBJAwVmp37AXG72+CZJlEBw6pdn9i5TBgLMhNDgijS4ZlblpHyWTA==",
"version": "6.30.3",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
"integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.23.1"
"@remix-run/router": "1.23.2"
},
"engines": {
"node": ">=14.0.0"
@@ -1648,13 +1723,13 @@
}
},
"node_modules/react-router-dom": {
"version": "6.30.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.2.tgz",
"integrity": "sha512-l2OwHn3UUnEVUqc6/1VMmR1cvZryZ3j3NzapC2eUXO1dB0sYp5mvwdjiXhpUbRb21eFow3qSxpP8Yv6oAU824Q==",
"version": "6.30.3",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
"integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.23.1",
"react-router": "6.30.2"
"@remix-run/router": "1.23.2",
"react-router": "6.30.3"
},
"engines": {
"node": ">=14.0.0"
@@ -1814,6 +1889,7 @@
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",

View File

@@ -13,6 +13,7 @@
"lucide-react": "^0.555.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-helmet": "^6.1.0",
"react-router": "^6.22.3",
"react-router-dom": "^6.22.3"
},

View File

@@ -1,8 +1,16 @@
import React, { useRef, useEffect, useState } from 'react';
import PageHeader from '../components/PageHeader';
import { CheckCircle2, Award, Users, Building2, Briefcase, Phone, Mail, Globe, FileText, ExternalLink, Star } from 'lucide-react';
import { useLocation } from 'react-router-dom';
const AboutPage: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const headerTitle = isEnglish ? 'About the company' : 'О компании';
const headerDescription = isEnglish
? 'GeoVector is a leading engineering survey and design company based in the Republic of Bashkortostan.'
: 'ООО «ГеоВектор» — лидер в области инженерных изысканий и проектирования в Республике Башкортостан.';
const scrollContainerRef = useRef<HTMLDivElement>(null);
const [isDragging, setIsDragging] = useState(false);
const [startX, setStartX] = useState(0);
@@ -132,8 +140,8 @@ const AboutPage: React.FC = () => {
return (
<div className="bg-white pb-20">
<PageHeader
title="О компании"
description="ООО «ГеоВектор» — лидер в области инженерных изысканий и проектирования в Республике Башкортостан."
title={headerTitle}
description={headerDescription}
image="/media/images/headers/header-about.png"
/>

View File

@@ -1,8 +1,13 @@
import React from 'react';
import PageHeader from '../components/PageHeader';
import { CheckCircle2, Building, Factory, ShoppingCart, Network, Wrench, Target, Award, Users, TrendingUp } from 'lucide-react';
import { useLocation } from 'react-router-dom';
const ConstructionPage: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
const capabilities = [
{ icon: Building, title: 'Строительство административных, жилых и офисных зданий' },
{ icon: Factory, title: 'Строительство промышленных комплексов' },
@@ -51,8 +56,12 @@ const ConstructionPage: React.FC = () => {
return (
<div className="bg-white pb-20">
<PageHeader
title="Строительство"
description="Полный комплекс строительных и монтажных работ любой сложности"
title={isEnglish ? 'Construction' : 'Строительство'}
description={
isEnglish
? 'Full range of construction and installation works of any complexity.'
: 'Полный комплекс строительных и монтажных работ любой сложности'
}
image="/media/images/headers/header-building.png"
/>
@@ -61,10 +70,15 @@ const ConstructionPage: React.FC = () => {
<div className="max-w-5xl mx-auto mb-20">
<div className="bg-gradient-to-br from-brand-orange to-orange-600 text-white rounded-2xl p-8 md:p-12 mb-12">
<p className="text-xl leading-relaxed mb-4 font-semibold">
Строительство зданий и сооружений одно из приоритетных направлений работы ООО «ГеоВектор».
{isEnglish
? 'Construction of buildings and facilities is one of the core business areas of GeoVector LLC.'
: 'Строительство зданий и сооружений одно из приоритетных направлений работы ООО «ГеоВектор».'
}
</p>
<p className="text-lg leading-relaxed">
Наши услуги включают выполнение полного комплекса строительных и монтажных работ.
{isEnglish
? 'Our services cover the full cycle of construction and installation works.'
: 'Наши услуги включают выполнение полного комплекса строительных и монтажных работ.'}
</p>
</div>
</div>
@@ -72,10 +86,12 @@ const ConstructionPage: React.FC = () => {
{/* Возможности компании */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-4 text-center">
Возможности нашей компании
{isEnglish ? 'Our capabilities' : 'Возможности нашей компании'}
</h2>
<p className="text-center text-gray-600 mb-12 max-w-3xl mx-auto">
Мы предлагаем не только типовые строительные решения, но также индивидуальные разработки
{isEnglish
? 'We offer both standard construction solutions and fully tailored designs.'
: 'Мы предлагаем не только типовые строительные решения, но также индивидуальные разработки'}
</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
@@ -101,20 +117,23 @@ const ConstructionPage: React.FC = () => {
<div className="max-w-6xl mx-auto mb-20">
<div className="bg-gray-50 rounded-2xl p-8 md:p-12">
<h2 className="text-3xl font-bold text-gray-900 mb-6 text-center">
Преимущества комплексного подхода к строительству
{isEnglish
? 'Benefits of an integrated construction approach'
: 'Преимущества комплексного подхода к строительству'}
</h2>
<div className="space-y-6">
<p className="text-lg text-gray-700 leading-relaxed">
Применение современных технических средств на всех этапах строительства, высокий профессионализм
архитекторов, инженеров-строителей и дизайнеров выступают гарантией своевременного и качественного
выполнения любого заказа.
{isEnglish
? 'Use of modern technologies at all stages of construction and high professionalism of our architects, structural engineers and designers guarantee timely and high-quality delivery of any project.'
: 'Применение современных технических средств на всех этапах строительства, высокий профессионализм архитекторов, инженеров-строителей и дизайнеров выступают гарантией своевременного и качественного выполнения любого заказа.'}
</p>
<div className="bg-white rounded-xl p-6 border-l-4 border-brand-orange">
<p className="text-gray-700 leading-relaxed">
За время деятельности компания реализовала значительное число проектов, включая возведение
и реконструкцию объектов, находящихся в Республике Башкортостан и других регионах страны.
{isEnglish
? 'Over the years we have implemented numerous projects, including construction and reconstruction works in the Republic of Bashkortostan and other regions of Russia.'
: 'За время деятельности компания реализовала значительное число проектов, включая возведение и реконструкцию объектов, находящихся в Республике Башкортостан и других регионах страны.'}
</p>
</div>
</div>
@@ -124,11 +143,14 @@ const ConstructionPage: React.FC = () => {
{/* Факторы, учитываемые при строительстве */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-4 text-center">
Важнейшие факторы при выполнении строительства
{isEnglish
? 'Key factors we consider during construction'
: 'Важнейшие факторы при выполнении строительства'}
</h2>
<p className="text-center text-gray-600 mb-12 max-w-3xl mx-auto">
В процессе выполнения строительства мы учитываем все важнейшие факторы,
влияющие на эффективность будущего использования здания
{isEnglish
? 'During construction we consider all major factors that affect the efficiency and usability of the future facility.'
: 'В процессе выполнения строительства мы учитываем все важнейшие факторы, влияющие на эффективность будущего использования здания'}
</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
@@ -161,12 +183,15 @@ const ConstructionPage: React.FC = () => {
<Users size={48} className="text-brand-orange" />
</div>
<div>
<h3 className="text-2xl font-bold mb-4">Проектирование и строительство единый процесс</h3>
<h3 className="text-2xl font-bold mb-4">
{isEnglish
? 'Design and construction as a single process'
: 'Проектирование и строительство единый процесс'}
</h3>
<p className="text-gray-300 leading-relaxed">
Проектирование и строительство объектов взаимосвязанные процессы, каждый из которых требует
тщательного контроля. Наша компания может предложить заказчикам не только непосредственное
проведение строительных работ, но и профессиональные услуги на всех стадиях выполнения заказа
от проектирования до отделочных работ и ввода готового объекта в эксплуатацию.
{isEnglish
? 'Design and construction are tightly connected processes, each requiring careful control. We can support you at every stage from initial design to finishing works and commissioning of the facility.'
: 'Проектирование и строительство объектов взаимосвязанные процессы, каждый из которых требует тщательного контроля. Наша компания может предложить заказчикам не только непосредственное проведение строительных работ, но и профессиональные услуги на всех стадиях выполнения заказа от проектирования до отделочных работ и ввода готового объекта в эксплуатацию.'}
</p>
</div>
</div>
@@ -176,7 +201,9 @@ const ConstructionPage: React.FC = () => {
{/* Разделы комплексных проектов */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Выполнение разделов комплексных проектов
{isEnglish
? 'Execution of sections for complex projects'
: 'Выполнение разделов комплексных проектов'}
</h2>
<div className="bg-gray-50 rounded-2xl p-8 md:p-10">
@@ -197,7 +224,9 @@ const ConstructionPage: React.FC = () => {
{/* Специальные разделы */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Разработка специальных разделов
{isEnglish
? 'Development of specialised sections'
: 'Разработка специальных разделов'}
</h2>
<div className="grid md:grid-cols-3 gap-6">
@@ -221,7 +250,7 @@ const ConstructionPage: React.FC = () => {
<div className="max-w-5xl mx-auto">
<div className="bg-gray-50 rounded-2xl p-8 md:p-12">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Почему выбирают нас
{isEnglish ? 'Why clients choose us' : 'Почему выбирают нас'}
</h2>
<div className="grid md:grid-cols-2 gap-6 mb-8">
@@ -230,9 +259,13 @@ const ConstructionPage: React.FC = () => {
1
</div>
<div>
<h3 className="font-bold text-lg mb-2 text-gray-900">Комплексный подход</h3>
<h3 className="font-bold text-lg mb-2 text-gray-900">
{isEnglish ? 'Integrated approach' : 'Комплексный подход'}
</h3>
<p className="text-gray-600 text-sm leading-relaxed">
От проектирования до ввода объекта в эксплуатацию
{isEnglish
? 'From design to commissioning of the facility.'
: 'От проектирования до ввода объекта в эксплуатацию'}
</p>
</div>
</div>
@@ -242,9 +275,13 @@ const ConstructionPage: React.FC = () => {
2
</div>
<div>
<h3 className="font-bold text-lg mb-2 text-gray-900">Современные технологии</h3>
<h3 className="font-bold text-lg mb-2 text-gray-900">
{isEnglish ? 'Modern technologies' : 'Современные технологии'}
</h3>
<p className="text-gray-600 text-sm leading-relaxed">
Применение передовых технических средств на всех этапах
{isEnglish
? 'Use of advanced technologies at all stages.'
: 'Применение передовых технических средств на всех этапах'}
</p>
</div>
</div>
@@ -254,9 +291,13 @@ const ConstructionPage: React.FC = () => {
3
</div>
<div>
<h3 className="font-bold text-lg mb-2 text-gray-900">Профессиональная команда</h3>
<h3 className="font-bold text-lg mb-2 text-gray-900">
{isEnglish ? 'Professional team' : 'Профессиональная команда'}
</h3>
<p className="text-gray-600 text-sm leading-relaxed">
Высокий профессионализм архитекторов, инженеров и дизайнеров
{isEnglish
? 'High professionalism of architects, engineers and designers.'
: 'Высокий профессионализм архитекторов, инженеров и дизайнеров'}
</p>
</div>
</div>
@@ -266,9 +307,13 @@ const ConstructionPage: React.FC = () => {
4
</div>
<div>
<h3 className="font-bold text-lg mb-2 text-gray-900">Индивидуальный подход</h3>
<h3 className="font-bold text-lg mb-2 text-gray-900">
{isEnglish ? 'Individual approach' : 'Индивидуальный подход'}
</h3>
<p className="text-gray-600 text-sm leading-relaxed">
Типовые и индивидуальные строительные решения
{isEnglish
? 'Standard and fully customised construction solutions.'
: 'Типовые и индивидуальные строительные решения'}
</p>
</div>
</div>
@@ -279,13 +324,15 @@ const ConstructionPage: React.FC = () => {
<div className="mt-12 text-center">
<div className="bg-white rounded-2xl p-8 shadow-lg">
<p className="text-gray-700 mb-6 text-lg">
Готовы начать строительство вашего объекта?
{isEnglish
? 'Ready to start construction of your facility?'
: 'Готовы начать строительство вашего объекта?'}
</p>
<a
href="#/contacts"
href={`#${prefix}/contacts`}
className="inline-block px-10 py-4 bg-brand-orange text-white font-bold rounded-lg hover:bg-orange-600 transition-colors text-lg shadow-lg hover:shadow-xl"
>
Обсудить проект
{isEnglish ? 'Discuss the project' : 'Обсудить проект'}
</a>
</div>
</div>

View File

@@ -1,39 +1,92 @@
import React from 'react';
import PageHeader from '../components/PageHeader';
import { Phone, Mail, MapPin, Clock, Send } from 'lucide-react';
import { Phone, Mail, MapPin, Clock, Send, MessageCircle } from 'lucide-react';
import { useLocation } from 'react-router-dom';
const ContactsPage: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const title = isEnglish ? 'Contacts' : 'Контакты';
const description = isEnglish
? 'We are always available and ready to answer any questions you may have.'
: 'Мы всегда на связи и готовы ответить на любые ваши вопросы.';
return (
<div className="bg-white pb-20">
<PageHeader
title="Контакты"
description="Мы всегда на связи и готовы ответить на любые ваши вопросы."
title={title}
description={description}
image="/media/images/headers/header-kontakri.png"
/>
>
{/* Кнопки мессенджеров сразу под надписью \"Мы всегда на связи\" в хедере */}
<div className="flex flex-wrap justify-center gap-4 mt-4">
{/* Скрытая кнопка MAX — оставлена в коде на будущее */}
{false && (
<a
href="https://wa.me/79273444557"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-6 py-3 rounded-xl bg-gradient-to-r from-[#5b2cff] to-[#8b5dff] text-white font-semibold hover:from-[#4b23d6] hover:to-[#7446e0] transition-colors shadow-lg"
>
<MessageCircle size={20} />
<span>MAX</span>
</a>
)}
<a
href="https://t.me/Edgar_MER"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-6 py-3 rounded-xl bg-[#229ED9] text-white font-semibold hover:bg-[#1b80b3] transition-colors shadow-lg"
>
<Send size={20} />
<span>Telegram</span>
</a>
</div>
</PageHeader>
<div className="container mx-auto px-6 py-20">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 mb-20">
<div className="bg-brand-light p-8 rounded-2xl">
<Phone className="text-brand-orange w-8 h-8 mb-4" />
<h3 className="font-bold text-lg mb-2">Телефон</h3>
<a href="tel:83472927370" className="text-gray-600 hover:text-brand-orange transition-colors">8 (347) 292 73 70</a>
<h3 className="font-bold text-lg mb-2">
{isEnglish ? 'Phone' : 'Телефон'}
</h3>
<a href="tel:83472927370" className="text-gray-600 hover:text-brand-orange transition-colors">
8 (347) 292 73 70
</a>
</div>
<div className="bg-brand-light p-8 rounded-2xl">
<Mail className="text-brand-orange w-8 h-8 mb-4" />
<h3 className="font-bold text-lg mb-2">Email</h3>
<a href="mailto:gw@geowektor.ru" className="text-gray-600 hover:text-brand-orange transition-colors">gw@geowektor.ru</a>
<a href="mailto:gw@geowektor.ru" className="text-gray-600 hover:text-brand-orange transition-colors">
gw@geowektor.ru
</a>
</div>
<div className="bg-brand-light p-8 rounded-2xl">
<MapPin className="text-brand-orange w-8 h-8 mb-4" />
<h3 className="font-bold text-lg mb-2">Офис</h3>
<p className="text-gray-600">450001, РБ, г. Уфа,</p>
<p className="text-gray-600">ул. Комсомольская 19/1</p>
<h3 className="font-bold text-lg mb-2">
{isEnglish ? 'Office' : 'Офис'}
</h3>
<p className="text-gray-600">
{isEnglish ? '450001, Republic of Bashkortostan, Ufa,' : '450001, РБ, г. Уфа,'}
</p>
<p className="text-gray-600">
{isEnglish ? '19/1 Komsomolskaya St.' : 'ул. Комсомольская 19/1'}
</p>
</div>
<div className="bg-brand-light p-8 rounded-2xl">
<Clock className="text-brand-orange w-8 h-8 mb-4" />
<h3 className="font-bold text-lg mb-2">Режим работы</h3>
<p className="text-gray-600">Пн-Пт: 09:00 - 18:00</p>
<p className="text-gray-600">Сб-Вс: Выходной</p>
<h3 className="font-bold text-lg mb-2">
{isEnglish ? 'Working hours' : 'Режим работы'}
</h3>
<p className="text-gray-600">
{isEnglish ? 'MonFri: 09:0018:00' : 'Пн-Пт: 09:00 - 18:00'}
</p>
<p className="text-gray-600">
{isEnglish ? 'SatSun: Closed' : 'Сб-Вс: Выходной'}
</p>
</div>
</div>
@@ -43,17 +96,31 @@ const ContactsPage: React.FC = () => {
<div>
<div className="flex items-center gap-3 mb-6">
<Send className="w-10 h-10 text-brand-orange" />
<h2 className="text-3xl font-bold text-brand-dark">Мы в Telegram</h2>
<h2 className="text-3xl font-bold text-brand-dark">
{isEnglish ? 'We are on Telegram' : 'Мы в Telegram'}
</h2>
</div>
<p className="text-gray-600 text-lg mb-6 leading-relaxed">
Подписывайтесь на наш Telegram-канал, чтобы быть в курсе новостей компании, актуальных проектов и специальных предложений.
{isEnglish
? 'Subscribe to our Telegram channel to stay up to date with company news, current projects and special offers.'
: 'Подписывайтесь на наш Telegram-канал, чтобы быть в курсе новостей компании, актуальных проектов и специальных предложений.'}
</p>
<div className="space-y-3">
<p className="text-gray-700">
📱 Отсканируйте QR-код камерой телефона
{isEnglish
? '📱 Scan the QR code with your phone camera'
: '📱 Отсканируйте QR-код камерой телефона'}
</p>
<p className="text-gray-700">
💬 Или перейдите по ссылке: <a href="https://t.me/ooo_geo_wektor" target="_blank" rel="noopener noreferrer" className="text-brand-orange hover:underline font-semibold">@ooo_geo_wektor</a>
{isEnglish ? '💬 Or follow the link: ' : '💬 Или перейдите по ссылке: '}
<a
href="https://t.me/ooo_geo_wektor"
target="_blank"
rel="noopener noreferrer"
className="text-brand-orange hover:underline font-semibold"
>
@ooo_geo_wektor
</a>
</p>
</div>
</div>
@@ -66,7 +133,7 @@ const ContactsPage: React.FC = () => {
className="w-64 h-64 object-contain"
/>
<p className="text-center mt-4 text-sm text-gray-600 font-medium">
Наведите камеру на QR-код
{isEnglish ? 'Point your camera at the QR code' : 'Наведите камеру на QR-код'}
</p>
</div>
</div>

View File

@@ -1,8 +1,13 @@
import React from 'react';
import PageHeader from '../components/PageHeader';
import { CheckCircle2, Building2, Factory, Construction, Shield, Lightbulb, FileCheck } from 'lucide-react';
import { useLocation } from 'react-router-dom';
const DesignPage: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
const objectTypes = [
{
icon: Building2,
@@ -43,8 +48,12 @@ const DesignPage: React.FC = () => {
return (
<div className="bg-white pb-20">
<PageHeader
title="Проектирование"
description="Комплексные проектные решения для объектов любой сложности"
title={isEnglish ? 'Design' : 'Проектирование'}
description={
isEnglish
? 'Comprehensive design solutions for projects of any complexity.'
: 'Комплексные проектные решения для объектов любой сложности'
}
image="/media/images/headers/header-projectirovanie.png"
/>
@@ -53,32 +62,40 @@ const DesignPage: React.FC = () => {
<div className="max-w-5xl mx-auto mb-20">
<div className="bg-gray-50 rounded-2xl p-8 md:p-12 mb-12">
<p className="text-lg text-gray-700 leading-relaxed mb-6">
Специалисты нашей организации обладают значительным опытом в сфере проектирования объектов
в Республике Башкортостан и других регионах РФ.
{isEnglish
? 'Our specialists have extensive experience in designing facilities in the Republic of Bashkortostan and other regions of Russia.'
: 'Специалисты нашей организации обладают значительным опытом в сфере проектирования объектов в Республике Башкортостан и других регионах РФ.'}
</p>
<p className="text-gray-600 leading-relaxed mb-6">
{isEnglish ? (
<>
<strong>Designing construction facilities</strong> is a demanding and complex process that directly
influences the construction phase and successful commissioning of the facility.
</>
) : (
<>
<strong>Проектирование строительных объектов</strong> ответственный и сложный процесс,
влияющий на проведение строительства объекта и его успешное введение в эксплуатацию.
</>
)}
</p>
<p className="text-gray-600 leading-relaxed">
На всех стадиях проектирования мы используем современные проектные и технические решения,
что позволяет нам получать отличные результаты работ независимо от сложности объекта.
Наши специалисты работают с актуальной законодательной базой в сфере проектирования
и способны находить оптимальные решения с учетом нормативных требований и пожеланий заказчика.
{isEnglish
? 'At every stage of design we use modern engineering and technical solutions, which allows us to achieve excellent results regardless of project complexity. Our team works with up-to-date design regulations and finds optimal solutions that meet both regulatory requirements and the clients expectations.'
: 'На всех стадиях проектирования мы используем современные проектные и технические решения, что позволяет нам получать отличные результаты работ независимо от сложности объекта. Наши специалисты работают с актуальной законодательной базой в сфере проектирования и способны находить оптимальные решения с учетом нормативных требований и пожеланий заказчика.'}
</p>
</div>
<div className="bg-brand-orange text-white rounded-2xl p-8 md:p-12">
<p className="text-lg leading-relaxed mb-4">
Необходимость проведения определенных видов изысканий, их объем и состав определяются после
анализа технического задания и зависят от вида, уровня ответственности, назначения сооружений,
стадии проектирования, условий участка и степени их изученности.
{isEnglish
? 'The need for certain types of surveys, their scope and composition is determined after analysing the technical assignment and depends on the type and responsibility level of the facility, design stage and site conditions.'
: 'Необходимость проведения определенных видов изысканий, их объем и состав определяются после анализа технического задания и зависят от вида, уровня ответственности, назначения сооружений, стадии проектирования, условий участка и степени их изученности.'}
</p>
<p className="leading-relaxed">
Проведение инженерных изысканий подразумевает анализ данных, оценку возможных последствий
реализации проекта, получение санитарно-эпидемиологических заключений и составление технических
отчетов, состав которых определяется действующими нормативными документами. Наши специалисты
профессионально выполняют изыскания для строящихся или реконструируемых объектов любых типов.
{isEnglish
? 'Engineering surveys include data analysis, assessment of potential consequences of project implementation, obtaining sanitary and epidemiological approvals and preparing technical reports as required by current regulations. Our specialists perform surveys for both new and reconstruction projects of any type.'
: 'Проведение инженерных изысканий подразумевает анализ данных, оценку возможных последствий реализации проекта, получение санитарно-эпидемиологических заключений и составление технических отчетов, состав которых определяется действующими нормативными документами. Наши специалисты профессионально выполняют изыскания для строящихся или реконструируемых объектов любых типов.'}
</p>
</div>
</div>
@@ -86,10 +103,14 @@ const DesignPage: React.FC = () => {
{/* Типы объектов */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-4 text-center">
Предоставление комплексных услуг по проектированию
{isEnglish
? 'Comprehensive design services'
: 'Предоставление комплексных услуг по проектированию'}
</h2>
<p className="text-center text-gray-600 mb-12 max-w-3xl mx-auto">
ООО «ГеоВектор» осуществляет проектирование следующих объектов
{isEnglish
? 'GeoVector LLC provides design services for the following types of facilities:'
: 'ООО «ГеоВектор» осуществляет проектирование следующих объектов'}
</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-12">
@@ -117,9 +138,9 @@ const DesignPage: React.FC = () => {
<div className="flex items-start gap-3">
<Shield className="text-brand-orange flex-shrink-0 mt-1" size={24} />
<p className="text-gray-700 leading-relaxed text-sm">
В каждом случае учитываются требования к архитектурно-планировочным решениям объекта
и соответствующие нормы пожарной безопасности. Значительное внимание уделяется также
проектированию надежных систем внутренних и наружных инженерных коммуникаций.
{isEnglish
? 'For each project we take into account architectural and planning requirements and applicable fire safety standards, with special attention to the design of reliable internal and external engineering networks.'
: 'В каждом случае учитываются требования к архитектурно-планировочным решениям объекта и соответствующие нормы пожарной безопасности. Значительное внимание уделяется также проектированию надежных систем внутренних и наружных инженерных коммуникаций.'}
</p>
</div>
</div>
@@ -128,10 +149,9 @@ const DesignPage: React.FC = () => {
<div className="flex items-start gap-3">
<Lightbulb className="text-brand-orange flex-shrink-0 mt-1" size={24} />
<p className="text-gray-700 leading-relaxed text-sm">
Перед выполнением работ по проектированию совместно с заказчиком определяются основные
требования к проекту, в том числе состав помещений и наполнение зданий инженерным
оборудованием. Проектировочные работы выполняются с учетом требований, предъявляемых
к определенному объекту, особенностей и условий его эксплуатации.
{isEnglish
? 'Before starting design work we agree with the client on the key project requirements, including room programme and engineering systems. The design process always accounts for the facilitys specific operating conditions and regulatory constraints.'
: 'Перед выполнением работ по проектированию совместно с заказчиком определяются основные требования к проекту, в том числе состав помещений и наполнение зданий инженерным оборудованием. Проектировочные работы выполняются с учетом требований, предъявляемых к определенному объекту, особенностей и условий его эксплуатации.'}
</p>
</div>
</div>
@@ -152,7 +172,9 @@ const DesignPage: React.FC = () => {
{/* Разделы комплексных проектов */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Выполнение разделов комплексных проектов
{isEnglish
? 'Design of sections for integrated projects'
: 'Выполнение разделов комплексных проектов'}
</h2>
<div className="bg-gray-50 rounded-2xl p-8 md:p-10">
@@ -173,7 +195,9 @@ const DesignPage: React.FC = () => {
{/* Специальные разделы */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Разработка специальных разделов
{isEnglish
? 'Development of specialised sections'
: 'Разработка специальных разделов'}
</h2>
<div className="grid md:grid-cols-3 gap-6">
@@ -197,7 +221,7 @@ const DesignPage: React.FC = () => {
<div className="max-w-5xl mx-auto">
<div className="bg-gradient-to-br from-gray-900 to-gray-800 text-white rounded-2xl p-8 md:p-12">
<h2 className="text-3xl font-bold mb-8 text-center">
Почему выбирают нас
{isEnglish ? 'Why clients choose us' : 'Почему выбирают нас'}
</h2>
<div className="grid md:grid-cols-2 gap-8">
@@ -206,9 +230,13 @@ const DesignPage: React.FC = () => {
1
</div>
<div>
<h3 className="font-bold text-lg mb-2">Современные решения</h3>
<h3 className="font-bold text-lg mb-2">
{isEnglish ? 'Modern solutions' : 'Современные решения'}
</h3>
<p className="text-gray-300 text-sm leading-relaxed">
Используем передовые проектные и технические решения на всех стадиях работы
{isEnglish
? 'We rely on advanced design and engineering solutions at every stage of the project.'
: 'Используем передовые проектные и технические решения на всех стадиях работы'}
</p>
</div>
</div>
@@ -218,9 +246,13 @@ const DesignPage: React.FC = () => {
2
</div>
<div>
<h3 className="font-bold text-lg mb-2">Актуальная база</h3>
<h3 className="font-bold text-lg mb-2">
{isEnglish ? 'Up-to-date regulations' : 'Актуальная база'}
</h3>
<p className="text-gray-300 text-sm leading-relaxed">
Работаем с актуальной законодательной базой в сфере проектирования
{isEnglish
? 'We work strictly within the current regulatory framework in the field of design.'
: 'Работаем с актуальной законодательной базой в сфере проектирования'}
</p>
</div>
</div>
@@ -230,9 +262,13 @@ const DesignPage: React.FC = () => {
3
</div>
<div>
<h3 className="font-bold text-lg mb-2">Опыт в регионе</h3>
<h3 className="font-bold text-lg mb-2">
{isEnglish ? 'Regional experience' : 'Опыт в регионе'}
</h3>
<p className="text-gray-300 text-sm leading-relaxed">
Значительный опыт проектирования в Республике Башкортостан и других регионах РФ
{isEnglish
? 'Extensive experience in the Republic of Bashkortostan and other Russian regions.'
: 'Значительный опыт проектирования в Республике Башкортостан и других регионах РФ'}
</p>
</div>
</div>
@@ -242,9 +278,13 @@ const DesignPage: React.FC = () => {
4
</div>
<div>
<h3 className="font-bold text-lg mb-2">Индивидуальный подход</h3>
<h3 className="font-bold text-lg mb-2">
{isEnglish ? 'Individual approach' : 'Индивидуальный подход'}
</h3>
<p className="text-gray-300 text-sm leading-relaxed">
Находим оптимальные решения с учетом нормативов и пожеланий заказчика
{isEnglish
? 'We find optimal solutions that balance regulations and the clients goals.'
: 'Находим оптимальные решения с учетом нормативов и пожеланий заказчика'}
</p>
</div>
</div>
@@ -255,13 +295,13 @@ const DesignPage: React.FC = () => {
<div className="mt-12 text-center">
<div className="bg-gray-50 rounded-2xl p-8">
<p className="text-gray-700 mb-6 text-lg">
Готовы обсудить ваш проект?
{isEnglish ? 'Ready to discuss your project?' : 'Готовы обсудить ваш проект?'}
</p>
<a
href="#/contacts"
href={`#${prefix}/contacts`}
className="inline-block px-10 py-4 bg-brand-orange text-white font-bold rounded-lg hover:bg-orange-600 transition-colors text-lg shadow-lg hover:shadow-xl"
>
Получить консультацию
{isEnglish ? 'Request a consultation' : 'Получить консультацию'}
</a>
</div>
</div>

View File

@@ -1,8 +1,11 @@
import React, { useState } from 'react';
import PageHeader from '../components/PageHeader';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { useLocation } from 'react-router-dom';
const FleetPage: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const [currentImageIndex, setCurrentImageIndex] = useState(0);
// Список всех фотографий из папки cars
@@ -70,8 +73,12 @@ const FleetPage: React.FC = () => {
return (
<div className="bg-white pb-20">
<PageHeader
title="Автопарк"
description="Современная техника для выполнения работ любой сложности"
title={isEnglish ? 'Fleet' : 'Автопарк'}
description={
isEnglish
? 'Modern equipment for projects of any complexity.'
: 'Современная техника для выполнения работ любой сложности'
}
image="/media/images/headers/header-fleet.png"
/>
@@ -79,7 +86,7 @@ const FleetPage: React.FC = () => {
{/* Карусель фотографий */}
<div className="mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Галерея техники
{isEnglish ? 'Equipment gallery' : 'Галерея техники'}
</h2>
<div className="relative max-w-5xl mx-auto">
@@ -87,7 +94,11 @@ const FleetPage: React.FC = () => {
<div className="relative aspect-[16/9] rounded-2xl overflow-hidden bg-gray-100">
<img
src={carImages[currentImageIndex]}
alt={`Транспортное средство ${currentImageIndex + 1}`}
alt={
isEnglish
? `Vehicle ${currentImageIndex + 1}`
: `Транспортное средство ${currentImageIndex + 1}`
}
className="w-full h-full object-cover"
/>
@@ -95,7 +106,7 @@ const FleetPage: React.FC = () => {
<button
onClick={prevImage}
className="absolute left-4 top-1/2 -translate-y-1/2 bg-white/90 hover:bg-white p-3 rounded-full shadow-lg transition-all duration-300 group"
aria-label="Предыдущее фото"
aria-label={isEnglish ? 'Previous photo' : 'Предыдущее фото'}
>
<ChevronLeft size={24} className="text-brand-orange" />
</button>
@@ -103,7 +114,7 @@ const FleetPage: React.FC = () => {
<button
onClick={nextImage}
className="absolute right-4 top-1/2 -translate-y-1/2 bg-white/90 hover:bg-white p-3 rounded-full shadow-lg transition-all duration-300 group"
aria-label="Следующее фото"
aria-label={isEnglish ? 'Next photo' : 'Следующее фото'}
>
<ChevronRight size={24} className="text-brand-orange" />
</button>
@@ -128,7 +139,7 @@ const FleetPage: React.FC = () => {
>
<img
src={image}
alt={`Миниатюра ${index + 1}`}
alt={isEnglish ? `Thumbnail ${index + 1}` : `Миниатюра ${index + 1}`}
className="w-full h-full object-cover"
/>
</button>
@@ -140,7 +151,9 @@ const FleetPage: React.FC = () => {
{/* Список транспортных средств */}
<div className="max-w-6xl mx-auto">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Список транспортных средств ООО «ГЕОВЕКТОР»
{isEnglish
? 'List of vehicles of GeoVector LLC'
: 'Список транспортных средств ООО «ГЕОВЕКТОР»'}
</h2>
<div className="overflow-x-auto shadow-lg rounded-2xl">
@@ -148,8 +161,12 @@ const FleetPage: React.FC = () => {
<thead>
<tr className="bg-brand-orange text-white">
<th className="px-6 py-4 text-left font-bold w-20"></th>
<th className="px-6 py-4 text-left font-bold">Тип авто</th>
<th className="px-6 py-4 text-left font-bold">Марка машины</th>
<th className="px-6 py-4 text-left font-bold">
{isEnglish ? 'Vehicle type' : 'Тип авто'}
</th>
<th className="px-6 py-4 text-left font-bold">
{isEnglish ? 'Make/model' : 'Марка машины'}
</th>
</tr>
</thead>
<tbody>
@@ -164,7 +181,42 @@ const FleetPage: React.FC = () => {
{index + 1}
</td>
<td className="px-6 py-4 text-gray-700">
{vehicle.type || ''}
{isEnglish
? (() => {
switch (vehicle.type) {
case 'Установка разведочного бурения':
return 'Exploration drilling rig';
case 'Специальное пассажирское ТС':
return 'Special-purpose passenger vehicle';
case 'Грузовой бортовой':
return 'Flatbed truck';
case 'УБШМ 1.20':
return 'UBShM 1.20 (drilling support vehicle)';
case 'Грузовое ТС, Фермер грузопассажирский УБШМ 1-13':
return 'Cargo vehicle, UBShM 1-13 crew cab (cargo-passenger)';
case 'Грузовой фургон':
return 'Box truck / cargo van';
case 'Установка разведочного бурения УРБ-2Д3':
return 'Exploration drilling rig URB-2D3';
case 'Седельный тягач':
return 'Tractor unit / semi-trailer truck';
case 'Полуприцеп низкорамный трал':
return 'Low-bed semi-trailer (lowboy)';
case 'Снегоход':
return 'Snowmobile';
case 'Прицеп для перевозки водной техники, грузов':
return 'Trailer for watercraft and cargo';
case 'Специальная установка каротажной станции СК 1-74-II':
return 'Special logging station unit SK 1-74-II';
case 'Фермер грузопассажирский УБШМ 1-13':
return 'UBShM 1-13 crew cab (cargo-passenger)';
case 'Болотовездеход':
return 'Amphibious all-terrain vehicle';
default:
return vehicle.type || '';
}
})()
: vehicle.type || ''}
</td>
<td className="px-6 py-4 font-semibold text-gray-900">
{vehicle.brand}
@@ -179,15 +231,21 @@ const FleetPage: React.FC = () => {
<div className="mt-12 grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="bg-brand-orange text-white rounded-2xl p-6 text-center">
<div className="text-4xl font-bold mb-2">{vehicles.length}</div>
<div className="text-lg">Единиц техники</div>
<div className="text-lg">
{isEnglish ? 'Units of equipment' : 'Единиц техники'}
</div>
</div>
<div className="bg-gray-900 text-white rounded-2xl p-6 text-center">
<div className="text-4xl font-bold mb-2">{carImages.length}</div>
<div className="text-lg">Фотографий</div>
<div className="text-lg">
{isEnglish ? 'Photos' : 'Фотографий'}
</div>
</div>
<div className="bg-brand-orange text-white rounded-2xl p-6 text-center">
<div className="text-4xl font-bold mb-2">24/7</div>
<div className="text-lg">Готовность к работе</div>
<div className="text-lg">
{isEnglish ? 'Ready for work' : 'Готовность к работе'}
</div>
</div>
</div>
</div>

View File

@@ -1,19 +1,67 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import Hero from '../components/Hero';
import Benefits from '../components/Benefits';
import Services from '../components/Services';
import Projects from '../components/Projects';
import Laboratories from '../components/Laboratories';
import Process from '../components/Process';
import FleetTeaser from '../components/FleetTeaser';
import Seo from '../components/Seo';
import { getHomepage, HomepageData } from '../src/api/strapiClient';
import { useLocation } from 'react-router-dom';
const Home: React.FC = () => {
const [homepage, setHomepage] = useState<HomepageData | null>(null);
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const locale = isEnglish ? 'en' : 'ru';
useEffect(() => {
getHomepage(locale)
.then((data) => setHomepage(data))
.catch(() => {
// Молча используем дефолтные тексты при ошибке
});
}, [locale]);
const defaultSeoTitleRu = 'Инженерные изыскания и проектирование для сложных объектов';
const defaultSeoTitleEn = 'Engineering surveys and design for complex construction projects';
const defaultSeoDescriptionRu =
'ООО «ГеоВектор» выполняет полный комплекс инженерных изысканий и проектных работ для девелоперов, промышленных предприятий и госзаказчиков в России и других странах.';
const defaultSeoDescriptionEn =
'GeoVector provides a full range of engineering surveys and design services for developers, industrial companies and public sector clients in Russia and other regions.';
const seoTitle =
homepage?.seoTitle || (isEnglish ? defaultSeoTitleEn : defaultSeoTitleRu);
const seoDescription =
homepage?.seoDescription || (isEnglish ? defaultSeoDescriptionEn : defaultSeoDescriptionRu);
const defaultHeroTitleRu = 'Инженерные изыскания и проектирование для сложных и ответственных объектов';
const defaultHeroTitleEn = 'Engineering surveys and design for complex and demanding projects';
const defaultHeroSubtitleRu =
'ООО «ГеоВектор» — полный комплекс инженерных изысканий и проектных решений для девелоперов, промышленных предприятий и госзаказчиков в России и других странах.';
const defaultHeroSubtitleEn =
'GeoVector provides end-to-end engineering surveys and design solutions for developers, industrial companies and public sector clients in Russia and other regions.';
const heroTitle = homepage?.heroTitle || (isEnglish ? defaultHeroTitleEn : defaultHeroTitleRu);
const heroSubtitle =
homepage?.heroSubtitle || (isEnglish ? defaultHeroSubtitleEn : defaultHeroSubtitleRu);
return (
<>
<Hero />
<Seo
title={seoTitle}
description={seoDescription}
/>
<Hero title={heroTitle} subtitle={heroSubtitle} />
<Benefits />
<Services />
<Projects />
<Laboratories />
<FleetTeaser />
<Process />
</>
);

View File

@@ -1,60 +1,100 @@
import React from 'react';
import PageHeader from '../components/PageHeader';
import { CheckCircle2, Map, FileText, MapPin, Landmark, ClipboardList, Navigation } from 'lucide-react';
import { useLocation } from 'react-router-dom';
const LandSurveyPage: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const services = [
{
icon: Landmark,
title: 'Рекультивация нарушенных земель',
description: 'Восстановление земель после промышленной деятельности и приведение их в состояние, пригодное для дальнейшего использования'
title: isEnglish ? 'Reclamation of disturbed lands' : 'Рекультивация нарушенных земель',
description: isEnglish
? 'Restoration of land affected by industrial activities and bringing it into a condition suitable for further use.'
: 'Восстановление земель после промышленной деятельности и приведение их в состояние, пригодное для дальнейшего использования'
},
{
icon: FileText,
title: 'Подготовка материалов к актам выбора земельного участка лесного фонда',
description: 'Комплексная подготовка документации и согласование с надзорными инстанциями для выбора участков лесного фонда'
title: isEnglish
? 'Preparation of documentation for selecting forest land plots'
: 'Подготовка материалов к актам выбора земельного участка лесного фонда',
description: isEnglish
? 'Comprehensive preparation of documentation and coordination with supervisory authorities for selecting forest fund land plots.'
: 'Комплексная подготовка документации и согласование с надзорными инстанциями для выбора участков лесного фонда'
},
{
icon: Map,
title: 'Градостроительный план земельного участка',
description: 'Разработка градостроительного плана с указанием всех необходимых параметров и ограничений использования территории'
title: isEnglish
? 'Urban development plan of a land plot'
: 'Градостроительный план земельного участка',
description: isEnglish
? 'Development of an urban development plan indicating all required parameters and landuse restrictions for the territory.'
: 'Разработка градостроительного плана с указанием всех необходимых параметров и ограничений использования территории'
},
{
icon: ClipboardList,
title: 'Проект планировки и проект межевания территории',
description: 'Разработка проектов планировки и межевания для комплексного освоения территорий и организации рационального землепользования'
title: isEnglish
? 'Planning and subdivision (demarcation) projects'
: 'Проект планировки и проект межевания территории',
description: isEnglish
? 'Development of planning and subdivision projects for integrated development of territories and rational land use.'
: 'Разработка проектов планировки и межевания для комплексного освоения территорий и организации рационального землепользования'
},
{
icon: Navigation,
title: 'Получение технических условий',
description: 'Получение технических условий на подключение к инженерным сетям и дальнейшее согласование в соответствующих организациях'
title: isEnglish ? 'Obtaining technical specifications' : 'Получение технических условий',
description: isEnglish
? 'Obtaining technical conditions for connection to engineering networks and further coordination with the relevant utility providers.'
: 'Получение технических условий на подключение к инженерным сетям и дальнейшее согласование в соответствующих организациях'
}
];
const cadastralWorks = [
{
title: 'Согласование границ земельных участков',
description: 'Проведение работ по установлению и согласованию границ земельных участков с заинтересованными лицами и смежными землепользователями'
title: isEnglish
? 'Boundary agreement for land plots'
: 'Согласование границ земельных участков',
description: isEnglish
? 'Carrying out works to establish and agree land plot boundaries with interested parties and adjoining land users.'
: 'Проведение работ по установлению и согласованию границ земельных участков с заинтересованными лицами и смежными землепользователями'
},
{
title: 'Определение координат и границ межевых знаков',
description: 'Геодезические измерения и закрепление межевых знаков на местности с использованием современного оборудования'
title: isEnglish
? 'Determining coordinates and boundaries of boundary markers'
: 'Определение координат и границ межевых знаков',
description: isEnglish
? 'Geodetic measurements and setting boundary markers in the field using modern surveying equipment.'
: 'Геодезические измерения и закрепление межевых знаков на местности с использованием современного оборудования'
},
{
title: 'Определение площади земельного участка',
description: 'Точный расчет площади земельного участка по полученным координатам межевых знаков с применением современных методик'
title: isEnglish
? 'Determining the land plot area'
: 'Определение площади земельного участка',
description: isEnglish
? 'Accurate calculation of the land plot area based on boundary marker coordinates using modern methods.'
: 'Точный расчет площади земельного участка по полученным координатам межевых знаков с применением современных методик'
},
{
title: 'Формирование пакета документов',
description: 'Подготовка полного пакета документов и последующая сдача землеустроительного дела (межевой план) в соответствующие государственные органы'
title: isEnglish
? 'Document package preparation'
: 'Формирование пакета документов',
description: isEnglish
? 'Preparation of the full set of documents and subsequent submission of the land management file (cadastral plan) to the relevant state authorities.'
: 'Подготовка полного пакета документов и последующая сдача землеустроительного дела (межевой план) в соответствующие государственные органы'
}
];
return (
<div className="bg-white pb-20">
<PageHeader
title="Землеустроительные и кадастровые работы"
description="Комплекс услуг по межеванию, кадастровому учету и землеустройству"
title={isEnglish ? 'Land management and cadastral works' : 'Землеустроительные и кадастровые работы'}
description={
isEnglish
? 'A comprehensive range of services in land surveying, cadastral registration and land management.'
: 'Комплекс услуг по межеванию, кадастровому учету и землеустройству'
}
image="/media/images/headers/header-zemlestroit-kadastr-jobs.png"
/>
@@ -62,10 +102,12 @@ const LandSurveyPage: React.FC = () => {
{/* Основные услуги */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-4 text-center">
Основные виды работ
{isEnglish ? 'Main types of works' : 'Основные виды работ'}
</h2>
<p className="text-center text-gray-600 mb-12 max-w-3xl mx-auto">
Профессиональное выполнение землеустроительных и кадастровых работ любой сложности
{isEnglish
? 'Professional execution of land management and cadastral works of any complexity.'
: 'Профессиональное выполнение землеустроительных и кадастровых работ любой сложности'}
</p>
<div className="space-y-6">
@@ -97,10 +139,14 @@ const LandSurveyPage: React.FC = () => {
<div className="text-center mb-12">
<div className="inline-flex items-center gap-3 bg-brand-orange text-white px-8 py-4 rounded-full mb-4">
<MapPin size={32} />
<span className="text-xl font-bold">КАДАСТРОВЫЕ РАБОТЫ</span>
<span className="text-xl font-bold">
{isEnglish ? 'CADASTRAL WORKS' : 'КАДАСТРОВЫЕ РАБОТЫ'}
</span>
</div>
<p className="text-gray-600 max-w-3xl mx-auto">
Комплексное выполнение работ по межеванию земельных участков и подготовке документации для государственного кадастрового учета
{isEnglish
? 'Comprehensive execution of boundary survey works and preparation of documentation for state cadastral registration.'
: 'Комплексное выполнение работ по межеванию земельных участков и подготовке документации для государственного кадастрового учета'}
</p>
</div>
@@ -129,7 +175,7 @@ const LandSurveyPage: React.FC = () => {
{/* Этапы работы */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-12 text-center">
Этапы выполнения работ
{isEnglish ? 'Work stages' : 'Этапы выполнения работ'}
</h2>
<div className="relative">
@@ -141,8 +187,14 @@ const LandSurveyPage: React.FC = () => {
1
</div>
<div className="flex-1 bg-white rounded-xl p-6 shadow-md">
<h3 className="text-lg font-bold text-gray-900 mb-2">Получение заявки и выезд на объект</h3>
<p className="text-gray-600 text-sm">Первичное обследование территории и определение объема работ</p>
<h3 className="text-lg font-bold text-gray-900 mb-2">
{isEnglish ? 'Receiving the request and site visit' : 'Получение заявки и выезд на объект'}
</h3>
<p className="text-gray-600 text-sm">
{isEnglish
? 'Initial site survey and defining the scope of work.'
: 'Первичное обследование территории и определение объема работ'}
</p>
</div>
</div>
@@ -151,8 +203,14 @@ const LandSurveyPage: React.FC = () => {
2
</div>
<div className="flex-1 bg-white rounded-xl p-6 shadow-md">
<h3 className="text-lg font-bold text-gray-900 mb-2">Геодезические измерения</h3>
<p className="text-gray-600 text-sm">Проведение измерений и закрепление границ межевыми знаками</p>
<h3 className="text-lg font-bold text-gray-900 mb-2">
{isEnglish ? 'Geodetic measurements' : 'Геодезические измерения'}
</h3>
<p className="text-gray-600 text-sm">
{isEnglish
? 'Carrying out field measurements and setting boundaries with boundary markers.'
: 'Проведение измерений и закрепление границ межевыми знаками'}
</p>
</div>
</div>
@@ -161,8 +219,14 @@ const LandSurveyPage: React.FC = () => {
3
</div>
<div className="flex-1 bg-white rounded-xl p-6 shadow-md">
<h3 className="text-lg font-bold text-gray-900 mb-2">Согласование границ</h3>
<p className="text-gray-600 text-sm">Согласование установленных границ со смежными землепользователями</p>
<h3 className="text-lg font-bold text-gray-900 mb-2">
{isEnglish ? 'Boundary agreement' : 'Согласование границ'}
</h3>
<p className="text-gray-600 text-sm">
{isEnglish
? 'Agreeing established boundaries with adjoining land users.'
: 'Согласование установленных границ со смежными землепользователями'}
</p>
</div>
</div>
@@ -171,8 +235,14 @@ const LandSurveyPage: React.FC = () => {
4
</div>
<div className="flex-1 bg-white rounded-xl p-6 shadow-md">
<h3 className="text-lg font-bold text-gray-900 mb-2">Подготовка документации</h3>
<p className="text-gray-600 text-sm">Формирование межевого плана и полного пакета документов</p>
<h3 className="text-lg font-bold text-gray-900 mb-2">
{isEnglish ? 'Documentation preparation' : 'Подготовка документации'}
</h3>
<p className="text-gray-600 text-sm">
{isEnglish
? 'Preparation of the cadastral (boundary) plan and the complete document package.'
: 'Формирование межевого плана и полного пакета документов'}
</p>
</div>
</div>
@@ -181,8 +251,14 @@ const LandSurveyPage: React.FC = () => {
5
</div>
<div className="flex-1 bg-white rounded-xl p-6 shadow-md">
<h3 className="text-lg font-bold text-gray-900 mb-2">Сдача в государственные органы</h3>
<p className="text-gray-600 text-sm">Передача документов в Росреестр для постановки на кадастровый учет</p>
<h3 className="text-lg font-bold text-gray-900 mb-2">
{isEnglish ? 'Submission to state authorities' : 'Сдача в государственные органы'}
</h3>
<p className="text-gray-600 text-sm">
{isEnglish
? 'Submitting documents to Rosreestr for state cadastral registration.'
: 'Передача документов в Росреестр для постановки на кадастровый учет'}
</p>
</div>
</div>
</div>
@@ -193,7 +269,7 @@ const LandSurveyPage: React.FC = () => {
<div className="max-w-6xl mx-auto">
<div className="bg-gradient-to-br from-gray-900 to-gray-800 text-white rounded-2xl p-8 md:p-12 mb-12">
<h2 className="text-3xl font-bold mb-8 text-center">
Наши преимущества
{isEnglish ? 'Our advantages' : 'Наши преимущества'}
</h2>
<div className="grid md:grid-cols-3 gap-8">
@@ -201,9 +277,13 @@ const LandSurveyPage: React.FC = () => {
<div className="inline-flex w-16 h-16 bg-brand-orange rounded-xl items-center justify-center mb-4">
<CheckCircle2 size={32} />
</div>
<h3 className="font-bold text-lg mb-3">Полное сопровождение</h3>
<h3 className="font-bold text-lg mb-3">
{isEnglish ? 'Endtoend support' : 'Полное сопровождение'}
</h3>
<p className="text-gray-300 text-sm leading-relaxed">
От измерений на местности до получения свидетельства о регистрации
{isEnglish
? 'From field measurements to obtaining the registration certificate.'
: 'От измерений на местности до получения свидетельства о регистрации'}
</p>
</div>
@@ -211,9 +291,13 @@ const LandSurveyPage: React.FC = () => {
<div className="inline-flex w-16 h-16 bg-brand-orange rounded-xl items-center justify-center mb-4">
<Navigation size={32} />
</div>
<h3 className="font-bold text-lg mb-3">Точность измерений</h3>
<h3 className="font-bold text-lg mb-3">
{isEnglish ? 'Measurement accuracy' : 'Точность измерений'}
</h3>
<p className="text-gray-300 text-sm leading-relaxed">
Использование современного геодезического оборудования высокой точности
{isEnglish
? 'Use of modern highprecision geodetic equipment.'
: 'Использование современного геодезического оборудования высокой точности'}
</p>
</div>
@@ -221,9 +305,13 @@ const LandSurveyPage: React.FC = () => {
<div className="inline-flex w-16 h-16 bg-brand-orange rounded-xl items-center justify-center mb-4">
<FileText size={32} />
</div>
<h3 className="font-bold text-lg mb-3">Юридическая чистота</h3>
<h3 className="font-bold text-lg mb-3">
{isEnglish ? 'Legal compliance' : 'Юридическая чистота'}
</h3>
<p className="text-gray-300 text-sm leading-relaxed">
Грамотное оформление всех документов в соответствии с законодательством
{isEnglish
? 'Proper preparation of all documents in strict accordance with current legislation.'
: 'Грамотное оформление всех документов в соответствии с законодательством'}
</p>
</div>
</div>
@@ -233,13 +321,15 @@ const LandSurveyPage: React.FC = () => {
<div className="text-center">
<div className="bg-gray-50 rounded-2xl p-8">
<p className="text-gray-700 mb-6 text-lg">
Нужны землеустроительные или кадастровые работы?
{isEnglish
? 'Do you need land management or cadastral services?'
: 'Нужны землеустроительные или кадастровые работы?'}
</p>
<a
href="#/contacts"
href={`#${isEnglish ? '/en/contacts' : '/contacts'}`}
className="inline-block px-10 py-4 bg-brand-orange text-white font-bold rounded-lg hover:bg-orange-600 transition-colors text-lg shadow-lg hover:shadow-xl"
>
Заказать услугу
{isEnglish ? 'Order a service' : 'Заказать услугу'}
</a>
</div>
</div>

View File

@@ -2,18 +2,29 @@ import React from 'react';
import PageHeader from '../components/PageHeader';
import { SERVICES } from '../constants';
import { ArrowRight } from 'lucide-react';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';
const ServicesPage: React.FC = () => {
// Отфильтровываем технические задания из основных услуг
const mainServices = SERVICES.filter(service => service.title !== 'Технические задания');
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
const pageTitle = isEnglish ? 'Our services' : 'Наши услуги';
const pageDescription = isEnglish
? 'A full range of engineering and construction services from surveys to turnkey delivery.'
: 'Полный спектр инженерных и строительных работ любой сложности. От изысканий до сдачи объекта под ключ.';
// Отфильтровываем технические задания и строительство из основных услуг
const mainServices = SERVICES.filter(
service => service.title !== 'Технические задания' && service.title !== 'Строительство'
);
const technicalTasksService = SERVICES.find(service => service.title === 'Технические задания');
return (
<div className="bg-gray-50 pb-20">
<PageHeader
title="Наши услуги"
description="Полный спектр инженерных и строительных работ любой сложности. От изысканий до сдачи объекта под ключ."
title={pageTitle}
description={pageDescription}
image="/media/images/headers/header-services.png"
/>
@@ -34,18 +45,19 @@ const ServicesPage: React.FC = () => {
<div className="flex-1">
<h3 className="text-2xl font-bold text-gray-900 mb-3">
Образцы технических заданий
{isEnglish ? 'Technical assignment templates' : 'Образцы технических заданий'}
</h3>
<p className="text-gray-600 leading-relaxed mb-6">
Готовые шаблоны и примеры технических заданий для различных видов инженерных работ.
Документы доступны для скачивания и могут быть адаптированы под ваш проект.
{isEnglish
? 'Ready-made templates and examples of technical assignments for different types of engineering work. The documents are available for download and can be adapted to your project.'
: 'Готовые шаблоны и примеры технических заданий для различных видов инженерных работ. Документы доступны для скачивания и могут быть адаптированы под ваш проект.'}
</p>
<Link
to="/services/technical-tasks"
to={`${prefix}/services/technical-tasks`}
className="inline-flex items-center gap-2 px-6 py-3 bg-brand-orange text-white font-bold rounded-lg hover:bg-orange-600 transition-colors"
>
Посмотреть образцы <ArrowRight size={20} />
{isEnglish ? 'View templates' : 'Посмотреть образцы'} <ArrowRight size={20} />
</Link>
</div>
</div>
@@ -55,7 +67,48 @@ const ServicesPage: React.FC = () => {
)}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{mainServices.map((service, idx) => (
{mainServices.map((service, idx) => {
const title =
isEnglish
? (() => {
switch (service.title) {
case 'Инженерные изыскания':
return 'Engineering surveys';
case 'Проектирование':
return 'Design';
case 'Обследование грунтов':
return 'Soil investigation';
case 'Обследование здания':
return 'Building survey';
case 'Землестроительный и Кадастровые работы':
return 'Land management and cadastral works';
default:
return service.title;
}
})()
: service.title;
const description =
isEnglish
? (() => {
switch (service.title) {
case 'Инженерные изыскания':
return 'Comprehensive investigation of construction site conditions: engineering-geodetic, geological, hydrometeorological and environmental surveys.';
case 'Проектирование':
return 'Development of design and working documentation for civil and industrial facilities, including architectural and structural solutions.';
case 'Обследование грунтов':
return 'Laboratory and field testing of soils. Determination of physical and mechanical properties for designing foundations and subgrades.';
case 'Обследование здания':
return 'Technical inspection of buildings and structures. Assessment of load-bearing structures, detection of defects and development of strengthening recommendations.';
case 'Землестроительный и Кадастровые работы':
return 'Land surveying, preparation of technical plans and inspection reports, registration of real estate in the state cadastre.';
default:
return service.description;
}
})()
: service.description;
return (
<div
key={idx}
className="bg-white rounded-2xl overflow-hidden shadow-lg hover:shadow-xl transition-shadow duration-300 flex flex-col h-full"
@@ -63,75 +116,67 @@ const ServicesPage: React.FC = () => {
<div className="h-64 overflow-hidden">
<img
src={service.image}
alt={service.title}
alt={title}
className="w-full h-full object-cover transition-transform duration-700 hover:scale-110"
/>
</div>
<div className="p-8 flex-grow flex flex-col">
<h3 className="text-2xl font-bold text-gray-900 mb-4">{service.title}</h3>
<h3 className="text-2xl font-bold text-gray-900 mb-4">{title}</h3>
<p className="text-gray-600 text-sm mb-6 flex-grow leading-relaxed">
{service.description}
{description}
</p>
<div className="flex flex-col gap-3 mt-auto">
{service.title === 'Инженерные изыскания' && (
<Link
to="/services/surveying"
to={`${prefix}/services/surveying`}
className="flex items-center justify-center gap-2 bg-brand-orange text-white font-bold px-6 py-3 rounded-lg hover:bg-orange-600 transition-colors"
>
Подробнее <ArrowRight size={18} />
{isEnglish ? 'Learn more' : 'Подробнее'} <ArrowRight size={18} />
</Link>
)}
{service.title === 'Проектирование' && (
<Link
to="/services/design"
to={`${prefix}/services/design`}
className="flex items-center justify-center gap-2 bg-brand-orange text-white font-bold px-6 py-3 rounded-lg hover:bg-orange-600 transition-colors"
>
Подробнее <ArrowRight size={18} />
</Link>
)}
{service.title === 'Строительство' && (
<Link
to="/services/construction"
className="flex items-center justify-center gap-2 bg-brand-orange text-white font-bold px-6 py-3 rounded-lg hover:bg-orange-600 transition-colors"
>
Подробнее <ArrowRight size={18} />
{isEnglish ? 'Learn more' : 'Подробнее'} <ArrowRight size={18} />
</Link>
)}
{service.title === 'Обследование грунтов' && (
<Link
to="/services/soil-survey"
to={`${prefix}/services/soil-survey`}
className="flex items-center justify-center gap-2 bg-brand-orange text-white font-bold px-6 py-3 rounded-lg hover:bg-orange-600 transition-colors"
>
Подробнее <ArrowRight size={18} />
{isEnglish ? 'Learn more' : 'Подробнее'} <ArrowRight size={18} />
</Link>
)}
{service.title === 'Обследование здания' && (
<Link
to="/services/building-survey"
to={`${prefix}/services/building-survey`}
className="flex items-center justify-center gap-2 bg-brand-orange text-white font-bold px-6 py-3 rounded-lg hover:bg-orange-600 transition-colors"
>
Подробнее <ArrowRight size={18} />
{isEnglish ? 'Learn more' : 'Подробнее'} <ArrowRight size={18} />
</Link>
)}
{service.title === 'Землестроительный и Кадастровые работы' && (
<Link
to="/services/land-survey"
to={`${prefix}/services/land-survey`}
className="flex items-center justify-center gap-2 bg-brand-orange text-white font-bold px-6 py-3 rounded-lg hover:bg-orange-600 transition-colors"
>
Подробнее <ArrowRight size={18} />
{isEnglish ? 'Learn more' : 'Подробнее'} <ArrowRight size={18} />
</Link>
)}
<Link
to="/contacts"
className={`flex items-center ${(service.title === 'Инженерные изыскания' || service.title === 'Проектирование' || service.title === 'Строительство' || service.title === 'Обследование грунтов' || service.title === 'Обследование здания' || service.title === 'Землестроительный и Кадастровые работы') ? 'justify-center' : 'gap-2'} text-brand-orange font-medium hover:gap-4 transition-all`}
to={`${prefix}/contacts`}
className={`flex items-center ${(service.title === 'Инженерные изыскания' || service.title === 'Проектирование' || service.title === 'Обследование грунтов' || service.title === 'Обследование здания' || service.title === 'Землестроительный и Кадастровые работы') ? 'justify-center' : 'gap-2'} text-brand-orange font-medium hover:gap-4 transition-all`}
>
Заказать услугу <ArrowRight size={16} />
{isEnglish ? 'Request this service' : 'Заказать услугу'} <ArrowRight size={16} />
</Link>
</div>
</div>
</div>
))}
)})}
</div>
</div>
</div>

View File

@@ -1,44 +1,71 @@
import React from 'react';
import PageHeader from '../components/PageHeader';
import { CheckCircle2, AlertTriangle, Target, ClipboardCheck, Search, TrendingDown, Activity } from 'lucide-react';
import { useLocation } from 'react-router-dom';
const SoilSurveyPage: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
const workTypes = [
{
icon: AlertTriangle,
title: 'Проходка котлованов и траншей',
description: 'Прокладка подземных коммуникаций, пешеходных и транспортных тоннелей ниже глубины заложения существующих фундаментов, особенно с применением водопонижения'
title: isEnglish ? 'Excavation of pits and trenches' : 'Проходка котлованов и траншей',
description: isEnglish
? 'Laying underground utilities, pedestrian and traffic tunnels below the depth of existing foundations, especially with groundwater lowering.'
: 'Прокладка подземных коммуникаций, пешеходных и транспортных тоннелей ниже глубины заложения существующих фундаментов, особенно с применением водопонижения',
},
{
icon: TrendingDown,
title: 'Строительство новых зданий',
description: 'Возведение объектов, вызывающих дополнительные напряжения, перераспределение напряжений и перемещения грунта в активной зоне фундаментов существующих зданий'
title: isEnglish ? 'Construction of new buildings' : 'Строительство новых зданий',
description: isEnglish
? 'Erection of facilities that cause additional stresses, stress redistribution and soil movements within the active zone of existing foundations.'
: 'Возведение объектов, вызывающих дополнительные напряжения, перераспределение напряжений и перемещения грунта в активной зоне фундаментов существующих зданий',
},
{
icon: Activity,
title: 'Устройство стен в грунте',
description: 'Забивка шпунта или свай вблизи существующих зданий с передачей на их основание динамических нагрузок'
title: isEnglish ? 'Installation of retaining walls and barriers' : 'Устройство стен в грунте',
description: isEnglish
? 'Driving sheet piles or piles near existing buildings, transferring dynamic loads to their foundations.'
: 'Забивка шпунта или свай вблизи существующих зданий с передачей на их основание динамических нагрузок',
},
{
icon: Target,
title: 'Динамические воздействия',
description: 'От авто- и железнодорожного транспорта, линий метрополитена, оборудования, устанавливаемого в сооружениях и промышленных установках'
}
title: isEnglish ? 'Dynamic impacts' : 'Динамические воздействия',
description: isEnglish
? 'From road and rail transport, metro lines, equipment installed in buildings and industrial facilities.'
: 'От авто- и железнодорожного транспорта, линий метрополитена, оборудования, устанавливаемого в сооружениях и промышленных установках',
},
];
const mainTasks = [
'Возможности надстройки, реконструкции зданий и сооружений с увеличением временных и постоянных нагрузок на фундаменты',
'Установления причин деформаций и разработки мер для предотвращения их дальнейшего развития, а также восстановления условий нормальной эксплуатации зданий и сооружений',
'Определения состояния грунтов основания, возможности и условий достройки зданий и сооружений после длительной консервации их строительства',
'Определения состояния мест примыкания зданий-пристроек к существующим и разработки мер по обеспечению их устойчивости',
'Выяснения причин затапливания и подтапливания подвалов и других подземных сооружений'
isEnglish
? 'Assessing the possibility of adding storeys or reconstructing buildings and structures with increased permanent and variable loads on foundations.'
: 'Возможности надстройки, реконструкции зданий и сооружений с увеличением временных и постоянных нагрузок на фундаменты',
isEnglish
? 'Identifying causes of deformations and developing measures to prevent their further development and restore normal operating conditions.'
: 'Установления причин деформаций и разработки мер для предотвращения их дальнейшего развития, а также восстановления условий нормальной эксплуатации зданий и сооружений',
isEnglish
? 'Determining the state of foundation soils and conditions for completing construction after long-term conservation.'
: 'Определения состояния грунтов основания, возможности и условий достройки зданий и сооружений после длительной консервации их строительства',
isEnglish
? 'Assessing the condition of junctions between extensions and existing buildings and developing measures to ensure their stability.'
: 'Определения состояния мест примыкания зданий-пристроек к существующим и разработки мер по обеспечению их устойчивости',
isEnglish
? 'Identifying causes of flooding and water ingress into basements and other underground structures.'
: 'Выяснения причин затапливания и подтапливания подвалов и других подземных сооружений',
];
return (
<div className="bg-white pb-20">
<PageHeader
title="Обследование грунтов"
description="Оценка состояния грунтов оснований зданий и сооружений"
title={isEnglish ? 'Soil investigation' : 'Обследование грунтов'}
description={
isEnglish
? 'Assessment of the condition of foundation soils beneath buildings and structures.'
: 'Оценка состояния грунтов оснований зданий и сооружений'
}
image="/media/images/headers/header-obsledovanie-gruntov.png"
/>
@@ -47,13 +74,14 @@ const SoilSurveyPage: React.FC = () => {
<div className="max-w-5xl mx-auto mb-20">
<div className="bg-gradient-to-br from-brand-orange to-orange-600 text-white rounded-2xl p-8 md:p-12 mb-12">
<h2 className="text-3xl font-bold mb-6">
Обследование состояния грунтов основания зданий и сооружений
{isEnglish
? 'Investigation of foundation soils beneath buildings and structures'
: 'Обследование состояния грунтов основания зданий и сооружений'}
</h2>
<p className="text-lg leading-relaxed">
Обследование состояния грунтов оснований зданий и сооружений выполняются для оценки возможных
негативных последствий от реконструкции существующего здания, а также от воздействия на него нового
строительства (реконструкции, расширения) на прилегающих территориях и, при необходимости, получение
данных для разработки мероприятий по обеспечению эксплуатационной надежности существующей застройки.
{isEnglish
? 'Soil investigations of existing foundations are carried out to assess possible adverse effects of reconstruction, as well as the impact of new construction (reconstruction or expansion) on adjacent plots and, where required, to obtain data for measures that ensure long-term operational reliability of existing developments.'
: 'Обследование состояния грунтов оснований зданий и сооружений выполняются для оценки возможных негативных последствий от реконструкции существующего здания, а также от воздействия на него нового строительства (реконструкции, расширения) на прилегающих территориях и, при необходимости, получение данных для разработки мероприятий по обеспечению эксплуатационной надежности существующей застройки.'}
</p>
</div>
@@ -61,12 +89,13 @@ const SoilSurveyPage: React.FC = () => {
<div className="flex items-start gap-4 mb-6">
<Search className="text-brand-orange flex-shrink-0" size={32} />
<h3 className="text-2xl font-bold text-gray-900">
Когда необходимо обследование
{isEnglish ? 'When soil investigation is required' : 'Когда необходимо обследование'}
</h3>
</div>
<p className="text-gray-700 leading-relaxed mb-6">
Обследование грунтов оснований существующих зданий необходимо производить, когда в прилегающей
к ним зоне производятся следующие виды работ (или возможны какие-то негативные воздействия):
{isEnglish
? 'Soil investigations of existing foundations are required when the following types of work are carried out in the surrounding area (or when similar adverse impacts are expected):'
: 'Обследование грунтов оснований существующих зданий необходимо производить, когда в прилегающей к ним зоне производятся следующие виды работ (или возможны какие-то негативные воздействия):'}
</p>
</div>
</div>
@@ -74,7 +103,9 @@ const SoilSurveyPage: React.FC = () => {
{/* Виды работ */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-12 text-center">
Виды работ, требующие обследования грунтов
{isEnglish
? 'Types of work that require soil investigation'
: 'Виды работ, требующие обследования грунтов'}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
@@ -104,16 +135,17 @@ const SoilSurveyPage: React.FC = () => {
<div className="text-center mb-12">
<div className="inline-flex items-center gap-3 bg-brand-orange text-white px-8 py-4 rounded-full mb-6">
<ClipboardCheck size={32} />
<span className="text-xl font-bold">ОСНОВНЫЕ ЗАДАЧИ ОБСЛЕДОВАНИЯ</span>
<span className="text-xl font-bold">
{isEnglish ? 'KEY TASKS OF SOIL INVESTIGATION' : 'ОСНОВНЫЕ ЗАДАЧИ ОБСЛЕДОВАНИЯ'}
</span>
</div>
</div>
<div className="bg-gray-50 rounded-2xl p-8 md:p-10 mb-8">
<p className="text-gray-700 leading-relaxed mb-8">
При обследовании необходимо определять изменения инженерно-геологических условий за период
строительства и эксплуатации предприятий, зданий и сооружений, включая изменения рельефа,
геологического строения, гидрогеологических условий, состава, состояния и свойств грунтов,
активности инженерно-геологических процессов, с целью получения данных для решения основных задач:
{isEnglish
? 'During investigations it is necessary to determine how engineeringgeological conditions have changed over the period of construction and operation, including relief, geological structure, hydrogeology, soil composition and properties, and the activity of engineeringgeological processes, in order to obtain data for solving the key tasks listed below:'
: 'При обследовании необходимо определять изменения инженерно-геологических условий за период строительства и эксплуатации предприятий, зданий и сооружений, включая изменения рельефа, геологического строения, гидрогеологических условий, состава, состояния и свойств грунтов, активности инженерно-геологических процессов, с целью получения данных для решения основных задач:'}
</p>
<div className="space-y-4">
@@ -135,7 +167,7 @@ const SoilSurveyPage: React.FC = () => {
{/* Методология */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Методология обследования
{isEnglish ? 'Methodology of soil investigation' : 'Методология обследования'}
</h2>
<div className="grid md:grid-cols-2 gap-8">
@@ -145,14 +177,13 @@ const SoilSurveyPage: React.FC = () => {
<Search size={24} />
</div>
<h3 className="text-xl font-bold text-gray-900">
Использование материалов изысканий
{isEnglish ? 'Use of previous survey data' : 'Использование материалов изысканий'}
</h3>
</div>
<p className="text-gray-600 leading-relaxed text-sm">
При наличии материалов изысканий прошлых лет их использование возможно с учетом п. 5.2 СП 11-105-97,
часть I. В случаях, когда материалы отсутствуют или при анализе оцениваются как недостаточные,
сомнительные, устаревшие, обследование следует выполнять в составе и в объемах, обоснованных
в программе изысканий.
{isEnglish
? 'Where survey data from previous years are available, they may be used in accordance with SP 1110597, Part I. If such materials are missing or are considered insufficient, unreliable or outdated, investigations must be carried out in scope justified by the survey programme.'
: 'При наличии материалов изысканий прошлых лет их использование возможно с учетом п. 5.2 СП 11-105-97, часть I. В случаях, когда материалы отсутствуют или при анализе оцениваются как недостаточные, сомнительные, устаревшие, обследование следует выполнять в составе и в объемах, обоснованных в программе изысканий.'}
</p>
</div>
@@ -162,14 +193,13 @@ const SoilSurveyPage: React.FC = () => {
<Activity size={24} />
</div>
<h3 className="text-xl font-bold text-gray-900">
Стационарные наблюдения
{isEnglish ? 'Stationary monitoring' : 'Стационарные наблюдения'}
</h3>
</div>
<p className="text-gray-600 leading-relaxed text-sm">
Обследование осуществляется на основе стационарных наблюдений за геофизическими параметрами среды
(скоростью упругих волн, электрическим сопротивлением, температурой и др.), изменение которых позволяет
судить об осадке оснований, фильтрации и других процессах. Выполняются повторные систематические
наблюдения на одной и той же базе.
{isEnglish
? 'Investigations are based on stationary monitoring of geophysical parameters (velocity of elastic waves, electrical resistivity, temperature, etc.), the changes of which indicate settlement of foundations, filtration and other processes. Repeated systematic measurements are carried out on the same base points.'
: 'Обследование осуществляется на основе стационарных наблюдений за геофизическими параметрами среды (скоростью упругих волн, электрическим сопротивлением, температурой и др.), изменение которых позволяет судить об осадке оснований, фильтрации и других процессах. Выполняются повторные систематические наблюдения на одной и той же базе.'}
</p>
</div>
</div>
@@ -183,11 +213,13 @@ const SoilSurveyPage: React.FC = () => {
<CheckCircle2 size={48} className="text-brand-orange" />
</div>
<div>
<h3 className="text-2xl font-bold mb-4">Дополнительные исследования</h3>
<h3 className="text-2xl font-bold mb-4">
{isEnglish ? 'Additional investigations' : 'Дополнительные исследования'}
</h3>
<p className="text-gray-300 leading-relaxed">
При обследовании оснований зданий и сооружений может выполняться определение глубины заложения
фундаментов и оценки их состояния. Это позволяет получить полную картину состояния объекта
и разработать комплекс мероприятий по обеспечению его безопасной эксплуатации.
{isEnglish
? 'During investigations we can determine foundation depth and assess foundation condition. This gives a complete picture of the asset and enables us to develop a set of measures to ensure its safe operation.'
: 'При обследовании оснований зданий и сооружений может выполняться определение глубины заложения фундаментов и оценки их состояния. Это позволяет получить полную картину состояния объекта и разработать комплекс мероприятий по обеспечению его безопасной эксплуатации.'}
</p>
</div>
</div>
@@ -198,7 +230,7 @@ const SoilSurveyPage: React.FC = () => {
<div className="max-w-6xl mx-auto">
<div className="bg-gray-50 rounded-2xl p-8 md:p-12">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Почему важно обследование грунтов
{isEnglish ? 'Why soil investigation is important' : 'Почему важно обследование грунтов'}
</h2>
<div className="grid md:grid-cols-3 gap-6">
@@ -206,9 +238,13 @@ const SoilSurveyPage: React.FC = () => {
<div className="inline-flex w-16 h-16 bg-brand-orange/10 text-brand-orange rounded-full items-center justify-center mb-4">
<AlertTriangle size={32} />
</div>
<h3 className="font-bold text-lg mb-3 text-gray-900">Предотвращение аварий</h3>
<h3 className="font-bold text-lg mb-3 text-gray-900">
{isEnglish ? 'Accident prevention' : 'Предотвращение аварий'}
</h3>
<p className="text-gray-600 text-sm leading-relaxed">
Своевременное выявление проблем позволяет избежать деформаций и разрушений зданий
{isEnglish
? 'Timely identification of problems helps prevent deformations and structural failures.'
: 'Своевременное выявление проблем позволяет избежать деформаций и разрушений зданий'}
</p>
</div>
@@ -216,9 +252,13 @@ const SoilSurveyPage: React.FC = () => {
<div className="inline-flex w-16 h-16 bg-brand-orange/10 text-brand-orange rounded-full items-center justify-center mb-4">
<Target size={32} />
</div>
<h3 className="font-bold text-lg mb-3 text-gray-900">Точная диагностика</h3>
<h3 className="font-bold text-lg mb-3 text-gray-900">
{isEnglish ? 'Accurate diagnostics' : 'Точная диагностика'}
</h3>
<p className="text-gray-600 text-sm leading-relaxed">
Определение причин деформаций и разработка эффективных мер по их устранению
{isEnglish
? 'Determining the causes of deformations and developing effective mitigation measures.'
: 'Определение причин деформаций и разработка эффективных мер по их устранению'}
</p>
</div>
@@ -226,9 +266,13 @@ const SoilSurveyPage: React.FC = () => {
<div className="inline-flex w-16 h-16 bg-brand-orange/10 text-brand-orange rounded-full items-center justify-center mb-4">
<CheckCircle2 size={32} />
</div>
<h3 className="font-bold text-lg mb-3 text-gray-900">Безопасность</h3>
<h3 className="font-bold text-lg mb-3 text-gray-900">
{isEnglish ? 'Safety' : 'Безопасность'}
</h3>
<p className="text-gray-600 text-sm leading-relaxed">
Обеспечение эксплуатационной надежности существующей и новой застройки
{isEnglish
? 'Ensuring operational reliability of both existing and new developments.'
: 'Обеспечение эксплуатационной надежности существующей и новой застройки'}
</p>
</div>
</div>
@@ -238,13 +282,13 @@ const SoilSurveyPage: React.FC = () => {
<div className="mt-12 text-center">
<div className="bg-white rounded-2xl p-8 shadow-lg">
<p className="text-gray-700 mb-6 text-lg">
Нужна консультация по обследованию грунтов?
{isEnglish ? 'Need a consultation on soil investigation?' : 'Нужна консультация по обследованию грунтов?'}
</p>
<a
href="#/contacts"
href={`#${isEnglish ? '/en/contacts' : '/contacts'}`}
className="inline-block px-10 py-4 bg-brand-orange text-white font-bold rounded-lg hover:bg-orange-600 transition-colors text-lg shadow-lg hover:shadow-xl"
>
Связаться с нами
{isEnglish ? 'Contact us' : 'Связаться с нами'}
</a>
</div>
</div>

View File

@@ -1,8 +1,13 @@
import React from 'react';
import PageHeader from '../components/PageHeader';
import { CheckCircle2, MapPin, Mountain, Leaf, Cloud, Wrench, Award } from 'lucide-react';
import { useLocation } from 'react-router-dom';
const SurveyingPage: React.FC = () => {
const location = useLocation();
const isEnglish = location.pathname.startsWith('/en');
const prefix = isEnglish ? '/en' : '';
const surveyTypes = [
{
icon: MapPin,
@@ -70,25 +75,90 @@ const SurveyingPage: React.FC = () => {
];
const pricing = [
{ service: 'Инженерные изыскания под коттедж (геология+геодезия)', price: 'от 25000 ₽' },
{ service: 'Инженерно-геологические изыскания для строительства', price: 'от 500 ₽ / м.' },
{ service: 'Инженерно-геологические изыскания с экспертизой', price: 'от 500 ₽ / м.' },
{ service: 'Геологические изыскания (особо крупные объекты)', price: 'от 300 ₽ / м.' },
{ service: 'Геологические изыскания под коттедж', price: 'от 15000 ₽' },
{ service: 'Инженерно-геодезические изыскания для строительства', price: 'от 5000 ₽' },
{ service: 'Геоподоснова (топоплан с коммуникациями)', price: 'от 10000 ₽' },
{ service: 'Геодезические изыскания под коттедж (топосъемка)', price: 'от 5000 ₽' },
{ service: 'Определение границ, координатная привязка', price: 'от 2000 ₽' },
{ service: 'Разбивка осей, реперы, сопровождение строительства', price: 'от 2000 ₽' },
{ service: 'Инженерно-экологические изыскания', price: 'от 8000 ₽' },
{ service: 'Инженерные изыскания (комплекс работ)', price: '10% скидка', highlight: true }
{
service: isEnglish
? 'Engineering surveys for a cottage (geology + geodesy)'
: 'Инженерные изыскания под коттедж (геология+геодезия)',
price: isEnglish ? 'from 25 000 ₽' : 'от 25000 ₽',
},
{
service: isEnglish
? 'Engineering-geological surveys for construction'
: 'Инженерно-геологические изыскания для строительства',
price: isEnglish ? 'from 500 ₽ / m' : 'от 500 ₽ / м.',
},
{
service: isEnglish
? 'Engineering-geological surveys with expert review'
: 'Инженерно-геологические изыскания с экспертизой',
price: isEnglish ? 'from 500 ₽ / m' : 'от 500 ₽ / м.',
},
{
service: isEnglish
? 'Geological surveys (large-scale facilities)'
: 'Геологические изыскания (особо крупные объекты)',
price: isEnglish ? 'from 300 ₽ / m' : 'от 300 ₽ / м.',
},
{
service: isEnglish
? 'Geological surveys for a cottage'
: 'Геологические изыскания под коттедж',
price: isEnglish ? 'from 15 000 ₽' : 'от 15000 ₽',
},
{
service: isEnglish
? 'Engineering-geodetic surveys for construction'
: 'Инженерно-геодезические изыскания для строительства',
price: isEnglish ? 'from 5 000 ₽' : 'от 5000 ₽',
},
{
service: isEnglish
? 'Geodetic base plan (topographic plan with utilities)'
: 'Геоподоснова (топоплан с коммуникациями)',
price: isEnglish ? 'from 10 000 ₽' : 'от 10000 ₽',
},
{
service: isEnglish
? 'Geodetic surveys for a cottage (topographic survey)'
: 'Геодезические изыскания под коттедж (топосъемка)',
price: isEnglish ? 'from 5 000 ₽' : 'от 5000 ₽',
},
{
service: isEnglish
? 'Boundary identification and coordinate referencing'
: 'Определение границ, координатная привязка',
price: isEnglish ? 'from 2 000 ₽' : 'от 2000 ₽',
},
{
service: isEnglish
? 'Axis layout, benchmarks, construction support'
: 'Разбивка осей, реперы, сопровождение строительства',
price: isEnglish ? 'from 2 000 ₽' : 'от 2000 ₽',
},
{
service: isEnglish
? 'Engineering-environmental surveys'
: 'Инженерно-экологические изыскания',
price: isEnglish ? 'from 8 000 ₽' : 'от 8000 ₽',
},
{
service: isEnglish
? 'Engineering surveys (full scope)'
: 'Инженерные изыскания (комплекс работ)',
price: isEnglish ? '10% discount' : '10% скидка',
highlight: true,
},
];
return (
<div className="bg-white pb-20">
<PageHeader
title="Инженерные изыскания"
description="Комплекс мероприятий для сбора и анализа детальной информации о территории строительства"
title={isEnglish ? 'Engineering surveys' : 'Инженерные изыскания'}
description={
isEnglish
? 'A full set of activities for collecting and analysing detailed information about the construction site.'
: 'Комплекс мероприятий для сбора и анализа детальной информации о территории строительства'
}
image="/media/images/headers/header-injinernie-iziskania.png"
/>
@@ -97,24 +167,33 @@ const SurveyingPage: React.FC = () => {
<div className="max-w-5xl mx-auto mb-20">
<div className="bg-gray-50 rounded-2xl p-8 md:p-12 mb-12">
<p className="text-lg text-gray-700 leading-relaxed mb-6">
{isEnglish ? (
<>
<strong>Engineering surveys for construction</strong> are a set of activities aimed at collecting
and analysing detailed information about the natural and man-made conditions of the site.
The resulting data form part of the mandatory documentation for design and construction.
</>
) : (
<>
<strong>Инженерные изыскания для выполнения строительства</strong> комплекс мероприятий,
направленных на сбор и анализ детальной информации о техногенных и природных условиях территории
для будущего строительства. Полученные данные входят в состав обязательной документации для
разработки проекта и проведения строительных работ.
</>
)}
</p>
<p className="text-gray-600 leading-relaxed">
Необходимость проведения определенных видов изысканий, их объем и состав определяются после анализа
технического задания и зависят от вида, уровня ответственности, назначения сооружений, стадии
проектирования, условий участка и степени их изученности.
{isEnglish
? 'The required types and scope of surveys are determined after analysing the technical assignment and depend on the type and responsibility level of the structures, design stage and the conditions and degree of study of the site.'
: 'Необходимость проведения определенных видов изысканий, их объем и состав определяются после анализа технического задания и зависят от вида, уровня ответственности, назначения сооружений, стадии проектирования, условий участка и степени их изученности.'}
</p>
</div>
<div className="bg-brand-orange text-white rounded-2xl p-8 md:p-12">
<p className="text-lg leading-relaxed">
Проведение инженерных изысканий подразумевает анализ данных, оценку возможных последствий реализации
проекта, получение санитарно-эпидемиологических заключений и составление технических отчетов, состав
которых определяется действующими нормативными документами. Наши специалисты профессионально выполняют
изыскания для строящихся или реконструируемых объектов любых типов.
{isEnglish
? 'Engineering surveys include data analysis, assessment of possible consequences of the project implementation, obtaining sanitary and epidemiological conclusions and preparing technical reports in accordance with current regulations. Our specialists carry out surveys for new construction and reconstruction projects of any type.'
: 'Проведение инженерных изысканий подразумевает анализ данных, оценку возможных последствий реализации проекта, получение санитарно-эпидемиологических заключений и составление технических отчетов, состав которых определяется действующими нормативными документами. Наши специалисты профессионально выполняют изыскания для строящихся или реконструируемых объектов любых типов.'}
</p>
</div>
</div>
@@ -122,7 +201,9 @@ const SurveyingPage: React.FC = () => {
{/* Типы изысканий */}
<div className="max-w-6xl mx-auto mb-20">
<h2 className="text-3xl font-bold text-gray-900 mb-12 text-center">
Мы выполняем следующие типы инженерных изысканий
{isEnglish
? 'We carry out the following types of engineering surveys'
: 'Мы выполняем следующие типы инженерных изысканий'}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
@@ -152,7 +233,11 @@ const SurveyingPage: React.FC = () => {
<div className="text-center mb-12">
<div className="inline-flex items-center gap-3 bg-brand-orange text-white px-8 py-4 rounded-full mb-6">
<Award size={32} />
<span className="text-xl font-bold">ООО ГЕОВЕКТОР ИМЕЕТ ДОПУСК К СЛЕДУЮЩИМ ВИДАМ РАБОТ</span>
<span className="text-xl font-bold">
{isEnglish
? 'GeoVector LLC is certified to perform the following types of work'
: 'ООО ГЕОВЕКТОР ИМЕЕТ ДОПУСК К СЛЕДУЮЩИМ ВИДАМ РАБОТ'}
</span>
</div>
</div>
@@ -161,15 +246,27 @@ const SurveyingPage: React.FC = () => {
<div className="bg-gray-50 rounded-2xl p-8">
<h3 className="text-2xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-2 h-8 bg-brand-orange rounded"></div>
Инженерно-геодезические изыскания
{isEnglish ? 'Engineering and geodetic surveys' : 'Инженерно-геодезические изыскания'}
</h3>
<ul className="space-y-3">
{geodesicWorks.map((work, index) => (
{geodesicWorks.map((work, index) => {
const text = isEnglish
? [
'Establishment of control geodetic networks.',
'Geodetic monitoring of deformations and settlements of buildings and structures, ground surface displacements and hazardous natural processes.',
'Creation and updating of engineering topographic plans at scales 1:2001:5000, including digital form, surveying of underground utilities and structures.',
'Route setting-out for linear facilities.',
'Engineering hydrographic works.',
'Special geodetic and topographic works during construction and reconstruction of buildings and structures.',
][index] || work
: work;
return (
<li key={index} className="flex items-start gap-3">
<CheckCircle2 className="flex-shrink-0 text-brand-orange mt-1" size={20} />
<span className="text-gray-700">{work}</span>
<span className="text-gray-700">{text}</span>
</li>
))}
);
})}
</ul>
</div>
@@ -177,15 +274,28 @@ const SurveyingPage: React.FC = () => {
<div className="bg-gray-50 rounded-2xl p-8">
<h3 className="text-2xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-2 h-8 bg-brand-orange rounded"></div>
Инженерно-геологические изыскания
{isEnglish ? 'Engineering and geological surveys' : 'Инженерно-геологические изыскания'}
</h3>
<ul className="space-y-3">
{geologicalWorks.map((work, index) => (
{geologicalWorks.map((work, index) => {
const text = isEnglish
? [
'Engineeringgeological mapping at scales 1:5001:25 000.',
'Drilling of exploratory boreholes with sampling and laboratory testing of soil physicalmechanical properties and groundwater chemistry.',
'Study of hazardous geological and engineeringgeological processes with recommendations for engineering protection of the territory.',
'Hydrogeological investigations.',
'Engineeringgeophysical investigations.',
'Engineeringgeocryological investigations.',
'Seismological and seismotectonic investigations, seismic microzoning of the territory.',
][index] || work
: work;
return (
<li key={index} className="flex items-start gap-3">
<CheckCircle2 className="flex-shrink-0 text-brand-orange mt-1" size={20} />
<span className="text-gray-700">{work}</span>
<span className="text-gray-700">{text}</span>
</li>
))}
);
})}
</ul>
</div>
@@ -193,15 +303,26 @@ const SurveyingPage: React.FC = () => {
<div className="bg-gray-50 rounded-2xl p-8">
<h3 className="text-2xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-2 h-8 bg-brand-orange rounded"></div>
Инженерно-экологические изыскания
{isEnglish ? 'Engineering and environmental surveys' : 'Инженерно-экологические изыскания'}
</h3>
<ul className="space-y-3">
{ecologicalWorks.map((work, index) => (
{ecologicalWorks.map((work, index) => {
const text = isEnglish
? [
'Engineeringenvironmental mapping of the territory.',
'Investigation of chemical contamination of soils, surface and groundwater, air and pollution sources.',
'Laboratory chemicalanalytical and gaschemical testing of soil and water samples.',
'Study and assessment of physical impacts and radiation conditions across the territory.',
'Study of vegetation and wildlife, sanitaryepidemiological and biomedical investigations of the area.',
][index] || work
: work;
return (
<li key={index} className="flex items-start gap-3">
<CheckCircle2 className="flex-shrink-0 text-brand-orange mt-1" size={20} />
<span className="text-gray-700">{work}</span>
<span className="text-gray-700">{text}</span>
</li>
))}
);
})}
</ul>
</div>
@@ -209,15 +330,25 @@ const SurveyingPage: React.FC = () => {
<div className="bg-gray-50 rounded-2xl p-8">
<h3 className="text-2xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-2 h-8 bg-brand-orange rounded"></div>
Инженерно-гидрометеорологические изыскания
{isEnglish ? 'Engineering and hydrometeorological surveys' : 'Инженерно-гидрометеорологические изыскания'}
</h3>
<ul className="space-y-3">
{hydrometeorologicalWorks.map((work, index) => (
{hydrometeorologicalWorks.map((work, index) => {
const text = isEnglish
? [
'Meteorological observations and study of the hydrological regime of water bodies.',
'Analysis of hazardous hydrometeorological processes and phenomena with calculation of their characteristics.',
'Study of channel processes in watercourses, bank deformation and erosion.',
'Investigation of ice regime in water bodies.',
][index] || work
: work;
return (
<li key={index} className="flex items-start gap-3">
<CheckCircle2 className="flex-shrink-0 text-brand-orange mt-1" size={20} />
<span className="text-gray-700">{work}</span>
<span className="text-gray-700">{text}</span>
</li>
))}
);
})}
</ul>
</div>
@@ -225,19 +356,32 @@ const SurveyingPage: React.FC = () => {
<div className="bg-gray-50 rounded-2xl p-8">
<h3 className="text-2xl font-bold text-gray-900 mb-6 flex items-center gap-3">
<div className="w-2 h-8 bg-brand-orange rounded"></div>
Инженерно-геотехнические изыскания
{isEnglish ? 'Engineering and geotechnical surveys' : 'Инженерно-геотехнические изыскания'}
</h3>
<p className="text-sm text-gray-600 italic mb-6">
(Выполняются в составе инженерно-геологических изысканий или отдельно на изученной
в инженерно-геологическом отношении территории под отдельные здания и сооружения).
{isEnglish
? 'Performed as part of engineering and geological surveys or separately on sites that have already been investigated from an engineering-geological perspective.'
: '(Выполняются в составе инженерно-геологических изысканий или отдельно на изученной в инженерно-геологическом отношении территории под отдельные здания и сооружения).'}
</p>
<ul className="space-y-3">
{geotechnicalWorks.map((work, index) => (
{geotechnicalWorks.map((work, index) => {
const text = isEnglish
? [
'Excavation of test pits and boreholes with sampling and laboratory tests to obtain parameters for specific foundation design schemes.',
'Field testing of soils to determine standard strength and deformation characteristics (plate load, shear, pressuremeter, vane shear tests) and testing of reference and fullscale piles.',
'Determination of standard mechanical characteristics of soils using static, dynamic and drilling penetration tests.',
'Physical and numerical modelling of interaction between buildings/structures and the geological environment.',
'Special soil studies under bespoke programmes for nonstandard, including nonlinear, foundation and structure design methods.',
'Geotechnical monitoring of construction of buildings, structures and adjacent areas.',
][index] || work
: work;
return (
<li key={index} className="flex items-start gap-3">
<CheckCircle2 className="flex-shrink-0 text-brand-orange mt-1" size={20} />
<span className="text-gray-700">{work}</span>
<span className="text-gray-700">{text}</span>
</li>
))}
);
})}
</ul>
</div>
</div>
@@ -246,15 +390,21 @@ const SurveyingPage: React.FC = () => {
{/* Прайс-лист */}
<div className="max-w-5xl mx-auto">
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
Цены на основные виды работ по инженерным изысканиям
{isEnglish
? 'Prices for main types of engineering surveys'
: 'Цены на основные виды работ по инженерным изысканиям'}
</h2>
<div className="overflow-x-auto shadow-lg rounded-2xl">
<table className="w-full bg-white">
<thead>
<tr className="bg-brand-orange text-white">
<th className="px-6 py-4 text-left font-bold">Наименование услуги</th>
<th className="px-6 py-4 text-right font-bold w-48">Стоимость</th>
<th className="px-6 py-4 text-left font-bold">
{isEnglish ? 'Service name' : 'Наименование услуги'}
</th>
<th className="px-6 py-4 text-right font-bold w-48">
{isEnglish ? 'Price' : 'Стоимость'}
</th>
</tr>
</thead>
<tbody>
@@ -285,13 +435,15 @@ const SurveyingPage: React.FC = () => {
<div className="mt-12 text-center">
<div className="bg-gray-50 rounded-2xl p-8">
<p className="text-gray-700 mb-6">
Точную стоимость работ можно узнать после анализа технического задания и условий объекта
{isEnglish
? 'The exact cost can be calculated after analysing the technical assignment and site conditions.'
: 'Точную стоимость работ можно узнать после анализа технического задания и условий объекта'}
</p>
<a
href="#/contacts"
href={`#${prefix}/contacts`}
className="inline-block px-8 py-4 bg-brand-orange text-white font-bold rounded-lg hover:bg-orange-600 transition-colors text-lg"
>
Получить консультацию
{isEnglish ? 'Request a consultation' : 'Получить консультацию'}
</a>
</div>
</div>

48
src/api/strapiClient.ts Normal file
View File

@@ -0,0 +1,48 @@
const STRAPI_URL = import.meta.env.VITE_STRAPI_URL || 'http://localhost:1337';
type Locale = 'ru' | 'en';
async function fetchJson(path: string, init?: RequestInit) {
const res = await fetch(`${STRAPI_URL}${path}`, {
headers: {
'Content-Type': 'application/json',
},
...init,
});
if (!res.ok) {
throw new Error(`Strapi request failed: ${res.status} ${res.statusText}`);
}
return res.json();
}
export interface HomepageData {
heroTitle?: string;
heroSubtitle?: string;
seoTitle?: string;
seoDescription?: string;
}
export async function getHomepage(locale: Locale): Promise<HomepageData | null> {
const query = new URLSearchParams({
locale,
populate: '*',
}).toString();
const json = await fetchJson(`/api/homepage?${query}`);
if (!json || !json.data || !json.data.attributes) {
return null;
}
const attrs = json.data.attributes;
return {
heroTitle: attrs.heroTitle,
heroSubtitle: attrs.heroSubtitle,
seoTitle: attrs.seoTitle,
seoDescription: attrs.seoDescription,
};
}