Добавлены настройки для оптимизации изображений в конфигурации Next.js и улучшена логика управления состоянием меню в компоненте Header. Обновлены стили и структура мобильного меню, добавлены данные о городах для отображения телефонных номеров.
This commit is contained in:
@ -55,6 +55,18 @@ const Header = ({ selectedCity, onCityChange }: HeaderProps) => {
|
|||||||
return () => window.removeEventListener('scroll', handleScroll);
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
}, [lastScrollY]);
|
}, [lastScrollY]);
|
||||||
|
|
||||||
|
// Закрываем меню при клике на ссылку
|
||||||
|
useEffect(() => {
|
||||||
|
if (isMenuOpen) {
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
};
|
||||||
|
}, [isMenuOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<motion.header
|
<motion.header
|
||||||
@ -65,35 +77,38 @@ const Header = ({ selectedCity, onCityChange }: HeaderProps) => {
|
|||||||
isVisible ? 'translate-y-0' : '-translate-y-full'
|
isVisible ? 'translate-y-0' : '-translate-y-full'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="container mx-auto px-4 py-4">
|
<div className="container mx-auto px-4 py-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
{/* Логотип */}
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
className="flex items-center space-x-2 text-blue-700"
|
className="flex items-center space-x-2 text-blue-700"
|
||||||
>
|
>
|
||||||
<Building className="h-6 w-6" />
|
<Building className="h-6 w-6" />
|
||||||
<div>
|
<div className="flex flex-col">
|
||||||
<span className="text-xl font-bold">ЦКЭ</span>
|
<span className="text-lg sm:text-xl font-bold">ЦКЭ</span>
|
||||||
<span className="block text-sm font-normal text-gray-600">
|
<span className="block text-xs sm:text-sm font-normal text-gray-600">
|
||||||
Центр комплексных экспертиз
|
Центр комплексных экспертиз
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
{/* Мобильный номер телефона и кнопка меню */}
|
||||||
|
<div className="flex items-center space-x-4 lg:hidden">
|
||||||
<a
|
<a
|
||||||
href={`tel:${cityData[selectedCity].phone}`}
|
href={`tel:${cityData[selectedCity].phone}`}
|
||||||
className="flex items-center space-x-1 text-gray-600 hover:text-blue-700 lg:hidden"
|
className="flex items-center text-gray-600 hover:text-blue-700 whitespace-nowrap"
|
||||||
>
|
>
|
||||||
<Phone className="h-4 w-4 text-blue-700" />
|
<Phone className="h-4 w-4 text-blue-700 mr-1" />
|
||||||
<span className="text-sm font-medium">
|
<span className="text-xs font-medium">
|
||||||
{cityData[selectedCity].phone}
|
{cityData[selectedCity].phone}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="lg:hidden p-2 hover:bg-gray-100 rounded-lg"
|
className="p-2 hover:bg-gray-100 rounded-lg"
|
||||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||||
|
aria-label={isMenuOpen ? 'Закрыть меню' : 'Открыть меню'}
|
||||||
>
|
>
|
||||||
{isMenuOpen ? (
|
{isMenuOpen ? (
|
||||||
<X className="h-6 w-6 text-gray-600" />
|
<X className="h-6 w-6 text-gray-600" />
|
||||||
@ -103,6 +118,7 @@ const Header = ({ selectedCity, onCityChange }: HeaderProps) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Десктопная навигация */}
|
||||||
<nav className="hidden lg:flex items-center space-x-6">
|
<nav className="hidden lg:flex items-center space-x-6">
|
||||||
{navigation.map((item) => (
|
{navigation.map((item) => (
|
||||||
<Link
|
<Link
|
||||||
@ -115,6 +131,7 @@ const Header = ({ selectedCity, onCityChange }: HeaderProps) => {
|
|||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
{/* Десктопные контакты и кнопки */}
|
||||||
<div className="hidden lg:flex items-center space-x-4">
|
<div className="hidden lg:flex items-center space-x-4">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
@ -158,62 +175,84 @@ const Header = ({ selectedCity, onCityChange }: HeaderProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
|
||||||
className={`lg:hidden overflow-hidden transition-all duration-300 ${
|
|
||||||
isMenuOpen ? 'max-h-[500px] mt-4' : 'max-h-0'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<nav className="flex flex-col space-y-4 pb-4">
|
|
||||||
{navigation.map((item) => (
|
|
||||||
<Link
|
|
||||||
key={item.href}
|
|
||||||
href={item.href}
|
|
||||||
className="text-gray-600 hover:text-blue-700 transition-colors text-sm py-2"
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
|
||||||
>
|
|
||||||
{item.name}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
<div className="pt-4 border-t">
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant="ghost" className="w-full justify-start">
|
|
||||||
<MapPin className="h-4 w-4 text-blue-700 mr-2" />
|
|
||||||
<span>{selectedCity}</span>
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => onCityChange('Москва')}
|
|
||||||
className="cursor-pointer"
|
|
||||||
>
|
|
||||||
Москва
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => onCityChange('Чебоксары')}
|
|
||||||
className="cursor-pointer"
|
|
||||||
>
|
|
||||||
Чебоксары
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
className="w-full bg-blue-700 hover:bg-blue-800 text-white mt-4"
|
|
||||||
onClick={() => {
|
|
||||||
setIsModalOpen(true);
|
|
||||||
setIsMenuOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Оставить заявку
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</motion.header>
|
</motion.header>
|
||||||
<div className="h-[72px]" />
|
|
||||||
|
{/* Выпадающее мобильное меню */}
|
||||||
|
<motion.div
|
||||||
|
initial={false}
|
||||||
|
animate={isMenuOpen ? { opacity: 1, x: 0 } : { opacity: 0, x: '100%' }}
|
||||||
|
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||||
|
className="fixed inset-y-0 right-0 w-full sm:w-80 bg-white shadow-xl z-50 lg:hidden overflow-y-auto"
|
||||||
|
style={{ top: '56px' }}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col h-full py-8 px-6">
|
||||||
|
<nav className="flex flex-col space-y-6">
|
||||||
|
{navigation.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.href}
|
||||||
|
href={item.href}
|
||||||
|
className="text-gray-800 hover:text-blue-700 transition-colors text-lg font-medium"
|
||||||
|
onClick={() => setIsMenuOpen(false)}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div className="mt-8 pt-8 border-t border-gray-200">
|
||||||
|
<p className="text-gray-500 mb-4">Выберите город:</p>
|
||||||
|
<div className="space-y-3 mb-8">
|
||||||
|
<button
|
||||||
|
onClick={() => onCityChange('Москва')}
|
||||||
|
className={`w-full py-2 px-4 rounded-lg text-left ${
|
||||||
|
selectedCity === 'Москва'
|
||||||
|
? 'bg-blue-50 text-blue-700'
|
||||||
|
: 'text-gray-700 hover:bg-gray-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<MapPin className="h-4 w-4 inline mr-2" />
|
||||||
|
Москва
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => onCityChange('Чебоксары')}
|
||||||
|
className={`w-full py-2 px-4 rounded-lg text-left ${
|
||||||
|
selectedCity === 'Чебоксары'
|
||||||
|
? 'bg-blue-50 text-blue-700'
|
||||||
|
: 'text-gray-700 hover:bg-gray-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<MapPin className="h-4 w-4 inline mr-2" />
|
||||||
|
Чебоксары
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className="w-full bg-blue-700 hover:bg-blue-800 text-white py-6 text-lg"
|
||||||
|
onClick={() => {
|
||||||
|
setIsModalOpen(true);
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Оставить заявку
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Темный оверлей при открытом меню */}
|
||||||
|
{isMenuOpen && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 0.5 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
className="fixed inset-0 bg-black z-40 lg:hidden"
|
||||||
|
style={{ top: '56px' }}
|
||||||
|
onClick={() => setIsMenuOpen(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="h-[56px]" />
|
||||||
|
|
||||||
<ContactModal
|
<ContactModal
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
|
@ -15,6 +15,13 @@ import ContactModal from '@/app/components/ContactModal';
|
|||||||
import { services } from '@/app/components/Services';
|
import { services } from '@/app/components/Services';
|
||||||
import { use } from 'react';
|
import { use } from 'react';
|
||||||
|
|
||||||
|
// Импортируем данные о городах, чтобы использовать телефоны
|
||||||
|
const cityData = {
|
||||||
|
Москва: {
|
||||||
|
phone: '+7 (916) 830-58-58',
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
interface ServicePageProps {
|
interface ServicePageProps {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
slug: string;
|
slug: string;
|
||||||
@ -330,11 +337,11 @@ export default function ServicePage({ params }: ServicePageProps) {
|
|||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col md:flex-row items-center justify-center gap-4 mb-6">
|
<div className="flex flex-col md:flex-row items-center justify-center gap-4 mb-6">
|
||||||
<a
|
<a
|
||||||
href="tel:+79991234567"
|
href={`tel:${cityData.Москва.phone}`}
|
||||||
className="flex items-center text-white hover:text-blue-100"
|
className="flex items-center text-white hover:text-blue-100"
|
||||||
>
|
>
|
||||||
<Phone className="h-5 w-5 mr-2" />
|
<Phone className="h-5 w-5 mr-2" />
|
||||||
<span className="text-xl">+7 (999) 123-45-67</span>
|
<span className="text-xl">{cityData.Москва.phone}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
output: 'standalone',
|
output: 'standalone',
|
||||||
|
images: {
|
||||||
|
unoptimized: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = nextConfig;
|
module.exports = nextConfig;
|
||||||
|
Reference in New Issue
Block a user