Consolidate Organization schema, add ItemList/Offer markup, expand FAQ

- Merge the duplicate Organization/LocalBusiness JSON-LD blocks (home's
  LocalBusiness + o-kompanii's Organization) into one shared
  getOrganizationSchema() call rendered on every page. Deliberately NOT
  wired through the root layout: fetches there get frozen into the
  static shell at build time (backend unreachable during image build)
  and never revalidate, silently serving stale data to crawlers - each
  page calls it directly instead, where revalidation is proven to work.
- Add optional ItemList schema on /uslugi and Offer/OfferCatalog schema
  on /aktsii; entity pages (/uslugi/[slug] Service, /blog/[slug]
  Article) reference the organization via @id instead of duplicating it.
- Add 8 more real FAQ entries (pricing model, contract terms,
  onboarding time, hybrid IT staff setup, supported stacks, backups,
  152-FZ compliance, escalation) and give every FAQ its own slug and
  dedicated /faq/[slug] page with its own title/H1/FAQPage schema,
  modeled on the one-topic-per-page pattern (h/t remontka.pro).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013otXCiTZsxdZ4SJKZ9wUky
This commit is contained in:
ogrechkoandClaude Sonnet 5 committed 2026-08-26 00:57:07 +03:00
1 parent f53c8b1ed7
commit 1e4f97030a
19 files changed
+271 -69

No files matched your search

@@ -0,0 +1,48 @@
-- Добавляет slug к FAQ (для отдельных страниц под каждый вопрос - по образцу
-- сайтов с базой знаний, где один вопрос/проблема = один адрес, а не только
-- пункт в общем аккордеоне) и добавляет более полный набор реальных вопросов
-- про условия работы, а не только про общий сервис.
-- Автоматически применяется при следующем запуске бэкенда.
BEGIN;
ALTER TABLE faqs ADD COLUMN IF NOT EXISTS slug TEXT;
CREATE UNIQUE INDEX IF NOT EXISTS idx_faqs_slug ON faqs(slug) WHERE slug IS NOT NULL;
UPDATE faqs SET slug = 'vremya-reakcii-na-zayavki' WHERE question = 'Как быстро вы реагируете на заявки?' AND slug IS NULL;
UPDATE faqs SET slug = 'udalennaya-podderzhka-po-rossii' WHERE question = 'Работаете ли вы полностью удалённо?' AND slug IS NULL;
UPDATE faqs SET slug = 'chto-vhodit-v-bazovyy-tarif' WHERE question = 'Что входит в базовый тариф IT-аутсорсинга?' AND slug IS NULL;
UPDATE faqs SET slug = 'kak-oplachivat-uslugi' WHERE question = 'Как происходит оплата услуг?' AND slug IS NULL;
UPDATE faqs SET slug = 'razovaya-usluga-bez-abonementa' WHERE question = 'Можно ли заказать разовую услугу без абонемента?' AND slug IS NULL;
UPDATE faqs SET slug = 'konfidencialnost-dannyh' WHERE question = 'Как обеспечивается конфиденциальность данных?' AND slug IS NULL;
INSERT INTO faqs (question, answer, slug, sort_order)
SELECT v.question, v.answer, v.slug, (SELECT COALESCE(MAX(sort_order), -1) + 1 FROM faqs) + v.rn - 1
FROM (VALUES
('Как рассчитывается стоимость услуг?',
'Стоимость абонентского обслуживания зависит от количества рабочих мест, графика поддержки (5/2 или 24/7) и состава инфраструктуры. Разовые работы (настройка сервера, аудит, разработка) оцениваются по объёму задачи после короткого созвона или Discovery-воркшопа. Точную сумму называем после короткого аудита - до этого момента ориентируемся на прайс-лист.',
'kak-rasschityvaetsya-stoimost', 1),
('Можно ли расторгнуть договор на обслуживание?',
'Да, договор на абонентское обслуживание можно расторгнуть, уведомив нас заранее (обычно за 30 дней) - жёсткой привязки на длительный срок нет, кроме случаев, когда клиент сам выбрал тариф с фиксированным сроком ради скидки (см. раздел «Акции»). Все условия фиксируются в договоре до начала работ.',
'kak-rastorgnut-dogovor', 2),
('Сколько занимает подключение к обслуживанию?',
'Обычно от 1 до 5 рабочих дней с момента подписания договора: сначала проводим аудит текущей инфраструктуры, получаем доступы, документируем текущее состояние - и только после этого переходим на плановое обслуживание. Для срочных случаев (например, уже произошёл инцидент) можем начать работу быстрее.',
'skolko-zanimaet-podklyuchenie', 3),
('Что если у нас уже есть свой системный администратор?',
'Работаем и в гибридном формате: берём на себя часть инфраструктуры (например, кибербезопасность или почтовые серверы), пока штатный специалист занимается остальным, либо выступаем вторым уровнем поддержки на случай отпуска или больничного. Разграничение зон ответственности фиксируется в договоре.',
'rabota-so-svoim-sisadminom', 4),
('С какими операционными системами и СУБД вы работаете?',
'Администрируем инфраструктуру на Linux и Windows Server, из СУБД - чаще всего PostgreSQL и MySQL, но беремся и за другие популярные СУБД по запросу. Перед стартом уточняем состав вашего стека на аудите.',
's-kakimi-sistemami-rabotaete', 5),
('Как обеспечивается резервное копирование данных?',
'Настраиваем регулярные автоматические бэкапы, изолированные от основной инфраструктуры (чтобы шифровальщик или сбой сервера не уничтожил и рабочие данные, и резервные копии одновременно), и периодически проверяем, что из бэкапа реально можно восстановиться - не только что он создаётся.',
'kak-obespechivaetsya-rezervnoe-kopirovanie', 6),
('Работаете ли вы с требованиями 152-ФЗ о персональных данных?',
'Да, при настройке инфраструктуры учитываем требования 152-ФЗ к обработке и хранению персональных данных: разграничение доступов, шифрование каналов, локализация данных на серверах в РФ при необходимости. Полный аудит на соответствие 152-ФЗ - по отдельному запросу.',
'rabota-s-152-fz', 7),
('Что происходит, если проблему не удаётся решить сразу?',
'Если проблема не решается на первом уровне поддержки, подключаем более опытного специалиста - это часть стандартного процесса, без дополнительной оплаты для клиентов на абонентском обслуживании. О ходе решения сложных инцидентов сообщаем в личном кабинете или напрямую.',
'eskalaciya-slozhnyh-problem', 8)
) AS v(question, answer, slug, rn)
WHERE NOT EXISTS (SELECT 1 FROM faqs WHERE slug = v.slug);
COMMIT;