Files
sag24-website/src/app/[lang]/uslugi/[slug]/page.tsx
striker 9f53641f16 seo: GA4, breadcrumbs JSON-LD, IndexNow post-deploy, llms.txt
- Add GA4 (G-C9J0D8FFH3) to root layout alongside Yandex.Metrika
- Add BreadcrumbList JSON-LD schema to all inner pages
- Add scripts/indexnow.mjs — submits 30 URLs to IndexNow + Yandex on deploy
- Add indexnow to postdeploy step in package.json
- Update llms.txt with all 8 services and new pages (about/clients/partners)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 04:04:50 +03:00

143 lines
6.1 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.
import type { Metadata } from 'next'
import Link from 'next/link'
import { notFound } from 'next/navigation'
import { getDictionary, LOCALES, type Locale } from '@/lib/i18n'
import { SERVICE_SLUGS } from '@/lib/services'
import { breadcrumbSchema } from '@/lib/breadcrumbs'
import { Server, Shield, Headphones, Camera, Network, HardDrive, Phone, Cloud, CheckCircle2, ChevronRight } from 'lucide-react'
const ICONS = [Server, Shield, Headphones, Camera, Network, HardDrive, Phone, Cloud]
export function generateStaticParams() {
return LOCALES.flatMap(lang =>
SERVICE_SLUGS.map(slug => ({ lang, slug }))
)
}
export async function generateMetadata({ params }: { params: Promise<{ lang: string; slug: string }> }): Promise<Metadata> {
const { lang, slug } = await params
const d = getDictionary(lang)
const svc = d.services.items.find(s => s.slug === slug)
if (!svc) return {}
const orgName = lang === 'ru' ? 'Сисадмингрупп' : 'SysadminGroup'
return {
title: svc.title,
description: svc.description,
alternates: {
canonical: `https://sag24.ru/${lang}/uslugi/${slug}/`,
languages: {
'ru': `https://sag24.ru/ru/uslugi/${slug}/`,
'en': `https://sag24.ru/en/uslugi/${slug}/`,
'x-default': `https://sag24.ru/ru/uslugi/${slug}/`,
},
},
openGraph: {
title: `${svc.title} | ${orgName}`,
description: svc.description,
url: `https://sag24.ru/${lang}/uslugi/${slug}/`,
siteName: orgName,
locale: lang === 'ru' ? 'ru_RU' : 'en_US',
images: [{ url: '/og-image.png' }],
},
}
}
export default async function ServicePage({ params }: { params: Promise<{ lang: string; slug: string }> }) {
const { lang: langStr, slug } = await params
const lang = langStr as Locale
const d = getDictionary(lang)
const idx = d.services.items.findIndex(s => s.slug === slug)
if (idx === -1) notFound()
const svc = d.services.items[idx]
const Icon = ICONS[idx]
return (
<div className="pt-16">
<section className="py-24 bg-gradient-to-br from-slate-900 via-blue-950 to-slate-900">
<div className="max-w-4xl mx-auto px-4 sm:px-6 text-center">
<div className="w-16 h-16 bg-blue-500/20 rounded-2xl flex items-center justify-center mx-auto mb-6">
<Icon size={32} className="text-blue-400" />
</div>
<h1 className="text-4xl sm:text-5xl font-bold text-white mb-6">{svc.title}</h1>
<p className="text-xl text-slate-300 max-w-2xl mx-auto mb-10">{svc.description}</p>
<Link href={`/${lang}/kontakty/`}
className="inline-flex items-center gap-2 px-8 py-4 bg-blue-600 hover:bg-blue-500 text-white font-semibold rounded-lg transition-colors">
{lang === 'ru' ? 'Обсудить проект' : 'Discuss project'}
<ChevronRight size={20} />
</Link>
</div>
</section>
<section className="py-16 bg-white">
<div className="max-w-4xl mx-auto px-4 sm:px-6">
<h2 className="text-2xl font-bold text-slate-900 mb-8">
{lang === 'ru' ? 'Что включено' : 'What is included'}
</h2>
<ul className="grid md:grid-cols-2 gap-4">
{svc.points.map((point, i) => (
<li key={i} className="flex items-start gap-3 p-4 bg-slate-50 rounded-lg border border-slate-100">
<CheckCircle2 size={20} className="text-blue-600 flex-shrink-0 mt-0.5" />
<span className="text-slate-700">{point}</span>
</li>
))}
</ul>
</div>
</section>
<section className="py-16 bg-slate-50 border-t border-slate-100">
<div className="max-w-4xl mx-auto px-4 sm:px-6 text-center">
<h2 className="text-2xl font-bold text-slate-900 mb-4">
{lang === 'ru' ? 'Нужна консультация?' : 'Need a consultation?'}
</h2>
<p className="text-slate-600 mb-8">
{lang === 'ru' ? 'Свяжитесь с нами — обсудим детали и рассчитаем стоимость.' : 'Contact us — we will discuss details and calculate the cost.'}
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<a href="tel:+74953637476" className="px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white font-semibold rounded-lg transition-colors">
+7 495 363-74-76
</a>
<Link href={`/${lang}/kontakty/`}
className="px-6 py-3 border border-slate-300 hover:border-blue-300 text-slate-700 font-semibold rounded-lg transition-colors">
{lang === 'ru' ? 'Написать нам' : 'Write to us'}
</Link>
</div>
</div>
</section>
<div className="max-w-4xl mx-auto px-4 sm:px-6 py-8">
<Link href={`/${lang}/uslugi/`} className="text-blue-600 hover:text-blue-800 text-sm font-medium">
{lang === 'ru' ? 'Все услуги' : 'All services'}
</Link>
</div>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema([
{ name: lang === 'ru' ? 'Главная' : 'Home', url: `https://sag24.ru/${lang}/` },
{ name: lang === 'ru' ? 'Услуги' : 'Services', url: `https://sag24.ru/${lang}/uslugi/` },
{ name: svc.title, url: `https://sag24.ru/${lang}/uslugi/${slug}/` },
])) }}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Service',
'name': svc.title,
'description': svc.description,
'url': `https://sag24.ru/${lang}/uslugi/${slug}/`,
'areaServed': lang === 'ru' ? 'Московская область' : 'Moscow Region',
'provider': {
'@type': 'Organization',
'name': lang === 'ru' ? 'Сисадмингрупп' : 'SysadminGroup',
'url': 'https://sag24.ru',
'telephone': '+7-495-363-74-76',
},
}),
}}
/>
</div>
)
}