refactor: extract sections

This commit is contained in:
Zotov Ivan Yuryevich
2026-04-04 19:28:07 +03:00
parent 18001a918f
commit 56f6ce41c1
17 changed files with 139 additions and 116 deletions

179
src/sections/About.tsx Normal file
View File

@@ -0,0 +1,179 @@
import {
CheckCircle,
Rocket,
Zap,
Target,
Award,
Shield,
Clock,
HeadphonesIcon,
TrendingUp,
Users,
CheckCircle2,
Cloud,
Lock,
Database,
Briefcase,
Server,
Cpu,
Package,
} from 'lucide-react'
import { useState, useEffect } from 'react'
const allVendors = [
'Яндекс.Облако',
'VK Cloud',
'MTC Web Services',
'Сбер Облако',
'Kaspersky',
'Positive Technologies',
'UserGate',
'Astra Linux',
'РедСофт',
'Альт Линукс',
'Postgres Pro',
'Tarantool',
'Мой Офис',
'Р7-Офис',
'1С',
'Галактика',
'Kraftway',
'Aquarius',
'Эльбрус',
'Битрикс24',
'Код Безопасности',
'Zabbix',
'Nginx',
'Selectel',
'Лаборатория Эльбрус',
'Basealt',
'МойОфис Почта',
'Видеомост',
'Jazz Telecom',
'Р-Видео',
'СКБ Контур',
'ЕТИС',
'Кодикс',
'Тензор',
'Infowatch',
'SearchInform',
'Гарда',
'Аладдин Р.Д.',
'С-Терра',
'DeviceLock',
'КриптоПро',
'Базальт СПО',
'Аскон',
'НаноСофт',
'Directum',
'Open Yard',
'ТруКонф',
'Yadro',
'Depo',
'Eltex',
'Qtech',
'D-Link',
]
export function About() {
const [currentVendorIndex, setCurrentVendorIndex] = useState(0)
const [isVisible, setIsVisible] = useState(true)
useEffect(() => {
const interval = setInterval(() => {
setIsVisible(false)
setTimeout(() => {
setCurrentVendorIndex(prev => (prev + 1) % allVendors.length)
setIsVisible(true)
}, 800)
}, 4500)
return () => clearInterval(interval)
}, [])
return (
<section className="py-4 bg-gray-50">
<div className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16">
<div className="max-w-3xl mb-16">
<p className="text-sm uppercase tracking-wider text-gray-500 mb-4">О компании</p>
<h2 className="text-4xl lg:text-5xl text-gray-900 mb-6 flex flex-wrap items-baseline gap-2">
<span>Сотрудничаем с</span>
<span
className="text-blue-600 transition-opacity duration-700 ease-in-out whitespace-nowrap"
style={{ opacity: isVisible ? 1 : 0 }}
>
{allVendors[currentVendorIndex]}
</span>
</h2>
<p className="text-xl text-gray-600">
Комплексные решения для цифровой трансформации бизнеса. Работаем с 2021 года.
</p>
</div>
{/* Ключевые преимущества для клиента */}
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="bg-white p-8 rounded-3xl border border-gray-100 hover:border-blue-200 hover:shadow-lg transition-all">
<div className="w-14 h-14 bg-blue-50 rounded-2xl flex items-center justify-center mb-6">
<Package className="w-7 h-7 text-blue-600" />
</div>
<h3 className="text-xl text-gray-900 mb-3">Поставка оборудования</h3>
<p className="text-gray-600 leading-relaxed">
Комплексные поставки серверов, СХД и сетевого оборудования от российских производителей.
</p>
</div>
<div className="bg-white p-8 rounded-3xl border border-gray-100 hover:border-blue-200 hover:shadow-lg transition-all">
<div className="w-14 h-14 bg-emerald-50 rounded-2xl flex items-center justify-center mb-6">
<Award className="w-7 h-7 text-emerald-600" />
</div>
<h3 className="text-xl text-gray-900 mb-3">Официальное партнерство</h3>
<p className="text-gray-600 leading-relaxed">
Статус золотого партнера ведущих российских вендоров. Прямые контракты и поддержка.
</p>
</div>
<div className="bg-white p-8 rounded-3xl border border-gray-100 hover:border-blue-200 hover:shadow-lg transition-all">
<div className="w-14 h-14 bg-violet-50 rounded-2xl flex items-center justify-center mb-6">
<CheckCircle2 className="w-7 h-7 text-violet-600" />
</div>
<h3 className="text-xl text-gray-900 mb-3">Сертифицированные специалисты</h3>
<p className="text-gray-600 leading-relaxed">
Инженеры с подтвержденной квалификацией по всем внедряемым решениям и платформам.
</p>
</div>
<div className="bg-white p-8 rounded-3xl border border-gray-100 hover:border-blue-200 hover:shadow-lg transition-all">
<div className="w-14 h-14 bg-orange-50 rounded-2xl flex items-center justify-center mb-6">
<Zap className="w-7 h-7 text-orange-600" />
</div>
<h3 className="text-xl text-gray-900 mb-3">Оперативный отклик</h3>
<p className="text-gray-600 leading-relaxed">
Быстрое реагирование на запросы клиента. Четкие сроки решения задач и прозрачная коммуникация.
</p>
</div>
<div className="bg-white p-8 rounded-3xl border border-gray-100 hover:border-blue-200 hover:shadow-lg transition-all">
<div className="w-14 h-14 bg-cyan-50 rounded-2xl flex items-center justify-center mb-6">
<Users className="w-7 h-7 text-cyan-600" />
</div>
<h3 className="text-xl text-gray-900 mb-3">Выделенная команда</h3>
<p className="text-gray-600 leading-relaxed">
Персональный менеджер проекта и команда сертифицированных специалистов.
</p>
</div>
<div className="bg-white p-8 rounded-3xl border border-gray-100 hover:border-blue-200 hover:shadow-lg transition-all">
<div className="w-14 h-14 bg-pink-50 rounded-2xl flex items-center justify-center mb-6">
<Rocket className="w-7 h-7 text-pink-600" />
</div>
<h3 className="text-xl text-gray-900 mb-3">Быстрый старт</h3>
<p className="text-gray-600 leading-relaxed">
Запуск типовых проектов за 2 недели. Пилотное внедрение перед полным развертываием.
</p>
</div>
</div>
</div>
</section>
)
}

