feat: редизайн 404 страницы по образцу hhivp + обновить CLAUDE.md/README

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-16 04:40:22 +03:00
parent 7f2986489a
commit 73c189ebb5
3 changed files with 196 additions and 26 deletions

69
CLAUDE.md Normal file
View File

@@ -0,0 +1,69 @@
# CLAUDE.md — sag24-website
Инструкции для Claude при работе с этим проектом.
## Язык
Всегда отвечать на **русском**.
## Всё через репозиторий
Все изменения — только через git репозиторий, если явно не сказано иное. Никогда не копировать файлы напрямую на сервер (scp/rsync/etc).
## Деплой — ОБЯЗАТЕЛЬНО после каждого изменения
После любых изменений в коде — деплоить самостоятельно, без ожидания команды:
```bash
# 1. Локально: commit + push
git add <файлы> && git commit -m "..." && git push
# 2. На сервере: pull + build
ssh -i ~/.ssh/id_ed25519_hhivp striker@str-u-01.striker.su 'cat <<XEOF | sudo -S bash
Gh_lpx2018!
cd /opt/www/sag24.ru/repo && git pull && npm run deploy
XEOF'
```
- `.git` принадлежит root — `git pull` требует sudo
- `npm run deploy` = `next build` (→ `../public_html`) + `indexnow.mjs`
## Задачи Singularity
Проект: **САГ** `P-a41aa057-5401-4ae5-843c-439381fdd7f2`
При работе — создавать/обновлять/закрывать задачи в Singularity:
- Создавать задачу до или в процессе работы
- Закрывать (`complete: 1`) сразу после выполнения
- Добавлять заметку с деталями
## Стек
- Next.js 15, TypeScript, App Router, static export
- Tailwind CSS v3 — конфиг `tailwind.config.js` (НЕ `.ts`!)
- i18n через `[lang]` сегмент: `/ru/...`, `/en/...`
- Контент: `src/locales/ru.ts`, `src/locales/en.ts`
## Структура страниц
```
/ru/ → Главная
/ru/uslugi/ → Каталог + /[slug]/ (8 услуг)
/ru/about/ → О компании
/ru/clients/ → Клиенты
/ru/partners/ → Партнёры
/ru/faq/ → FAQ
/ru/kontakty/ → Контакты
```
## SEO
- BreadcrumbList JSON-LD на всех страницах — `src/lib/breadcrumbs.ts`
- generateMetadata на каждой странице с canonical + hreflang
- После деплоя IndexNow отправляется автоматически
## Известные ограничения
- Static export — API routes не работают; форма идёт на `/api/contact.php` (PHP)
- `trailingSlash: true` — все URL заканчиваются на `/`
- Turnstile SITE_KEY: `0x4AAAAAACrQS-dAb7E9RGPQ`

View File

@@ -1,3 +1,54 @@
# sag24-website
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> sag24.ru <EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Сайт sag24.ru — IT-аутсорсинг «Сисадмингрупп» (Пушкино, МО).
## Стек
- **Next.js 15** + TypeScript + App Router
- **Static export** (`output: 'export'`, `trailingSlash: true`)
- **Tailwind CSS v3** — конфиг: `tailwind.config.js` (не .ts!)
- **i18n**: `[lang]` сегмент (`/ru/...`, `/en/...`)
- **Шрифт**: Manrope (`next/font/google`)
## Страницы
```
/ru/ → Главная
/ru/uslugi/ → Каталог услуг
/ru/uslugi/[slug]/ → 8 страниц услуг
/ru/about/ → О компании
/ru/clients/ → Клиенты
/ru/partners/ → Партнёры
/ru/faq/ → FAQ
/ru/kontakty/ → Контакты
/en/... → Аналогично
```
## Деплой
```bash
# Локально:
git add . && git commit -m "..." && git push
# На сервере (str-u-01.striker.su):
ssh -i ~/.ssh/id_ed25519_hhivp striker@str-u-01.striker.su 'cat <<XEOF | sudo -S bash
Gh_lpx2018!
cd /opt/www/sag24.ru/repo && git pull && npm run deploy
XEOF'
```
`npm run deploy` = `BUILD_DIR=../public_html next build && node scripts/indexnow.mjs`
После деплоя автоматически отправляется уведомление в IndexNow (Yandex + api.indexnow.org).
## Локали
`src/locales/ru.ts` и `en.ts` — весь текстовый контент.
## SEO
- GA4: G-C9J0D8FFH3
- Yandex.Metrika: 97525679
- IndexNow key: 40c65b722891b81d944f2c3fea6cab95
- BreadcrumbList JSON-LD на всех страницах (`src/lib/breadcrumbs.ts`)
- sitemap.xml: 36 URLs

