Комплексные улучшения: FAQ, форма, карта, WhatsApp/Telegram, SEO
- FloatingContacts: кнопки WhatsApp + Telegram (правый нижний угол) - ScrollToTop: кнопка "наверх" (появляется после 400px скролла) - FAQ секция: 6 вопросов с аккордеоном, id=faq в навбаре - Hero: телефон под CTA кнопками - Форма: поле телефона, реальная отправка (fetch FormSpree или mailto fallback) - Яндекс.Карты embed в блоке контактов - Navigation: убран дубль "Контакты" из links, добавлен FAQ - img width/height на логотипах (антиCLS) - JSON-LD: og-image.svg → .png, добавлен postalCode, уточнен streetAddress - prerender.mjs: динамически обновляет хеш preload шрифта - src/config.js: централизованный конфиг (WhatsApp, Telegram, form endpoint) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import React, { useState } from 'react'
|
||||
import { ChevronRight, Server, Shield, Headphones, Phone, Mail, MapPin, CheckCircle2 } from 'lucide-react'
|
||||
import { ChevronRight, Server, Shield, Headphones, Phone, Mail, MapPin, CheckCircle2, ChevronDown } from 'lucide-react'
|
||||
import { useLanguage } from '../contexts/LanguageContext.jsx'
|
||||
import { useReveal } from '../components/useReveal.js'
|
||||
import { FORM_ENDPOINT, EMAIL } from '../config.js'
|
||||
|
||||
const serviceIcons = [Server, Shield, Headphones]
|
||||
|
||||
@@ -26,20 +27,69 @@ function ServiceCard({ icon: Icon, title, description, points }) {
|
||||
)
|
||||
}
|
||||
|
||||
function FaqItem({ q, a }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
return (
|
||||
<div className="border border-slate-200 rounded-xl overflow-hidden">
|
||||
<button
|
||||
onClick={() => setOpen(!open)}
|
||||
className="w-full flex items-center justify-between px-6 py-4 text-left bg-white hover:bg-slate-50 transition-colors"
|
||||
>
|
||||
<span className="font-semibold text-slate-800 pr-4">{q}</span>
|
||||
<ChevronDown size={18} className={`text-slate-400 flex-shrink-0 transition-transform duration-200 ${open ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
{open && (
|
||||
<div className="px-6 py-4 bg-slate-50 border-t border-slate-200 text-slate-600 text-sm leading-relaxed">
|
||||
{a}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const { t } = useLanguage()
|
||||
const [formState, setFormState] = useState({ name: '', company: '', message: '' })
|
||||
const [formState, setFormState] = useState({ name: '', company: '', phone: '', message: '' })
|
||||
const [submitted, setSubmitted] = useState(false)
|
||||
const [sending, setSending] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
|
||||
const aboutRef = useReveal()
|
||||
const contactRef = useReveal()
|
||||
const faqRef = useReveal()
|
||||
|
||||
const services = t('services.items')
|
||||
const stats = [t('about.stat1'), t('about.stat2'), t('about.stat3')]
|
||||
const faqItems = t('faq.items')
|
||||
const firstPhone = t('contact.phones')[0]
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
setSubmitted(true)
|
||||
setSending(true)
|
||||
setError('')
|
||||
|
||||
if (FORM_ENDPOINT) {
|
||||
try {
|
||||
const res = await fetch(FORM_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(formState),
|
||||
})
|
||||
if (res.ok) {
|
||||
setSubmitted(true)
|
||||
} else {
|
||||
setError(t('contact.formError'))
|
||||
}
|
||||
} catch {
|
||||
setError(t('contact.formError'))
|
||||
}
|
||||
} else {
|
||||
// Fallback: mailto
|
||||
const body = `Имя: ${formState.name}\nКомпания: ${formState.company}\nТелефон: ${formState.phone}\n\n${formState.message}`
|
||||
window.location.href = `mailto:${EMAIL}?subject=Заявка с сайта sag24.ru&body=${encodeURIComponent(body)}`
|
||||
setSubmitted(true)
|
||||
}
|
||||
setSending(false)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -61,7 +111,7 @@ export default function Home() {
|
||||
<p className="text-lg sm:text-xl text-slate-300 max-w-2xl mx-auto mb-10 leading-relaxed">
|
||||
{t('hero.subtitle')}
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-10">
|
||||
<a href="#contact" className="px-8 py-4 bg-blue-600 hover:bg-blue-500 text-white font-semibold rounded-lg transition-all duration-300 flex items-center justify-center gap-2 group">
|
||||
{t('hero.cta')}
|
||||
<ChevronRight size={20} className="group-hover:translate-x-1 transition-transform" />
|
||||
@@ -70,6 +120,10 @@ export default function Home() {
|
||||
{t('hero.ctaSecondary')}
|
||||
</a>
|
||||
</div>
|
||||
<a href={`tel:${firstPhone.replace(/\D/g,'')}`} className="inline-flex items-center gap-2 text-blue-300 hover:text-white transition-colors text-lg font-medium">
|
||||
<Phone size={18} />
|
||||
{firstPhone}
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -165,6 +219,23 @@ export default function Home() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FAQ */}
|
||||
<section id="faq" className="py-24 bg-white border-t border-slate-100">
|
||||
<div className="max-w-3xl mx-auto px-4 sm:px-6">
|
||||
<div ref={faqRef} className="section-reveal">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 mb-4">{t('faq.title')}</h2>
|
||||
<p className="text-slate-500 text-lg">{t('faq.subtitle')}</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
{faqItems.map((item, i) => (
|
||||
<FaqItem key={i} q={item.q} a={item.a} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact */}
|
||||
<section id="contact" className="py-24 bg-slate-900">
|
||||
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
@@ -197,8 +268,8 @@ export default function Home() {
|
||||
<a href="mailto:info@sag24.ru" className="text-white font-medium hover:text-blue-400 transition-colors">info@sag24.ru</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-10 h-10 bg-blue-500/10 rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 bg-blue-500/10 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5">
|
||||
<MapPin size={20} className="text-blue-400" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -207,6 +278,19 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Yandex Map */}
|
||||
<div className="mt-8 rounded-xl overflow-hidden border border-white/10">
|
||||
<iframe
|
||||
src="https://yandex.ru/map-widget/v1/?ll=37.857200%2C56.009400&z=16&pt=37.857200%2C56.009400%2Cpm2rdm&l=map"
|
||||
width="100%"
|
||||
height="220"
|
||||
frameBorder="0"
|
||||
title="Офис Сисадмингрупп на карте"
|
||||
loading="lazy"
|
||||
allowFullScreen
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/5 border border-white/10 rounded-xl p-8">
|
||||
@@ -232,19 +316,28 @@ export default function Home() {
|
||||
onChange={e => setFormState({ ...formState, company: e.target.value })}
|
||||
className="w-full bg-white/10 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-slate-400 focus:outline-none focus:border-blue-500 transition-colors text-sm"
|
||||
/>
|
||||
<input
|
||||
type="tel"
|
||||
placeholder={t('contact.formPhone')}
|
||||
value={formState.phone}
|
||||
onChange={e => setFormState({ ...formState, phone: e.target.value })}
|
||||
className="w-full bg-white/10 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-slate-400 focus:outline-none focus:border-blue-500 transition-colors text-sm"
|
||||
/>
|
||||
<textarea
|
||||
required
|
||||
rows={5}
|
||||
rows={4}
|
||||
placeholder={t('contact.formMessage')}
|
||||
value={formState.message}
|
||||
onChange={e => setFormState({ ...formState, message: e.target.value })}
|
||||
className="w-full bg-white/10 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-slate-400 focus:outline-none focus:border-blue-500 transition-colors text-sm resize-none"
|
||||
/>
|
||||
{error && <p className="text-red-400 text-sm">{error}</p>}
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full py-3 bg-blue-600 hover:bg-blue-500 text-white font-semibold rounded-lg transition-colors"
|
||||
disabled={sending}
|
||||
className="w-full py-3 bg-blue-600 hover:bg-blue-500 disabled:opacity-60 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
{t('contact.formSubmit')}
|
||||
{sending ? t('contact.formSending') : t('contact.formSubmit')}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user