100
src/sections/Cases.tsx Normal file
View File

@@ -0,0 +1,100 @@
import { ArrowRight, Cloud, Cpu, Shield } from 'lucide-react'
const cases = [
{
icon: Cloud,
category: 'Облако',
company: 'Торговая сеть',
title: 'Миграция в Яндекс.Облако',
description: 'Перенос корпоративной инфраструктуры 500+ серверов с нулевым простоем',
results: '−40% TCO',
gradient: 'from-blue-100 to-cyan-100',
iconBg: 'bg-white',
iconColor: 'text-blue-600',
},
{
icon: Cpu,
category: 'Автоматизация',
company: 'Производственный холдинг',
title: 'Внедрение 1С:ERP',
description: 'Автоматизация производственного и финансового учета для 15 предприятий',
results: '+25% эффективность',
gradient: 'from-emerald-100 to-teal-100',
iconBg: 'bg-white',
iconColor: 'text-emerald-600',
},
{
icon: Shield,
category: 'Безопасность',
company: 'Финансовая группа',
title: 'Защита периметра Kaspersky',
description: 'Построение многоуровневой системы защиты критичной инфраструктуры',
results: '99.9% uptime',
gradient: 'from-violet-100 to-purple-100',
iconBg: 'bg-white',
iconColor: 'text-violet-600',
},
]
export function Cases() {
return (
<section className="py-4 bg-white">
<div className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16">
<div className="max-w-3xl mb-16">
<p className="text-sm uppercase tracking-wider text-gray-500 mb-4">Кейсы</p>
<h2 className="text-4xl lg:text-5xl text-gray-900 mb-6">Реализованные проекты</h2>
<p className="text-xl text-gray-600">Примеры успешного внедрения с конкретными результатами</p>
</div>
<div className="grid lg:grid-cols-3 gap-8">
{cases.map((caseItem, index) => {
const Icon = caseItem.icon
const floatClass =
index === 0 ? 'animate-float' : index === 1 ? 'animate-float-delay-1' : 'animate-float-delay-2'
return (
<div
key={index}
className="group bg-white rounded-2xl overflow-hidden border border-gray-100 hover:border-blue-200 hover:shadow-xl transition-all duration-300 cursor-pointer flex flex-col"
>
<div
className={`relative h-64 overflow-hidden bg-linear-to-br ${caseItem.gradient} flex items-center justify-center`}
>
<div
className={`w-24 h-24 ${caseItem.iconBg} rounded-2xl flex items-center justify-center group-hover:scale-110 transition-transform duration-300 shadow-md ${floatClass}`}
>
<Icon className={`w-12 h-12 ${caseItem.iconColor}`} />
</div>
<div className="absolute top-4 left-4">
<span className="px-3 py-1 bg-white/90 backdrop-blur-sm text-gray-900 rounded-full border border-gray-200 text-sm">
{caseItem.category}
</span>
</div>
</div>
<div className="p-6 flex flex-col flex-1">
<div className="text-sm text-blue-600 mb-2">{caseItem.company}</div>
<h3 className="text-xl text-gray-900 mb-3">{caseItem.title}</h3>
<p className="text-gray-600 mb-4 leading-relaxed flex-1">{caseItem.description}</p>
<div className="pt-4 border-t border-gray-100 flex items-center justify-between">
<span className="text-gray-900">{caseItem.results}</span>
<ArrowRight className="w-5 h-5 text-gray-400 group-hover:text-blue-600 group-hover:translate-x-1 transition-all" />
</div>
</div>
</div>
)
})}
</div>
<div className="text-center mt-12">
<button className="px-8 py-4 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-all shadow-md hover:shadow-lg inline-flex items-center gap-2">
Все кейсы
<ArrowRight className="w-5 h-5" />
</button>
</div>
</div>
</section>
)
}