View File

@@ -1,32 +1,82 @@
import type { Metadata } from 'next'
import Link from 'next/link'
'use client'
export const metadata: Metadata = {
title: 'Страница не найдена — Сисадмингрупп',
description: 'Запрошенная страница не существует.',
robots: { index: false },
}
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { Home, Wrench, Mail, ArrowLeft } from 'lucide-react'
export default function NotFound() {
const pathname = usePathname()
const lang = pathname?.startsWith('/en') ? 'en' : 'ru'
const t = lang === 'en' ? {
subtitle: 'Page Not Found',
description: 'The page you are looking for does not exist or has been moved.',
home: 'Go Home',
services: 'Our Services',
contact: 'Contact Us',
back: 'Go Back',
} : {
subtitle: 'Страница не найдена',
description: 'Запрошенная страница не существует или была перемещена.',
home: 'На главную',
services: 'Услуги',
contact: 'Контакты',
back: 'Назад',
}
return (
<html lang="ru">
<body style={{ margin: 0, fontFamily: 'sans-serif', background: '#f8fafc' }}>
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '2rem' }}>
<div style={{ textAlign: 'center', maxWidth: '480px' }}>
<div style={{ fontSize: '6rem', fontWeight: 700, color: '#2563eb', lineHeight: 1 }}>404</div>
<h1 style={{ fontSize: '1.5rem', fontWeight: 700, color: '#0f172a', margin: '1rem 0 0.5rem' }}>
Страница не найдена
</h1>
<p style={{ color: '#64748b', marginBottom: '2rem' }}>
Запрошенная страница не существует или была перемещена.
</p>
<Link href="/ru/"
style={{ display: 'inline-block', padding: '0.75rem 2rem', background: '#2563eb', color: '#fff', borderRadius: '0.5rem', textDecoration: 'none', fontWeight: 600 }}>
На главную
</Link>
</div>
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-blue-950 to-slate-900 flex items-center justify-center px-4 relative overflow-hidden">
{/* Grid pattern */}
<div className="absolute inset-0 opacity-5"
style={{ backgroundImage: 'linear-gradient(#fff 0.5px, transparent 0.5px), linear-gradient(90deg, #fff 0.5px, transparent 0.5px)', backgroundSize: '4rem 4rem' }} />
{/* Glow orbs */}
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-blue-500/10 rounded-full blur-3xl" />
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-blue-700/10 rounded-full blur-3xl" />
<div className="relative z-10 text-center max-w-2xl mx-auto">
{/* 404 */}
<div className="text-[10rem] sm:text-[14rem] font-bold leading-none select-none"
style={{
background: 'linear-gradient(135deg, #60a5fa, #3b82f6, #60a5fa)',
backgroundSize: '200% auto',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
backgroundClip: 'text',
}}>
404
</div>
</body>
</html>
<h1 className="text-2xl sm:text-3xl font-bold text-white mt-2 mb-4">{t.subtitle}</h1>
<p className="text-slate-400 text-lg mb-10">{t.description}</p>
{/* Buttons */}
<div className="flex flex-wrap justify-center gap-3 mb-8">
<Link href={`/${lang}/`}
className="inline-flex items-center gap-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white font-semibold rounded-lg transition-colors">
<Home size={18} />
{t.home}
</Link>
<Link href={`/${lang}/uslugi/`}
className="inline-flex items-center gap-2 px-6 py-3 bg-slate-800 hover:bg-slate-700 text-white font-semibold rounded-lg border border-slate-700 transition-colors">
<Wrench size={18} />
{t.services}
</Link>
<Link href={`/${lang}/kontakty/`}
className="inline-flex items-center gap-2 px-6 py-3 bg-slate-800 hover:bg-slate-700 text-white font-semibold rounded-lg border border-slate-700 transition-colors">
<Mail size={18} />
{t.contact}
</Link>
</div>
{/* Back */}
<button
onClick={() => window.history.back()}
className="inline-flex items-center gap-2 text-slate-400 hover:text-blue-400 transition-colors text-sm">
<ArrowLeft size={16} />
{t.back}
</button>
</div>
</div>
)
}