Files
ckeproekt/app/components/Services.tsx

269 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useRef, useState } from 'react';
import { motion, useInView } from 'framer-motion';
import {
Home,
Droplets,
Waves,
CheckSquare,
Thermometer,
FlaskConical,
ArrowRight,
FileText,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import ContactModal from './ContactModal';
import Link from 'next/link';
import Image from 'next/image';
export const services = [
{
icon: Droplets,
title: 'Экспертиза при заливе',
slug: 'flood-expertise',
description:
'Строительно-техническая экспертиза для определения причины залития помещений, оценка ущерба и рекомендации по устранению последствий.',
details: [
'Определение источника протечки',
'Оценка нанесенного ущерба',
'Расчет стоимости восстановительных работ',
'Составление экспертного заключения',
],
image: '/images/placeholders/services/flood-expertise.jpg',
},
{
icon: Waves,
title: 'Обследование канализации',
slug: 'sewerage-inspection',
description:
'Профессиональное обследование канализационных систем с использованием современного диагностического оборудования.',
details: [
'Видеодиагностика труб',
'Проверка герметичности',
'Оценка состояния коммуникаций',
'Рекомендации по ремонту',
],
image: '/images/placeholders/services/sewerage-inspection.jpg',
},
{
icon: Home,
title: 'Признание дома жилым',
slug: 'house-recognition',
description:
'Экспертиза для признания дома пригодным для круглогодичного проживания, оценка соответствия всем необходимым нормам.',
details: [
'Оценка конструкций',
'Проверка инженерных систем',
'Анализ микроклимата',
'Подготовка документации',
],
image: '/images/placeholders/services/house-recognition.jpg',
},
{
icon: CheckSquare,
title: 'Экспертиза ремонтных работ',
slug: 'renovation-expertise',
description:
'Определение качества выполненных ремонтных работ по отделке помещений, выявление дефектов и нарушений.',
details: [
'Проверка качества материалов',
'Оценка технологии работ',
'Выявление дефектов',
'Рекомендации по устранению',
],
image: '/images/placeholders/services/renovation-expertise.jpg',
},
{
icon: Thermometer,
title: 'Тепловизионная экспертиза',
slug: 'thermal-inspection',
description:
'Определение утечек тепла с помощью современного тепловизионного оборудования, выявление проблемных зон.',
details: [
'Тепловизионная съемка',
'Анализ теплопотерь',
'Выявление мостиков холода',
'Рекомендации по утеплению',
],
image: '/images/placeholders/services/thermal-inspection.jpg',
},
{
icon: Thermometer,
title: 'Определение причины возникновения плесени',
slug: 'mold-inspection',
description:
'Профессиональное обследование с целью выявления и устранения причин появления плесени.',
details: [
'Измерение влажности воздуха и конструкций',
'Проверка вентиляции',
'Взятие соскобов плесени для анализа',
'Рекомендации по устранению',
],
image: '/images/placeholders/services/construction-control.jpg',
},
{
icon: FlaskConical,
title: 'Обмер помещений',
slug: 'room-measurement',
description:
'Профессиональные обмеры помещений с использованием современного оборудования и составлением точных планов.',
details: [
'Замеры всех помещений',
'Создание поэтажных планов',
'Расчет площадей',
'Составление технического паспорта',
],
image: '/images/placeholders/services/room-measurement.jpg',
},
];
const Services = () => {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
const [isModalOpen, setIsModalOpen] = useState(false);
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.1,
},
},
};
const itemVariants = {
hidden: { y: 20, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: {
type: 'spring',
stiffness: 100,
damping: 15,
duration: 0.3,
},
},
};
return (
<section ref={ref} className="py-20 bg-white" id="services">
<div className="container mx-auto px-4">
<motion.div
initial="hidden"
animate={isInView ? 'visible' : 'hidden'}
variants={containerVariants}
>
<motion.div
variants={itemVariants}
className="max-w-3xl mx-auto text-center mb-16"
>
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
Наши услуги
</h2>
<p className="text-xl text-gray-600">
Предоставляем полный спектр услуг по экспертизе и обследованию
объектов
</p>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{services.map((service, index) => (
<Link href={`/services/${service.slug}`} key={index}>
<motion.div
variants={itemVariants}
whileHover={{
scale: 1.02,
transition: { duration: 0.2 },
}}
className="group bg-gray-50 rounded-xl p-6 hover:bg-blue-700 transition-all duration-300 h-full flex flex-col"
>
<div className="flex items-start space-x-4 mb-4">
<div className="flex-shrink-0">
<motion.div
whileHover={{ rotate: 360 }}
transition={{ duration: 0.5 }}
className="w-12 h-12 rounded-lg bg-blue-100 group-hover:bg-blue-600 flex items-center justify-center transition-colors"
>
<service.icon className="h-6 w-6 text-blue-700 group-hover:text-white" />
</motion.div>
</div>
<div>
<h3 className="text-xl font-semibold text-gray-900 group-hover:text-white mb-2 line-clamp-2 h-14">
{service.title}
</h3>
<p className="text-gray-600 group-hover:text-blue-100 mb-4 line-clamp-3 h-18">
{service.description}
</p>
</div>
</div>
<ul className="space-y-2 mb-4">
{service.details.map((detail, idx) => (
<li
key={idx}
className="flex items-start text-gray-600 group-hover:text-blue-100"
>
<ArrowRight className="h-4 w-4 mr-2 flex-shrink-0 mt-1" />
<span className="line-clamp-1">{detail}</span>
</li>
))}
</ul>
<div className="relative h-48 mt-auto rounded-lg overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent z-10" />
<Image
src={service.image}
alt={service.title}
fill
className="object-cover object-center transition-transform duration-300 group-hover:scale-105"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
<motion.div
initial={{ opacity: 0 }}
whileHover={{ opacity: 1 }}
className="absolute inset-0 bg-blue-900/30 flex items-center justify-center z-20"
>
<motion.div
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.9 }}
>
<FileText className="w-10 h-10 text-white" />
</motion.div>
</motion.div>
</div>
</motion.div>
</Link>
))}
</div>
<motion.div variants={itemVariants} className="mt-16 text-center">
<p className="text-gray-600 mb-6 text-lg">
Подберем оптимальное решение под ваши задачи.
<br />
Все консультации бесплатны.
</p>
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
<Button
className="bg-blue-700 hover:bg-blue-800 text-white px-8"
onClick={() => setIsModalOpen(true)}
>
Получить консультацию
</Button>
</motion.div>
</motion.div>
</motion.div>
</div>
<ContactModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
/>
</section>
);
};
export default Services;