View File

@@ -0,0 +1,33 @@
import { ArrowLeft } from 'lucide-react'
interface CategoryPageHeroProps {
title?: string
description?: string
badge?: string
}
export function CategoryPageHero({ title, description, badge }: CategoryPageHeroProps) {
return (
<section className="bg-white border-b border-gray-100 pt-32 pb-16">
<div className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16">
<a
href="/"
className="inline-flex items-center gap-2 text-gray-600 hover:text-blue-600 mb-8 transition-colors group"
>
<ArrowLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
Назад к главной
</a>
<div className="flex items-start gap-8">
<div className="flex-1">
{badge && (
<div className="inline-block px-4 py-1.5 bg-blue-50 text-blue-600 rounded-full text-sm mb-6">{badge}</div>
)}
{title && <h1 className="text-5xl lg:text-6xl mb-6 leading-tight text-gray-900">{title}</h1>}
{description && <p className="text-xl text-gray-600 max-w-3xl leading-relaxed">{description}</p>}
</div>
</div>
</div>
</section>
)
}

102
src/sections/Contact.tsx Normal file
View File

@@ -0,0 +1,102 @@
import { Phone, Mail, MapPin, Clock } from 'lucide-react'
export function Contact() {
return (
<section className="py-4 bg-gray-50">
<div className="max-w-225 mx-auto px-6">
{/* Заголовок */}
<div className="text-center mb-12">
<h2 className="text-3xl lg:text-4xl text-gray-900 mb-3">Обсудим ваш проект</h2>
<p className="text-gray-600">Оставьте заявку — перезвоним в течение часа</p>
</div>
{/* Форма */}
<div className="bg-white rounded-2xl p-8 shadow-sm border border-gray-100">
<form className="space-y-5">
<div className="grid md:grid-cols-2 gap-5">
<div>
<input
type="text"
id="name"
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
placeholder="Ваше имя"
required
/>
</div>
<div>
<input
type="text"
id="company"
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
placeholder="Компания"
/>
</div>
</div>
<div className="grid md:grid-cols-2 gap-5">
<div>
<input
type="email"
id="email"
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
placeholder="Email"
required
/>
</div>
<div>
<input
type="tel"
id="phone"
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
placeholder="Телефон"
required
/>
</div>
</div>
<div>
<textarea
id="message"
rows={4}
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all resize-none"
placeholder="Расскажите о вашем проекте"
></textarea>
</div>
<div className="flex flex-col sm:flex-row items-center gap-4">
<button
type="submit"
className="w-full sm:w-auto px-8 py-3 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all"
>
Отправить заявку
</button>
<p className="text-sm text-gray-500">Нажимая кнопку, вы соглашаетесь с политикой конфиденциальности</p>
</div>
</form>
</div>
{/* Контакты компактно */}
<div className="mt-12 flex flex-wrap justify-center gap-8 text-sm text-gray-600">
<a href="tel:+74951234567" className="flex items-center gap-2 hover:text-blue-600 transition-colors">
<Phone className="w-4 h-4" />
<span>+7 (495) 123-45-67</span>
</a>
<a href="mailto:info@softclick.ru" className="flex items-center gap-2 hover:text-blue-600 transition-colors">
<Mail className="w-4 h-4" />
<span>info@softclick.ru</span>
</a>
<div className="flex items-center gap-2">
<MapPin className="w-4 h-4" />
<span>Москва, Пресненская наб., 12</span>
</div>
<div className="flex items-center gap-2">
<Clock className="w-4 h-4" />
<span>Пн-Пт 9:00–18:00</span>
</div>
</div>
</div>
</section>
)
}

142
src/sections/Footer.tsx Normal file
View File

