Compare commits
2 Commits
6597497f11
...
6ede021ba8
Author | SHA1 | Date | |
---|---|---|---|
6ede021ba8 | |||
b49f351d70 |
BIN
public/images/1q.jpeg
Normal file
After Width: | Height: | Size: 183 KiB |
BIN
public/images/2q.jpeg
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
public/images/3q.jpeg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
public/images/4q.jpeg
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
public/images/5q.jpeg
Normal file
After Width: | Height: | Size: 219 KiB |
BIN
public/images/6q.jpeg
Normal file
After Width: | Height: | Size: 220 KiB |
BIN
public/images/o_nac.jpg
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
public/images/stanica.png
Normal file
After Width: | Height: | Size: 2.5 MiB |
@ -4,7 +4,7 @@ import Header from '@/components/Header';
|
||||
import AboutSection from '@/components/AboutSection';
|
||||
import ProjectsSection from '@/components/ProjectsSection';
|
||||
import WhyChooseUsSection from '@/components/WhyChooseUsSection';
|
||||
import TeamSection from '@/components/TeamSection';
|
||||
import WorkExamplesSection from '@/components/TeamSection';
|
||||
import ReviewsSection from '@/components/ReviewsSection';
|
||||
import ContactSection from '@/components/ContactSection';
|
||||
import Footer from '@/components/Footer';
|
||||
@ -26,7 +26,6 @@ export default function Home() {
|
||||
const [phone, setPhone] = useState('');
|
||||
const [name, setName] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
@ -51,44 +50,19 @@ export default function Home() {
|
||||
const handleFormSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
setLoading(true);
|
||||
|
||||
if (!validateName(name)) {
|
||||
setError('Пожалуйста, укажите корректное имя (только буквы, не менее 2 символов)');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validatePhone(phone)) {
|
||||
setError('Пожалуйста, укажите корректный российский номер телефона');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Отправляем данные в Telegram
|
||||
try {
|
||||
const res = await fetch('/api/send-telegram', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name,
|
||||
phone,
|
||||
material: 'Расчет стоимости',
|
||||
message: 'Запрос расчета стоимости дома'
|
||||
}),
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
// Если отправка успешна, открываем калькулятор
|
||||
setIsCalculatorOpen(true);
|
||||
} else {
|
||||
setError('Ошибка отправки. Попробуйте позже.');
|
||||
}
|
||||
} catch {
|
||||
setError('Ошибка отправки. Попробуйте позже.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
// Просто открываем калькулятор без отправки
|
||||
setIsCalculatorOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -113,7 +87,7 @@ export default function Home() {
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Image
|
||||
src="/images/header.jpg"
|
||||
src="/images/stanica.png"
|
||||
alt="Строительство домов"
|
||||
fill
|
||||
className="object-cover"
|
||||
@ -135,7 +109,7 @@ export default function Home() {
|
||||
</h1>
|
||||
|
||||
<p className="text-xl lg:text-2xl text-white/90 mb-12">
|
||||
Построим технологичный дом от 6 млн. руб за 90 дней
|
||||
Построим технологичный дом за 90 дней
|
||||
</p>
|
||||
|
||||
{/* Stats Section */}
|
||||
@ -187,15 +161,12 @@ export default function Home() {
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full bg-gradient-to-r from-blue-500 to-purple-500 text-white py-4 rounded-xl hover:from-blue-600 hover:to-purple-600 transition-all duration-300 text-lg font-semibold flex items-center justify-center group disabled:opacity-60 disabled:cursor-not-allowed hover:scale-105 shadow-lg"
|
||||
className="w-full bg-gradient-to-r from-blue-500 to-purple-500 text-white py-4 rounded-xl hover:from-blue-600 hover:to-purple-600 transition-all duration-300 text-lg font-semibold flex items-center justify-center group hover:scale-105 shadow-lg"
|
||||
>
|
||||
{loading ? 'Отправка...' : 'Обсудить проект'}
|
||||
{!loading && (
|
||||
<svg className="w-5 h-5 ml-2 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
||||
</svg>
|
||||
)}
|
||||
Обсудить проект
|
||||
<svg className="w-5 h-5 ml-2 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{error && (
|
||||
@ -226,7 +197,7 @@ export default function Home() {
|
||||
<AboutSection />
|
||||
<ProjectsSection onCatalogClick={() => setIsCatalogModalOpen(true)} />
|
||||
<WhyChooseUsSection />
|
||||
<TeamSection />
|
||||
<WorkExamplesSection />
|
||||
<ReviewsSection />
|
||||
<ContactSection />
|
||||
<Footer />
|
||||
|
@ -58,7 +58,7 @@ const AboutSection = () => {
|
||||
<div className="relative h-[500px] w-full rounded-2xl overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-gray-900/80 via-transparent to-transparent z-10"></div>
|
||||
<Image
|
||||
src="/images/company2.jpg"
|
||||
src="/images/o_nac.jpg"
|
||||
alt="О нашей компании"
|
||||
fill
|
||||
className="object-cover transition-transform duration-700 group-hover:scale-110"
|
||||
|
@ -68,69 +68,125 @@ const CallbackModal = ({ isOpen, onClose }: CallbackModalProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-gray-900/50 z-50 flex items-center justify-center">
|
||||
<div className="bg-white rounded-lg p-8 max-w-md w-full mx-4 relative">
|
||||
<button
|
||||
onClick={closeModal}
|
||||
className="absolute top-4 right-4 text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
<X className="w-6 h-6" />
|
||||
</button>
|
||||
<div className="fixed inset-0 bg-gray-900/80 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className="relative max-w-md w-full">
|
||||
{/* Декоративные фоновые элементы */}
|
||||
<div className="absolute -top-8 -left-8 w-32 h-32 bg-gradient-to-br from-blue-500/30 to-purple-500/30 rounded-full blur-2xl opacity-70"></div>
|
||||
<div className="absolute -bottom-8 -right-8 w-24 h-24 bg-gradient-to-br from-purple-500/30 to-blue-500/30 rounded-full blur-2xl opacity-70"></div>
|
||||
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||
Заказать звонок
|
||||
</h2>
|
||||
{/* Основной контейнер модального окна */}
|
||||
<div className="relative bg-gradient-to-br from-gray-900/95 via-gray-800/95 to-gray-900/95 backdrop-blur-md border border-white/20 rounded-2xl p-8 shadow-2xl">
|
||||
{/* Кнопка закрытия */}
|
||||
<button
|
||||
onClick={closeModal}
|
||||
className="absolute top-4 right-4 text-gray-400 hover:text-white transition-colors duration-300 hover:bg-white/10 rounded-full p-2 group"
|
||||
>
|
||||
<X className="w-5 h-5 group-hover:scale-110 transition-transform duration-300" />
|
||||
</button>
|
||||
|
||||
{!success ? (
|
||||
<form className="space-y-4" onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Ваше имя"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
||||
/>
|
||||
<input
|
||||
type="tel"
|
||||
placeholder="Ваш телефон"
|
||||
value={phone}
|
||||
onChange={e => {
|
||||
const val = e.target.value.replace(/\D/g, '').slice(0, 11);
|
||||
setPhone(val);
|
||||
}}
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
||||
/>
|
||||
<textarea
|
||||
placeholder="Ваше сообщение (необязательно)"
|
||||
rows={3}
|
||||
value={message}
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
||||
/>
|
||||
{error && (
|
||||
<div className="bg-red-500 text-white text-center py-3 px-4 rounded-lg flex items-center justify-center min-h-[48px] md:min-h-[40px] md:text-base text-sm whitespace-pre-line">
|
||||
<AlertCircle className="w-5 h-5 mr-2 shrink-0" />
|
||||
<span className="block w-full break-words">{error}</span>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition-colors font-semibold disabled:opacity-60"
|
||||
>
|
||||
{loading ? 'Отправка...' : 'Отправить'}
|
||||
</button>
|
||||
</form>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center py-8 animate-fadeIn">
|
||||
<div className="bg-green-100 rounded-2xl p-4 mb-6 flex items-center shadow-lg w-full">
|
||||
<CheckCircle className="w-8 h-8 text-green-600 mr-2" />
|
||||
<span className="text-green-700 text-lg font-semibold leading-snug text-left">
|
||||
Спасибо! Данные успешно отправлены.
|
||||
</span>
|
||||
</div>
|
||||
{/* Заголовок */}
|
||||
<div className="text-center mb-8">
|
||||
<h2 className="text-3xl font-bold bg-gradient-to-r from-white via-blue-100 to-white bg-clip-text text-transparent mb-2">
|
||||
Заказать звонок
|
||||
</h2>
|
||||
<div className="w-16 h-1 bg-gradient-to-r from-blue-500 to-purple-500 mx-auto rounded-full"></div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!success ? (
|
||||
<form className="space-y-6" onSubmit={handleSubmit}>
|
||||
{/* Поле имени */}
|
||||
<div className="relative group">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Ваше имя"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
className="w-full px-4 py-3 bg-white/10 backdrop-blur-sm border border-white/20 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-blue-500/50 focus:bg-white/20 transition-all duration-300 group-hover:border-white/30"
|
||||
/>
|
||||
<div className="absolute inset-0 rounded-xl bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
|
||||
</div>
|
||||
|
||||
{/* Поле телефона */}
|
||||
<div className="relative group">
|
||||
<input
|
||||
type="tel"
|
||||
placeholder="Ваш телефон"
|
||||
value={phone}
|
||||
onChange={e => {
|
||||
const val = e.target.value.replace(/\D/g, '').slice(0, 11);
|
||||
setPhone(val);
|
||||
}}
|
||||
className="w-full px-4 py-3 bg-white/10 backdrop-blur-sm border border-white/20 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-blue-500/50 focus:bg-white/20 transition-all duration-300 group-hover:border-white/30"
|
||||
/>
|
||||
<div className="absolute inset-0 rounded-xl bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
|
||||
</div>
|
||||
|
||||
{/* Поле сообщения */}
|
||||
<div className="relative group">
|
||||
<textarea
|
||||
placeholder="Ваше сообщение (необязательно)"
|
||||
rows={3}
|
||||
value={message}
|
||||
onChange={e => setMessage(e.target.value)}
|
||||
className="w-full px-4 py-3 bg-white/10 backdrop-blur-sm border border-white/20 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-blue-500/50 focus:bg-white/20 transition-all duration-300 group-hover:border-white/30 resize-none"
|
||||
/>
|
||||
<div className="absolute inset-0 rounded-xl bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
|
||||
</div>
|
||||
|
||||
{/* Сообщение об ошибке */}
|
||||
{error && (
|
||||
<div className="relative p-4 rounded-xl bg-gradient-to-r from-red-500/20 to-red-600/20 border border-red-500/30 backdrop-blur-sm">
|
||||
<div className="flex items-center">
|
||||
<AlertCircle className="w-5 h-5 text-red-400 mr-3 shrink-0" />
|
||||
<span className="text-red-200 text-sm leading-relaxed">{error}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Кнопка отправки */}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="relative w-full group overflow-hidden rounded-xl bg-gradient-to-r from-blue-500/20 to-purple-500/20 border border-white/20 backdrop-blur-sm hover:scale-105 transition-all duration-300 disabled:opacity-60 disabled:hover:scale-100"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/30 to-purple-500/30 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
<span className="relative block py-3 px-6 text-white font-semibold">
|
||||
{loading ? 'Отправка...' : 'Отправить'}
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
) : (
|
||||
<div className="text-center py-8 space-y-6">
|
||||
{/* Иконка успеха */}
|
||||
<div className="relative mx-auto w-20 h-20 rounded-full bg-gradient-to-br from-green-500/20 to-emerald-500/20 border border-green-500/30 backdrop-blur-sm flex items-center justify-center group">
|
||||
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-green-500/30 to-emerald-500/30 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||
<CheckCircle className="w-10 h-10 text-green-400 relative z-10" />
|
||||
</div>
|
||||
|
||||
{/* Сообщение об успехе */}
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-xl font-bold text-white">
|
||||
Спасибо за заявку!
|
||||
</h3>
|
||||
<p className="text-gray-300 leading-relaxed">
|
||||
Ваши данные успешно отправлены. Мы свяжемся с вами в ближайшее время.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Кнопка закрытия */}
|
||||
<button
|
||||
onClick={closeModal}
|
||||
className="relative group overflow-hidden rounded-xl bg-gradient-to-r from-blue-500/20 to-purple-500/20 border border-white/20 backdrop-blur-sm hover:scale-105 transition-all duration-300 px-8 py-3"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/30 to-purple-500/30 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
<span className="relative text-white font-medium">Закрыть</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Декоративный элемент */}
|
||||
<div className="absolute top-4 left-4 w-2 h-2 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full opacity-50"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -127,8 +127,8 @@ const ContactSection = () => {
|
||||
Реквизиты
|
||||
</h3>
|
||||
<p className="text-gray-300 leading-relaxed">
|
||||
ИП Степанов Денис Сергеевич<br />
|
||||
ИНН 212306083987
|
||||
ИП Фомин Александр Вениаминович<br />
|
||||
ИНН 213012845835
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -71,7 +71,7 @@ const Footer = () => {
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-800 mt-8 pt-8 text-center text-gray-400">
|
||||
<p>© 2021 ВашДом. Все права защищены.</p>
|
||||
<p>© 2021-2025 ВашДом. Все права защищены.</p>
|
||||
<p className="mt-2 text-sm">
|
||||
Разработка сайта <a href="https://biveki.ru/" target="_blank" rel="noopener noreferrer" className="underline hover:text-white">BivekiGroup</a>
|
||||
</p>
|
||||
|
@ -24,26 +24,25 @@ const Header = () => {
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-white">
|
||||
<div className="text-xl font-bold">SD</div>
|
||||
<div className="text-sm font-semibold -mt-1">STROY</div>
|
||||
<div className="text-xl font-bold">Ваш Дом</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="hidden lg:flex items-center space-x-8">
|
||||
<Link href="#services" className="text-white hover:text-blue-400 transition-colors text-sm font-medium py-1">
|
||||
<Link href="#services" className="text-white hover:text-blue-400 transition-colors text-lg font-medium py-2 px-3 rounded-lg hover:bg-white/10">
|
||||
Услуги
|
||||
</Link>
|
||||
<Link href="#about" className="text-white hover:text-blue-400 transition-colors text-sm font-medium py-1">
|
||||
<Link href="#about" className="text-white hover:text-blue-400 transition-colors text-lg font-medium py-2 px-3 rounded-lg hover:bg-white/10">
|
||||
О нас
|
||||
</Link>
|
||||
<Link href="#projects" className="text-white hover:text-blue-400 transition-colors text-sm font-medium py-1">
|
||||
<Link href="#projects" className="text-white hover:text-blue-400 transition-colors text-lg font-medium py-2 px-3 rounded-lg hover:bg-white/10">
|
||||
Каталог домов
|
||||
</Link>
|
||||
<Link href="#reviews" className="text-white hover:text-blue-400 transition-colors text-sm font-medium py-1">
|
||||
<Link href="#reviews" className="text-white hover:text-blue-400 transition-colors text-lg font-medium py-2 px-3 rounded-lg hover:bg-white/10">
|
||||
Отзывы
|
||||
</Link>
|
||||
<Link href="#contacts" className="text-white hover:text-blue-400 transition-colors text-sm font-medium py-1">
|
||||
<Link href="#contacts" className="text-white hover:text-blue-400 transition-colors text-lg font-medium py-2 px-3 rounded-lg hover:bg-white/10">
|
||||
Контакты
|
||||
</Link>
|
||||
</nav>
|
||||
|
@ -5,9 +5,9 @@ import { X, CheckCircle, AlertCircle } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const materials = [
|
||||
{ label: 'Кирпич/керамический блок', value: 'brick', img: '/images/keramic.jpg' },
|
||||
{ label: 'Газобетон', value: 'aerated', img: '/images/gazobet.png' },
|
||||
{ label: 'Керамзитобетон', value: 'claydite', img: '/images/keramiz.jpg' },
|
||||
{ label: 'Кирпич/керамический блок', value: 'Кирпич/керамический блок', img: '/images/keramic.jpg' },
|
||||
{ label: 'Газобетон', value: 'Газобетон', img: '/images/gazobet.png' },
|
||||
{ label: 'Керамзитобетон', value: 'Керамзитобетон', img: '/images/keramiz.jpg' },
|
||||
];
|
||||
|
||||
const areas = [
|
||||
@ -44,7 +44,7 @@ const HouseCalculatorModal = ({ isOpen, onClose, userName = '', userPhone = '' }
|
||||
const [finish, setFinish] = useState('');
|
||||
const [finance, setFinance] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [success, setSuccess] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
@ -71,6 +71,12 @@ const HouseCalculatorModal = ({ isOpen, onClose, userName = '', userPhone = '' }
|
||||
};
|
||||
|
||||
const handleSubmitCalculator = async () => {
|
||||
// Защита от двойной отправки
|
||||
if (isSubmitting) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/send-telegram', {
|
||||
method: 'POST',
|
||||
@ -92,6 +98,8 @@ const HouseCalculatorModal = ({ isOpen, onClose, userName = '', userPhone = '' }
|
||||
}
|
||||
} catch {
|
||||
setError('Ошибка отправки. Попробуйте позже.');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -102,7 +110,7 @@ const HouseCalculatorModal = ({ isOpen, onClose, userName = '', userPhone = '' }
|
||||
setFinish('');
|
||||
setFinance('');
|
||||
setError('');
|
||||
setSuccess(false);
|
||||
setIsSubmitting(false);
|
||||
onClose();
|
||||
};
|
||||
|
||||
@ -276,22 +284,7 @@ const HouseCalculatorModal = ({ isOpen, onClose, userName = '', userPhone = '' }
|
||||
</div>
|
||||
)}
|
||||
|
||||
{success && (
|
||||
<div className="flex flex-col items-center justify-center py-8 sm:py-12 animate-fadeIn">
|
||||
<div className="bg-green-500/20 border border-green-500/30 backdrop-blur-sm rounded-2xl p-4 sm:p-6 mb-6 sm:mb-8 flex items-center shadow-lg w-full max-w-xl mx-auto">
|
||||
<CheckCircle className="w-8 h-8 sm:w-10 sm:h-10 text-green-400 mr-3 sm:mr-4 flex-shrink-0" />
|
||||
<span className="text-green-300 text-base sm:text-lg md:text-xl font-semibold leading-snug text-left">
|
||||
Благодарим за обращение в нашу компанию!<br className='hidden md:block' /> В течение 15 минут мы свяжемся с вами!
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
className="bg-gradient-to-r from-blue-500 to-purple-500 text-white px-6 sm:px-8 py-3 rounded-full hover:from-blue-600 hover:to-purple-600 transition-all duration-300 text-base sm:text-lg font-semibold shadow-lg hover:scale-105"
|
||||
>
|
||||
Закрыть
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{error && (
|
||||
<div className="mt-4 sm:mt-6 bg-red-500/20 border border-red-500/30 text-red-300 text-center py-2 sm:py-3 px-3 sm:px-4 rounded-xl flex items-center justify-center min-h-[40px] sm:min-h-[48px] text-sm sm:text-base backdrop-blur-sm">
|
||||
@ -300,7 +293,7 @@ const HouseCalculatorModal = ({ isOpen, onClose, userName = '', userPhone = '' }
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!success && step < 5 && (
|
||||
{step < 5 && (
|
||||
<div className="flex justify-between mt-6 sm:mt-8">
|
||||
{step > 1 && (
|
||||
<button
|
||||
@ -321,9 +314,10 @@ const HouseCalculatorModal = ({ isOpen, onClose, userName = '', userPhone = '' }
|
||||
{step === 4 && (
|
||||
<button
|
||||
onClick={handleNext}
|
||||
className="ml-auto bg-gradient-to-r from-blue-500 to-purple-500 text-white px-6 sm:px-8 py-2 rounded-full hover:from-blue-600 hover:to-purple-600 hover:scale-105 transition-all duration-300 text-sm sm:text-base font-medium shadow-lg"
|
||||
disabled={isSubmitting}
|
||||
className="ml-auto bg-gradient-to-r from-blue-500 to-purple-500 text-white px-6 sm:px-8 py-2 rounded-full hover:from-blue-600 hover:to-purple-600 hover:scale-105 transition-all duration-300 text-sm sm:text-base font-medium shadow-lg disabled:opacity-60 disabled:cursor-not-allowed disabled:hover:scale-100"
|
||||
>
|
||||
Рассчитать стоимость
|
||||
{isSubmitting ? 'Отправка...' : 'Рассчитать стоимость'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,36 +1,64 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { useState } from 'react';
|
||||
import FadeInSection from './FadeInSection';
|
||||
import CallbackModal from './CallbackModal';
|
||||
|
||||
const team = [
|
||||
const workExamples = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Степанов Денис',
|
||||
position: 'Основатель и владелец компании',
|
||||
image: '/images/Stepan.jpg',
|
||||
title: 'Дом из газобетона',
|
||||
description: 'Современный двухэтажный дом площадью 120 кв.м. с чистовой отделкой',
|
||||
image: '/images/1q.jpeg',
|
||||
area: '120 кв.м',
|
||||
material: 'Газобетон',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Романов Даниил',
|
||||
position: 'Генеральный директор',
|
||||
image: '/images/Roman.jpg',
|
||||
title: 'Каркасный дом',
|
||||
description: 'Уютный одноэтажный дом с террасой, построен за 45 дней',
|
||||
image: '/images/2q.jpeg',
|
||||
area: '85 кв.м',
|
||||
material: 'Каркасная технология',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Степанова Оксана',
|
||||
position: 'Финансовый директор',
|
||||
image: '/images/Oksana.jpg',
|
||||
title: 'Дом из керамических блоков',
|
||||
description: 'Энергоэффективный дом с современными инженерными системами',
|
||||
image: '/images/3q.jpeg',
|
||||
area: '160 кв.м',
|
||||
material: 'Керамические блоки',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Семенов Максим',
|
||||
position: 'Производитель работ',
|
||||
image: '/images/Maksim.jpg',
|
||||
title: 'Дом из керамзитобетона',
|
||||
description: 'Надежный и теплый дом для большой семьи',
|
||||
image: '/images/4q.jpeg',
|
||||
area: '140 кв.м',
|
||||
material: 'Керамзитобетон',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Компактный дом',
|
||||
description: 'Идеальное решение для молодой семьи с оптимальной планировкой',
|
||||
image: '/images/5q.jpeg',
|
||||
area: '95 кв.м',
|
||||
material: 'Газобетон',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: 'Загородный дом',
|
||||
description: 'Просторный дом с панорамными окнами и современным дизайном',
|
||||
image: '/images/6q.jpeg',
|
||||
area: '180 кв.м',
|
||||
material: 'Кирпич',
|
||||
},
|
||||
];
|
||||
|
||||
const TeamSection = () => {
|
||||
const WorkExamplesSection = () => {
|
||||
const [isCallbackModalOpen, setIsCallbackModalOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<section className="relative py-20 bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 overflow-hidden">
|
||||
{/* Статичный фон */}
|
||||
@ -44,46 +72,67 @@ const TeamSection = () => {
|
||||
{/* Заголовок секции */}
|
||||
<FadeInSection as="div" className="text-center mb-16">
|
||||
<h2 className="text-4xl md:text-5xl font-bold bg-gradient-to-r from-white via-blue-100 to-white bg-clip-text text-transparent mb-4">
|
||||
Наша команда
|
||||
Примеры наших работ
|
||||
</h2>
|
||||
<div className="w-24 h-1 bg-gradient-to-r from-blue-500 to-purple-500 mx-auto rounded-full"></div>
|
||||
</FadeInSection>
|
||||
|
||||
<FadeInSection as="p" className="text-lg text-gray-300 text-center mb-12 max-w-2xl mx-auto" delay={0.2}>
|
||||
Каждый день работает над тем, чтобы предоставить лучший сервис и сделать наших клиентов счастливыми
|
||||
Реальные проекты, которые мы успешно реализовали для наших клиентов
|
||||
</FadeInSection>
|
||||
|
||||
{/* Карточки команды */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
{team.map((member, index) => (
|
||||
{/* Карточки примеров работ */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{workExamples.map((example, index) => (
|
||||
<FadeInSection
|
||||
key={member.id}
|
||||
key={example.id}
|
||||
as="div"
|
||||
className="group relative p-6 rounded-2xl bg-gradient-to-br from-white/10 to-white/5 backdrop-blur-md border border-white/20 hover:border-white/40 transition-all duration-500 hover:scale-105 hover:-translate-y-2"
|
||||
className="group relative rounded-2xl bg-gradient-to-br from-white/10 to-white/5 backdrop-blur-md border border-white/20 hover:border-white/40 transition-all duration-500 hover:scale-105 hover:-translate-y-2 overflow-hidden"
|
||||
delay={0.3 + (index * 0.1)}
|
||||
>
|
||||
{/* Фон */}
|
||||
<div className="absolute inset-0 rounded-2xl bg-gradient-to-br from-blue-500/20 to-purple-500/20 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||
|
||||
<div className="relative z-10 text-center">
|
||||
<div className="relative w-32 h-32 mx-auto mb-4 rounded-full overflow-hidden group-hover:scale-110 transition-transform duration-300">
|
||||
<div className="relative z-10">
|
||||
{/* Изображение */}
|
||||
<div className="relative h-48 w-full overflow-hidden rounded-t-2xl">
|
||||
<Image
|
||||
src={member.image}
|
||||
alt={member.name}
|
||||
src={example.image}
|
||||
alt={example.title}
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 25vw"
|
||||
className="object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
/>
|
||||
{/* Декоративная рамка */}
|
||||
<div className="absolute inset-0 rounded-full border-2 border-white/20 group-hover:border-white/40 transition-colors duration-300"></div>
|
||||
{/* Градиент поверх изображения */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-gray-900/60 via-transparent to-transparent"></div>
|
||||
|
||||
{/* Бейдж с материалом */}
|
||||
<div className="absolute top-4 left-4 px-3 py-1 bg-blue-500/80 backdrop-blur-sm rounded-full text-white text-sm font-medium">
|
||||
{example.material}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-bold text-white mb-2 group-hover:text-blue-300 transition-all duration-300">
|
||||
{member.name}
|
||||
</h3>
|
||||
<p className="text-gray-300 leading-relaxed">
|
||||
{member.position}
|
||||
</p>
|
||||
{/* Контент */}
|
||||
<div className="p-6">
|
||||
<h3 className="text-xl font-bold text-white mb-2 group-hover:text-blue-300 transition-all duration-300">
|
||||
{example.title}
|
||||
</h3>
|
||||
<p className="text-gray-300 text-sm mb-4 leading-relaxed">
|
||||
{example.description}
|
||||
</p>
|
||||
|
||||
{/* Характеристики */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-2 h-2 bg-green-400 rounded-full"></div>
|
||||
<span className="text-gray-400 text-sm">Площадь:</span>
|
||||
<span className="text-white text-sm font-medium">{example.area}</span>
|
||||
</div>
|
||||
<div className="px-3 py-1 bg-white/10 rounded-full text-green-400 text-xs font-medium">
|
||||
Завершен
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Декоративный элемент */}
|
||||
@ -91,9 +140,26 @@ const TeamSection = () => {
|
||||
</FadeInSection>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Призыв к действию */}
|
||||
<FadeInSection as="div" delay={0.8} className="text-center mt-16">
|
||||
<button
|
||||
onClick={() => setIsCallbackModalOpen(true)}
|
||||
className="inline-flex items-center space-x-2 px-6 py-3 rounded-full bg-gradient-to-r from-blue-500/20 to-purple-500/20 border border-white/20 backdrop-blur-sm hover:scale-105 transition-transform duration-300 cursor-pointer hover:from-blue-500/30 hover:to-purple-500/30"
|
||||
>
|
||||
<span className="text-white font-medium">Хотите увидеть свой дом в этой галерее?</span>
|
||||
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
|
||||
</button>
|
||||
</FadeInSection>
|
||||
</div>
|
||||
|
||||
{/* Модальное окно */}
|
||||
<CallbackModal
|
||||
isOpen={isCallbackModalOpen}
|
||||
onClose={() => setIsCallbackModalOpen(false)}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamSection;
|
||||
export default WorkExamplesSection;
|