@@ -0,0 +1,142 @@
import { Mail, Phone, MapPin, Send, Hash, Code, Video } from 'lucide-react'
export function Footer() {
return (
<footer className="bg-gray-900 text-gray-400 pt-20 pb-10 border-t border-gray-800">
<div className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16">
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-12 mb-12">
<div>
<div className="flex items-center gap-3 mb-6">
<div className="w-9 h-9 bg-linear-to-br from-blue-600 to-blue-700 rounded-lg flex items-center justify-center shadow-md">
<div className="w-5 h-5 border-2 border-white rounded"></div>
</div>
<span className="text-2xl text-white tracking-tight">SOFTCLICK</span>
</div>
<p className="mb-6 leading-relaxed">Комплексные IT-решения для развития бизнеса</p>
<div className="flex gap-4">
<a
href="#"
className="w-10 h-10 bg-gray-800 rounded-lg flex items-center justify-center hover:bg-blue-600 transition-colors border border-gray-700 hover:border-blue-600"
title="Telegram"
>
<Send className="w-5 h-5" />
</a>
<a
href="#"
className="w-10 h-10 bg-gray-800 rounded-lg flex items-center justify-center hover:bg-blue-600 transition-colors border border-gray-700 hover:border-blue-600"
title="VK"
>
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
<path d="M12.785 16.241s.288-.032.436-.19c.136-.145.131-.419.131-.419s-.019-1.281.576-1.47c.586-.185 1.341 1.238 2.138 1.788.604.417 1.064.325 1.064.325l2.137-.03s1.117-.069.587-.948c-.043-.072-.309-.651-1.589-1.84-1.339-1.246-1.16-1.044.453-3.197.983-1.312 1.376-2.113 1.253-2.456-.117-.328-.84-.241-.84-.241l-2.406.015s-.178-.024-.31.055c-.129.077-.212.258-.212.258s-.381.987-.887 1.827c-1.063 1.766-1.497 1.86-1.668 1.748-.395-.259-.296-1.04-.296-1.595 0-1.734.263-2.456-.512-2.642-.258-.062-.447-.102-1.106-.109-.845-.009-1.56.003-1.964.201-.269.132-.476.426-.35.443.156.021.509.095.696.349.241.328.233 1.064.233 1.064s.139 2.042-.324 2.296c-.318.174-.754-.182-1.69-1.806-.479-.821-.841-1.729-.841-1.729s-.07-.171-.194-.263c-.151-.111-.362-.146-.362-.146l-2.286.015s-.343.01-.469.159c-.112.132-.009.405-.009.405s1.789 4.182 3.814 6.291c1.857 1.935 3.965 1.808 3.965 1.808h.955z" />
</svg>
</a>
<a
href="#"
className="w-10 h-10 bg-gray-800 rounded-lg flex items-center justify-center hover:bg-blue-600 transition-colors border border-gray-700 hover:border-blue-600"
title="Habr"
>
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm4.326 14.467H7.674v-1.652h8.652v1.652zm0-3.239H7.674V9.576h8.652v1.652z" />
</svg>
</a>
<a
href="#"
className="w-10 h-10 bg-gray-800 rounded-lg flex items-center justify-center hover:bg-blue-600 transition-colors border border-gray-700 hover:border-blue-600"
title="VK Video"
>
<Video className="w-5 h-5" />
</a>
</div>
</div>
<div>
<h4 className="text-white mb-6">Решения</h4>
<ul className="space-y-3">
<li>
<a href="#" className="hover:text-blue-400 transition-colors">
Облачные решения
</a>
</li>
<li>
<a href="#" className="hover:text-blue-400 transition-colors">
Кибербезопасность
</a>
</li>
<li>
<a href="#" className="hover:text-blue-400 transition-colors">
Цифровая трансформация
</a>
</li>
<li>
<a href="#" className="hover:text-blue-400 transition-colors">
Управление данными
</a>
</li>
</ul>
</div>
<div>
<h4 className="text-white mb-6">Компания</h4>
<ul className="space-y-3">
<li>
<a href="#" className="hover:text-blue-400 transition-colors">
О нас
</a>
</li>
<li>
<a href="#" className="hover:text-blue-400 transition-colors">
Карьера
</a>
</li>
<li>
<a href="#" className="hover:text-blue-400 transition-colors">
Новости
</a>
</li>
<li>
<a href="#" className="hover:text-blue-400 transition-colors">
Партнерам
</a>
</li>
</ul>
</div>
<div>
<h4 className="text-white mb-6">Контакты</h4>
<ul className="space-y-4">
<li className="flex items-start gap-3">
<Phone className="w-5 h-5 mt-1 shrink-0 text-blue-400" />
<div>
<div className="text-white">+7 (495) 123-45-67</div>
<div>Пн-Пт 9:00-18:00</div>
</div>
</li>
<li className="flex items-start gap-3">
<Mail className="w-5 h-5 mt-1 shrink-0 text-blue-400" />
<a href="mailto:info@softclick.ru" className="hover:text-blue-400 transition-colors">
info@softclick.ru
</a>
</li>
<li className="flex items-start gap-3">
<MapPin className="w-5 h-5 mt-1 shrink-0 text-blue-400" />
<div>Москва, Пресненская наб., 12</div>
</li>
</ul>
</div>
</div>
<div className="pt-8 border-t border-gray-800 flex flex-col md:flex-row justify-between items-center gap-4">
<div>&copy; 2025 SOFTCLICK. Все права защищены.</div>
<div className="flex gap-6">
<a href="#" className="hover:text-blue-400 transition-colors">
Политика конфиденциальности
</a>
<a href="#" className="hover:text-blue-400 transition-colors">
Условия использования
</a>
</div>
</div>
</div>
</footer>
)
}

229
src/sections/Header.tsx Normal file
View File

@@ -0,0 +1,229 @@
import { Menu, X, ChevronDown } from 'lucide-react'
import { useState } from 'react'
import { ContactModal } from '../components/ContactModal'
import { navigateHomeSectionAnchor } from '../lib/scrollToHash'
export function Header() {
const [isMenuOpen, setIsMenuOpen] = useState(false)
const [isSolutionsOpen, setIsSolutionsOpen] = useState(false)
const [isServicesOpen, setIsServicesOpen] = useState(false)
const [isContactModalOpen, setIsContactModalOpen] = useState(false)
const solutions = [
{ name: 'Облако', path: 'cloud' },
{ name: 'Российское ПО', path: 'russian-software' },
{ name: 'Кибербезопасность', path: 'cybersecurity' },
{ name: 'Решения на базе ИИ', path: 'ai-solutions' },
{ name: 'Российские ноутбуки, ПК и серверы', path: 'russian-hardware' },
{ name: 'Аппаратные решения', path: 'hardware-solutions' },
{ name: 'САПР и ГИС', path: 'cad-gis' },
{ name: 'Индустриальные решения', path: 'industrial-solutions' },
{ name: 'Для госсектора', path: 'government' },
{ name: 'Решения для бизнеса', path: 'business' },
]
const services = [
{ name: 'Консалтинг и проектирование', path: 'consulting' },
{ name: 'Поставка и внедрение решений', path: 'implementation' },
{ name: 'Управляемые сервисы', path: 'managed-services' },
{ name: 'Интеграция и разработка', path: 'integration' },
{ name: 'Специализированные услуги по отраслям', path: 'specialized-services' },
{ name: 'Обслуживание и сопровождение', path: 'support' },
{ name: 'Услуги по подписке', path: 'subscription' },
]
return (
<header className="fixed top-0 left-0 right-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-100">
<nav className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-12">
<a href="/" className="flex items-center gap-3 group">
<span className="text-2xl text-gray-900 tracking-wide font-bold">SOFTCLICK</span>
</a>
<div className="hidden lg:flex items-center gap-8">
<div
className="relative"
onMouseEnter={() => setIsSolutionsOpen(true)}
onMouseLeave={() => setIsSolutionsOpen(false)}
>
<button className="flex items-center gap-1 text-gray-700 hover:text-blue-600 transition-colors py-2 whitespace-nowrap">
Решения
<ChevronDown className={`w-4 h-4 transition-transform ${isSolutionsOpen ? 'rotate-180' : ''}`} />
</button>
{isSolutionsOpen && (
<div className="absolute top-full left-0 pt-2">
<div className="w-80 bg-white rounded-xl shadow-xl border border-gray-100 py-3 animate-in fade-in slide-in-from-top-2 duration-200">
{solutions.map(solution => (
<a
key={solution.path}
href={`/${solution.path}`}
className="block w-full text-left px-5 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors"
>
{solution.name}
</a>
))}
</div>
</div>
)}
</div>
<div
className="relative"
onMouseEnter={() => setIsServicesOpen(true)}
onMouseLeave={() => setIsServicesOpen(false)}
>
<button className="flex items-center gap-1 text-gray-700 hover:text-blue-600 transition-colors py-2 whitespace-nowrap">
Услуги
<ChevronDown className={`w-4 h-4 transition-transform ${isServicesOpen ? 'rotate-180' : ''}`} />
</button>
{isServicesOpen && (
<div className="absolute top-full left-0 pt-2">
<div className="w-80 bg-white rounded-xl shadow-xl border border-gray-100 py-3 animate-in fade-in slide-in-from-top-2 duration-200">
{services.map(service => (
<a
key={service.path}
href={`/${service.path}`}
className="block w-full text-left px-5 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors"
>
{service.name}
</a>
))}
</div>
</div>
)}
</div>
<a
href="/#cases"
className="text-gray-700 hover:text-blue-600 transition-colors whitespace-nowrap"
onClick={e => navigateHomeSectionAnchor(e, 'cases')}
>
Кейсы
</a>
<a
href="/#about"
className="text-gray-700 hover:text-blue-600 transition-colors whitespace-nowrap"
onClick={e => navigateHomeSectionAnchor(e, 'about')}
>
О компании
</a>
<a
href="/#contact"
className="text-gray-700 hover:text-blue-600 transition-colors whitespace-nowrap"
onClick={e => navigateHomeSectionAnchor(e, 'contact')}
>
Контакты
</a>
</div>
</div>
<div className="hidden lg:flex items-center gap-4">
<button
className="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-all shadow-sm hover:shadow-md"
onClick={() => setIsContactModalOpen(true)}
>
Связаться
</button>
</div>
<button type="button" onClick={() => setIsMenuOpen(!isMenuOpen)} className="lg:hidden p-2">
{isMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
</button>
</div>
{isMenuOpen && (
<div className="lg:hidden pt-4 pb-2">
<div className="flex flex-col gap-4">
<div>
<button
type="button"
onClick={() => setIsSolutionsOpen(!isSolutionsOpen)}
className="text-gray-700 hover:text-blue-600 w-full text-left flex items-center justify-between"
>
Решения
<ChevronDown className={`w-4 h-4 transition-transform ${isSolutionsOpen ? 'rotate-180' : ''}`} />
</button>
{isSolutionsOpen && (
<div className="pl-4 mt-2 space-y-2">
{solutions.map(solution => (
<a
key={solution.path}
href={`/${solution.path}`}
onClick={() => {
setIsMenuOpen(false)
setIsSolutionsOpen(false)
}}
className="block w-full text-left text-sm text-gray-600 hover:text-blue-600 py-2"
>
{solution.name}
</a>
))}
</div>
)}
</div>
<div>
<button
type="button"
onClick={() => setIsServicesOpen(!isServicesOpen)}
className="text-gray-700 hover:text-blue-600 w-full text-left flex items-center justify-between"
>
Услуги
<ChevronDown className={`w-4 h-4 transition-transform ${isServicesOpen ? 'rotate-180' : ''}`} />
</button>
{isServicesOpen && (
<div className="pl-4 mt-2 space-y-2">
{services.map(service => (
<a
key={service.path}
href={`/${service.path}`}
onClick={() => {
setIsMenuOpen(false)
setIsServicesOpen(false)
}}
className="block w-full text-left text-sm text-gray-600 hover:text-blue-600 py-2"
>
{service.name}
</a>
))}
</div>
)}
</div>
<a
href="/#cases"
onClick={e => {
navigateHomeSectionAnchor(e, 'cases')
setIsMenuOpen(false)
}}
className="text-gray-700 hover:text-blue-600"
>
Кейсы
</a>
<a
href="/#about"
onClick={e => {
navigateHomeSectionAnchor(e, 'about')
setIsMenuOpen(false)
}}
className="text-gray-700 hover:text-blue-600"
>
О компании
</a>
<a
href="/#contact"
onClick={e => {
navigateHomeSectionAnchor(e, 'contact')
setIsMenuOpen(false)
}}
className="text-gray-700 hover:text-blue-600"
>
Контакты
</a>
</div>
</div>
)}
</nav>
<ContactModal isOpen={isContactModalOpen} onClose={() => setIsContactModalOpen(false)} />
</header>
)
}

105
src/sections/Hero.tsx Normal file
View File

@@ -0,0 +1,105 @@
import { ArrowRight, Play, Cloud, Shield, Zap, Database, Code, Users } from 'lucide-react'
interface HeroProps {
onOpenContactModal?: () => void
}
export function Hero({ onOpenContactModal }: HeroProps) {
return (
<section className="relative pt-32 pb-20 overflow-hidden bg-white">
<div className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16">
<div className="grid lg:grid-cols-2 gap-16 items-center">
<div className="space-y-8">
<div className="inline-flex items-center gap-2 px-4 py-2 bg-blue-50 text-blue-700 rounded-full border border-blue-100">
<span className="w-2 h-2 bg-blue-600 rounded-full animate-pulse"></span>
<span>Надежный партнер с 2021 года</span>
</div>
<h1 className="text-5xl lg:text-6xl text-gray-900 leading-tight">IT-решения для роста вашего бизнеса</h1>
<p className="text-xl text-gray-600 leading-relaxed">
Проектируем, внедряем и сопровождаем корпоративные IT-системы. Облачная инфраструктура, безопасность,
автоматизация процессов.
</p>
<div className="flex flex-wrap gap-4">
<button
className="group flex items-center gap-2 px-8 py-4 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all"
onClick={onOpenContactModal}
>
Обсудить проект
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</button>
<button className="flex items-center gap-2 px-8 py-4 bg-gray-50 text-gray-900 rounded-xl hover:bg-gray-100 transition-all">
Портфолио
</button>
</div>
<div className="grid grid-cols-3 gap-8 pt-8 border-t border-gray-100">
<div>
<div className="text-3xl text-gray-900 mb-1">300+</div>
<div className="text-gray-600">клиентов</div>
</div>
<div>
<div className="text-3xl text-gray-900 mb-1">50+</div>
<div className="text-gray-600">партнерств</div>
</div>
<div>
<div className="text-3xl text-gray-900 mb-1">95%</div>
<div className="text-gray-600">повторных обращений</div>
</div>
</div>
</div>
{/* Простая сетка иконок в стиле VK/Яндекс */}
<div className="relative">
<style>{`
@keyframes gentleFloat {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-8px); }
}
.float-item {
animation: gentleFloat 6s ease-in-out infinite;
}
.float-item:nth-child(1) { animation-delay: 0s; }
.float-item:nth-child(2) { animation-delay: 0.5s; }
.float-item:nth-child(3) { animation-delay: 1s; }
.float-item:nth-child(4) { animation-delay: 1.5s; }
.float-item:nth-child(5) { animation-delay: 2s; }
.float-item:nth-child(6) { animation-delay: 2.5s; }
`}</style>
<div className="grid grid-cols-3 gap-6">
<div className="space-y-6">
<div className="float-item aspect-square bg-blue-50 rounded-3xl flex items-center justify-center group hover:bg-blue-100 transition-colors">
<Cloud className="w-12 h-12 text-blue-600" />
</div>
<div className="float-item aspect-square bg-linear-to-br from-blue-500 to-blue-600 rounded-3xl flex items-center justify-center shadow-lg">
<Shield className="w-12 h-12 text-white" />
</div>
</div>
<div className="space-y-6 pt-12">
<div className="float-item aspect-square bg-violet-50 rounded-3xl flex items-center justify-center group hover:bg-violet-100 transition-colors">
<Database className="w-12 h-12 text-violet-600" />
</div>
<div className="float-item aspect-square bg-emerald-50 rounded-3xl flex items-center justify-center group hover:bg-emerald-100 transition-colors">
<Code className="w-12 h-12 text-emerald-600" />
</div>
</div>
<div className="space-y-6">
<div className="float-item aspect-square bg-orange-50 rounded-3xl flex items-center justify-center group hover:bg-orange-100 transition-colors">
<Zap className="w-12 h-12 text-orange-600" />
</div>
<div className="float-item aspect-square bg-cyan-50 rounded-3xl flex items-center justify-center group hover:bg-cyan-100 transition-colors">
<Users className="w-12 h-12 text-cyan-600" />
</div>
</div>
</div>
</div>
</div>
</div>
</section>
)
}

109
src/sections/Services.tsx Normal file
View File

@@ -0,0 +1,109 @@
import { Cloud, Shield, Cpu, Database, Server, Settings } from 'lucide-react'
const services = [
{
icon: Cloud,
title: 'Облачные решения',
description: 'Миграция и управление инфраструктурой на базе Яндекс.Облако и VK Cloud',
gradient: 'from-blue-50 to-cyan-50',
iconBg: 'bg-white',
iconColor: 'text-blue-600',
},
{
icon: Shield,
title: 'Кибербезопасность',
description: 'Комплексная защита периметра и данных с решениями Kaspersky',
gradient: 'from-violet-50 to-purple-50',
iconBg: 'bg-white',
iconColor: 'text-violet-600',
},
{
icon: Cpu,
title: 'Цифровая трансформация',
description: 'Автоматизация процессов на платформах 1С и Галактика',
gradient: 'from-emerald-50 to-teal-50',
iconBg: 'bg-white',
iconColor: 'text-emerald-600',
},
{
icon: Database,
title: 'Управление данными',
description: 'Развертывание и поддержка СУБД Postgres Pro',
gradient: 'from-orange-50 to-amber-50',
iconBg: 'bg-white',
iconColor: 'text-orange-600',
},
{
icon: Server,
title: 'Серверная инфраструктура',
description: 'Поставка и настройка оборудования Kraftway',
gradient: 'from-pink-50 to-rose-50',
iconBg: 'bg-white',
iconColor: 'text-pink-600',
},
{
icon: Settings,
title: 'Системная интеграция',
description: 'Построение корпоративной ИТ-архитектуры на Astra Linux',
gradient: 'from-indigo-50 to-blue-50',
iconBg: 'bg-white',
iconColor: 'text-indigo-600',
},
]
export function Services() {
return (
<section id="services" className="py-24 bg-white">
<div className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16">
<div className="max-w-3xl mb-16">
<p className="text-sm uppercase tracking-wider text-gray-500 mb-4">Услуги</p>
<h2 className="text-4xl lg:text-5xl text-gray-900 mb-6">Комплексные IT-решения для вашего бизнеса</h2>
<p className="text-xl text-gray-600">Полный цикл: от стратегии до технической поддержки</p>
</div>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{services.map((service, index) => {
const Icon = service.icon
const floatClasses = [
'animate-float',
'animate-float-delay-1',
'animate-float-delay-2',
'animate-float-delay-3',
'animate-float-delay-4',
'animate-float-delay-5',
]
const floatClass = floatClasses[index % 6]
return (
<div
key={index}
className="group bg-white rounded-2xl border border-gray-100 hover:border-blue-200 hover:shadow-xl transition-all duration-300 overflow-hidden"
>
<div
className={`relative h-48 overflow-hidden bg-linear-to-br ${service.gradient} flex items-center justify-center`}
>
<div
className={`w-20 h-20 ${service.iconBg} rounded-2xl flex items-center justify-center group-hover:scale-110 transition-transform duration-300 shadow-md ${floatClass}`}
>
<Icon className={`w-10 h-10 ${service.iconColor}`} />
</div>
</div>
<div className="p-6">
<h3 className="text-xl text-gray-900 mb-3">{service.title}</h3>
<p className="text-gray-600 mb-4 leading-relaxed">{service.description}</p>
<button className="text-blue-600 hover:text-blue-700 transition-colors inline-flex items-center gap-2">
Подробнее
<span className="transition-all">&rarr;</span>
</button>
</div>
</div>
)
})}
</div>
</div>
</section>
)
}

View File

@@ -0,0 +1,80 @@
import { Building2, ShoppingCart, Factory, Heart, GraduationCap, Landmark } from 'lucide-react'
const solutions = [
{
icon: Building2,
title: 'Корпоративный сектор',
description: 'ERP, CRM, автоматизация документооборота',
projects: '1200+ проектов',
},
{
icon: ShoppingCart,
title: 'Ритейл и e-commerce',
description: 'Омниканальные платформы продаж',
projects: '850+ проектов',
},
{
icon: Factory,
title: 'Производство',
description: 'IoT, системы управления производством',
projects: '650+ проектов',
},
{
icon: Heart,
title: 'Здравоохранение',
description: 'Медицинские информационные системы',
projects: '420+ проектов',
},
{
icon: GraduationCap,
title: 'Образование',
description: 'Платформы дистанционного обучения',
projects: '380+ проектов',
},
{
icon: Landmark,
title: 'Государственный сектор',
description: 'Цифровизация госуслуг',
projects: '500+ проектов',
},
]
export function Solutions() {
return (
<section id="solutions" className="py-24 bg-gray-50">
<div className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16">
<div className="max-w-3xl mb-16">
<p className="text-sm uppercase tracking-wider text-gray-500 mb-4">Отраслевые решения</p>
<h2 className="text-4xl lg:text-5xl text-gray-900 mb-6">Решения под ключ для разных отраслей</h2>
<p className="text-xl text-gray-600">Глубокое понимание специфики бизнеса в каждой отрасли</p>
</div>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{solutions.map((solution, index) => {
const Icon = solution.icon
return (
<div
key={index}
className="group p-8 bg-white rounded-2xl hover:shadow-xl border border-gray-100 hover:border-blue-100 transition-all duration-300 cursor-pointer"
>
<div className="flex items-start gap-4 mb-4">
<div className="w-12 h-12 bg-linear-to-br from-blue-500 to-blue-600 rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform shadow-sm">
<Icon className="w-6 h-6 text-white" />
</div>
<div className="flex-1">
<h3 className="text-lg text-gray-900 mb-2">{solution.title}</h3>
<p className="text-gray-600">{solution.description}</p>
</div>
</div>
<div className="pt-4 border-t border-gray-100">
<span className="text-sm text-blue-600">{solution.projects}</span>
</div>
</div>
)
})}
</div>
</div>
</section>
)
}

25
src/sections/Vendors.tsx Normal file
View File

@@ -0,0 +1,25 @@
import { VendorCard } from '../components/VendorCard'
interface Vendor {
name?: string
description?: string
products?: string[]
}
interface VendorsProps {
vendors?: Vendor[]
}
export function Vendors({ vendors }: VendorsProps) {
if (vendors && vendors.length > 0) {
return (
<div className="max-w-400 mx-auto px-6 xl:px-12 2xl:px-16 py-20">
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{vendors.map(({ name, description, products }, index) => (
<VendorCard key={index} name={name} description={description} products={products} />
))}
</div>
</div>
)
}
}