Browse Source
- Add BlockSnapshot Prisma model (html, css, version, changelog) + migration - Add API endpoints: POST/GET /blocks/snapshots, GET /blocks/snapshots/:id - BlockMetaBar: version dropdown, HTML capture on save, onSnapshotSelect prop - "Сохранить версию" now captures innerHTML snapshot + CSS and stores in DB - Selecting archived version shows stored HTML snapshot instead of live component - Yellow banner "Архивная версия" with link to return to current - Split all 8 block pages into Server Component (metadata) + Client Component - Add data-block-capture attribute for snapshot capture targeting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>main
21 changed files with 1962 additions and 1554 deletions
@ -0,0 +1,18 @@
|
||||
-- CreateTable |
||||
CREATE TABLE "BlockSnapshot" ( |
||||
"id" TEXT NOT NULL, |
||||
"blockPath" TEXT NOT NULL, |
||||
"version" TEXT NOT NULL, |
||||
"changelog" JSONB NOT NULL DEFAULT '[]', |
||||
"html" TEXT NOT NULL, |
||||
"css" TEXT NOT NULL, |
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, |
||||
|
||||
CONSTRAINT "BlockSnapshot_pkey" PRIMARY KEY ("id") |
||||
); |
||||
|
||||
-- CreateIndex |
||||
CREATE INDEX "BlockSnapshot_blockPath_idx" ON "BlockSnapshot"("blockPath"); |
||||
|
||||
-- CreateIndex |
||||
CREATE UNIQUE INDEX "BlockSnapshot_blockPath_version_key" ON "BlockSnapshot"("blockPath", "version"); |
||||
@ -0,0 +1,217 @@
|
||||
"use client"; |
||||
|
||||
import { useState } from "react"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { BlockMetaBar, type SnapshotData } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import { CeoBlock, CEO_QUESTIONS } from "@/components/blocks/CeoBlock"; |
||||
|
||||
const BLOCK_PATH = "/blocks/ceo"; |
||||
|
||||
const LLM_CEO_TEXT = ` |
||||
БЛОК: Вводный текст клиники (CEO-блок) |
||||
Источник: perm.oclinica.ru/lor — секция после баннера |
||||
Версия: v1.1 |
||||
|
||||
НАЗНАЧЕНИЕ: |
||||
Информационный блок под hero-баннером. Рассказывает о специализации клиники |
||||
и вовлекает пациента через вопросы-стимулы. |
||||
|
||||
СТРУКТУРА: |
||||
1. Вводный абзац |
||||
«Клиника ухо, нос специализируется на оториноларингологии – лечении взрослых и детей |
||||
с ЛОР заболеваниями. ЛОР клиника представлена на двух адресах: ул. Цитная, 9, ул. Г. Звезда, 31а...» |
||||
|
||||
2. Вопросы-стимулы (формат: «— Вопрос?») |
||||
Каждый вопрос — отдельный абзац с тире, связанный с симптомами пациентов: |
||||
— У вас болит ухо, заложен нос, першит в горле, и вы не можете понять причину? |
||||
— Вам срочно нужен платный ЛОР в Перми или, как ещё говорят, «ухогорлонос»? |
||||
— Заболел ребёнок? |
||||
— Срочно ищете частные ЛОР-клиники Перми для детей 0+ и взрослых... |
||||
— Вам назначили проведение эндоскопической операции на ухе, горле или носе? |
||||
|
||||
3. Заключительный абзац |
||||
«Обращайтесь в ЛОР центр ухо, горло, нос в Перми...» |
||||
«Будьте здоровы!» |
||||
|
||||
ТИПОГРАФИКА: |
||||
Шрифт: Fira Sans, 14px, line-height 1.6–1.8 |
||||
Цвет текста: #374151 (#bb-text) |
||||
Вопросы: тот же стиль, начинаются с «—» |
||||
Ключевые слова в тексте — обычно ссылки синего цвета (#0089c3) |
||||
Фон блока: #ffffff, отступы 40–60px сверху и снизу |
||||
|
||||
ПРАВИЛА: |
||||
✓ Вопросы начинаются с «—» (тире) |
||||
✓ Ключевые медицинские термины — ссылки #0089c3 |
||||
✓ Текст без H2 заголовка — просто связный параграф |
||||
✕ Не добавлять маркированные списки (только тире) |
||||
✕ Не менять стиль вопросов на другой формат |
||||
`.trim();
|
||||
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Адрес: «ул. Цитная, 9» заменён на «ул. Клары Цеткин, 9»", |
||||
"Цвет ссылок: #52b4bd (было #0089c3)", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function CeoPageClient() { |
||||
const [snapshot, setSnapshot] = useState<SnapshotData | null>(null); |
||||
|
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Вводный текст (CEO-блок) |
||||
</h1> |
||||
<BlockMetaBar |
||||
path={BLOCK_PATH} |
||||
defaultVersion="v1.1" |
||||
defaultIsInPreview={false} |
||||
defaultChangelog={CHANGELOG} |
||||
onSnapshotSelect={setSnapshot} |
||||
/> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Блок после hero-баннера на perm.oclinica.ru/lor. Описание специализации клиники |
||||
+ вопросы-стимулы для пациентов. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
{snapshot ? ( |
||||
<div className="rounded-xl overflow-hidden" style={{ border: "1px solid var(--bb-border)" }}> |
||||
<style dangerouslySetInnerHTML={{ __html: snapshot.css }} /> |
||||
<div dangerouslySetInnerHTML={{ __html: snapshot.html }} /> |
||||
</div> |
||||
) : ( |
||||
<div data-block-capture={BLOCK_PATH} className="rounded-xl overflow-hidden" style={{ border: "1px solid var(--bb-border)" }}> |
||||
<CeoBlock /> |
||||
</div> |
||||
)} |
||||
</section> |
||||
|
||||
{/* Анатомия */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Структура блока |
||||
</h2> |
||||
<div className="space-y-3"> |
||||
{[ |
||||
{ |
||||
num: "1", |
||||
title: "Вводный абзац", |
||||
bg: "#f0f9ff", |
||||
desc: 'Специализация клиники, адреса. Ключевые слова — ссылки синего цвета (#0089c3).', |
||||
}, |
||||
{ |
||||
num: "2", |
||||
title: "Вопросы-стимулы", |
||||
bg: "#fefce8", |
||||
desc: 'Каждый вопрос — отдельный абзац, начинается с «—». Адресован пациенту с конкретным симптомом.', |
||||
}, |
||||
{ |
||||
num: "3", |
||||
title: "Заключительный абзац", |
||||
bg: "#f0fdf4", |
||||
desc: 'Призыв обращаться. «Будьте здоровы!» — фирменная подпись клиники.', |
||||
}, |
||||
].map((s) => ( |
||||
<div |
||||
key={s.num} |
||||
className="flex gap-4 p-4 rounded-xl" |
||||
style={{ background: s.bg, border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<div |
||||
className="w-7 h-7 rounded-full flex items-center justify-center font-bold text-sm shrink-0" |
||||
style={{ background: "var(--brand-053m)", color: "#fff" }} |
||||
> |
||||
{s.num} |
||||
</div> |
||||
<div> |
||||
<p className="font-semibold text-sm mb-1" style={{ color: "var(--bb-text)" }}> |
||||
{s.title} |
||||
</p> |
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> |
||||
{s.desc} |
||||
</p> |
||||
</div> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Вопросы-стимулы */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Вопросы-стимулы (5 штук) |
||||
</h2> |
||||
<div className="space-y-2"> |
||||
{CEO_QUESTIONS.map((q, i) => ( |
||||
<div |
||||
key={i} |
||||
className="flex items-start gap-3 p-3 rounded-lg text-sm" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<span className="font-bold shrink-0 mt-0.5" style={{ color: "var(--brand-053m)" }}> |
||||
— |
||||
</span> |
||||
<span style={{ color: "var(--bb-text)" }}>{q}</span> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/ceo" version="v1.1" specText={LLM_CEO_TEXT}> |
||||
<LlmSection title="Структура блока" /> |
||||
<LlmTable |
||||
headers={["Часть", "Содержимое", "Примечание"]} |
||||
rows={[ |
||||
["Вводный абзац", "Специализация клиники, адреса", "Ключевые слова = ссылки #0089c3"], |
||||
["Вопросы-стимулы", "5 вопросов от лица пациента, начинаются с «—»", "Без маркированных списков"], |
||||
["Заключение", 'Призыв обращаться + «Будьте здоровы!»', "Фирменная подпись клиники"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Типографика" /> |
||||
<LlmTable |
||||
headers={["Параметр", "Значение"]} |
||||
rows={[ |
||||
["Шрифт", "Fira Sans"], |
||||
["Размер", "14px"], |
||||
["Line-height", "1.6–1.8"], |
||||
["Цвет текста", "#374151 (--bb-text)"], |
||||
["Цвет ссылок в тексте", "#0089c3 (--brand-053m)"], |
||||
["Фон блока", "#ffffff"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "Вопросы начинаются с «—» (длинное тире)" }, |
||||
{ ok: true, text: "Ключевые медицинские термины — ссылки #0089c3" }, |
||||
{ ok: true, text: "Фирменная подпись «Будьте здоровы!» в конце" }, |
||||
{ ok: false, text: "Не добавлять маркированные списки (•)" }, |
||||
{ ok: false, text: "Не добавлять H2 заголовок внутри блока" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
} |
||||
@ -1,202 +1,10 @@
|
||||
import type { Metadata } from "next"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { BlockMetaBar } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import { CeoBlock, CEO_QUESTIONS } from "@/components/blocks/CeoBlock"; |
||||
import CeoPageClient from "./CeoPageClient"; |
||||
|
||||
export const metadata: Metadata = { |
||||
title: "Вводный текст (CEO-блок). Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой", |
||||
}; |
||||
|
||||
const LLM_CEO_TEXT = ` |
||||
БЛОК: Вводный текст клиники (CEO-блок) |
||||
Источник: perm.oclinica.ru/lor — секция после баннера |
||||
Версия: v1.1 |
||||
|
||||
НАЗНАЧЕНИЕ: |
||||
Информационный блок под hero-баннером. Рассказывает о специализации клиники |
||||
и вовлекает пациента через вопросы-стимулы. |
||||
|
||||
СТРУКТУРА: |
||||
1. Вводный абзац |
||||
«Клиника ухо, нос специализируется на оториноларингологии – лечении взрослых и детей |
||||
с ЛОР заболеваниями. ЛОР клиника представлена на двух адресах: ул. Цитная, 9, ул. Г. Звезда, 31а...» |
||||
|
||||
2. Вопросы-стимулы (формат: «— Вопрос?») |
||||
Каждый вопрос — отдельный абзац с тире, связанный с симптомами пациентов: |
||||
— У вас болит ухо, заложен нос, першит в горле, и вы не можете понять причину? |
||||
— Вам срочно нужен платный ЛОР в Перми или, как ещё говорят, «ухогорлонос»? |
||||
— Заболел ребёнок? |
||||
— Срочно ищете частные ЛОР-клиники Перми для детей 0+ и взрослых... |
||||
— Вам назначили проведение эндоскопической операции на ухе, горле или носе? |
||||
|
||||
3. Заключительный абзац |
||||
«Обращайтесь в ЛОР центр ухо, горло, нос в Перми...» |
||||
«Будьте здоровы!» |
||||
|
||||
ТИПОГРАФИКА: |
||||
Шрифт: Fira Sans, 14px, line-height 1.6–1.8 |
||||
Цвет текста: #374151 (#bb-text) |
||||
Вопросы: тот же стиль, начинаются с «—» |
||||
Ключевые слова в тексте — обычно ссылки синего цвета (#0089c3) |
||||
Фон блока: #ffffff, отступы 40–60px сверху и снизу |
||||
|
||||
ПРАВИЛА: |
||||
✓ Вопросы начинаются с «—» (тире) |
||||
✓ Ключевые медицинские термины — ссылки #0089c3 |
||||
✓ Текст без H2 заголовка — просто связный параграф |
||||
✕ Не добавлять маркированные списки (только тире) |
||||
✕ Не менять стиль вопросов на другой формат |
||||
`.trim();
|
||||
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Адрес: «ул. Цитная, 9» заменён на «ул. Клары Цеткин, 9»", |
||||
"Цвет ссылок: #52b4bd (было #0089c3)", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function CeoPage() { |
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Вводный текст (CEO-блок) |
||||
</h1> |
||||
<BlockMetaBar path="/blocks/ceo" defaultVersion="v1.1" defaultIsInPreview={false} defaultChangelog={CHANGELOG} /> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Блок после hero-баннера на perm.oclinica.ru/lor. Описание специализации клиники |
||||
+ вопросы-стимулы для пациентов. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
<div className="rounded-xl overflow-hidden" style={{ border: "1px solid var(--bb-border)" }}> |
||||
<CeoBlock /> |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Анатомия */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Структура блока |
||||
</h2> |
||||
<div className="space-y-3"> |
||||
{[ |
||||
{ |
||||
num: "1", |
||||
title: "Вводный абзац", |
||||
bg: "#f0f9ff", |
||||
desc: 'Специализация клиники, адреса. Ключевые слова — ссылки синего цвета (#0089c3).', |
||||
}, |
||||
{ |
||||
num: "2", |
||||
title: "Вопросы-стимулы", |
||||
bg: "#fefce8", |
||||
desc: 'Каждый вопрос — отдельный абзац, начинается с «—». Адресован пациенту с конкретным симптомом.', |
||||
}, |
||||
{ |
||||
num: "3", |
||||
title: "Заключительный абзац", |
||||
bg: "#f0fdf4", |
||||
desc: 'Призыв обращаться. «Будьте здоровы!» — фирменная подпись клиники.', |
||||
}, |
||||
].map((s) => ( |
||||
<div |
||||
key={s.num} |
||||
className="flex gap-4 p-4 rounded-xl" |
||||
style={{ background: s.bg, border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<div |
||||
className="w-7 h-7 rounded-full flex items-center justify-center font-bold text-sm shrink-0" |
||||
style={{ background: "var(--brand-053m)", color: "#fff" }} |
||||
> |
||||
{s.num} |
||||
</div> |
||||
<div> |
||||
<p className="font-semibold text-sm mb-1" style={{ color: "var(--bb-text)" }}> |
||||
{s.title} |
||||
</p> |
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> |
||||
{s.desc} |
||||
</p> |
||||
</div> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Вопросы-стимулы */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Вопросы-стимулы (5 штук) |
||||
</h2> |
||||
<div className="space-y-2"> |
||||
{CEO_QUESTIONS.map((q, i) => ( |
||||
<div |
||||
key={i} |
||||
className="flex items-start gap-3 p-3 rounded-lg text-sm" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<span className="font-bold shrink-0 mt-0.5" style={{ color: "var(--brand-053m)" }}> |
||||
— |
||||
</span> |
||||
<span style={{ color: "var(--bb-text)" }}>{q}</span> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/ceo" version="v1.1" specText={LLM_CEO_TEXT}> |
||||
<LlmSection title="Структура блока" /> |
||||
<LlmTable |
||||
headers={["Часть", "Содержимое", "Примечание"]} |
||||
rows={[ |
||||
["Вводный абзац", "Специализация клиники, адреса", "Ключевые слова = ссылки #0089c3"], |
||||
["Вопросы-стимулы", "5 вопросов от лица пациента, начинаются с «—»", "Без маркированных списков"], |
||||
["Заключение", 'Призыв обращаться + «Будьте здоровы!»', "Фирменная подпись клиники"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Типографика" /> |
||||
<LlmTable |
||||
headers={["Параметр", "Значение"]} |
||||
rows={[ |
||||
["Шрифт", "Fira Sans"], |
||||
["Размер", "14px"], |
||||
["Line-height", "1.6–1.8"], |
||||
["Цвет текста", "#374151 (--bb-text)"], |
||||
["Цвет ссылок в тексте", "#0089c3 (--brand-053m)"], |
||||
["Фон блока", "#ffffff"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "Вопросы начинаются с «—» (длинное тире)" }, |
||||
{ ok: true, text: "Ключевые медицинские термины — ссылки #0089c3" }, |
||||
{ ok: true, text: "Фирменная подпись «Будьте здоровы!» в конце" }, |
||||
{ ok: false, text: "Не добавлять маркированные списки (•)" }, |
||||
{ ok: false, text: "Не добавлять H2 заголовок внутри блока" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
return <CeoPageClient />; |
||||
} |
||||
|
||||
@ -0,0 +1,190 @@
|
||||
"use client"; |
||||
|
||||
import { useState } from "react"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { BlockMetaBar, type SnapshotData } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import { ContactFormsBlock } from "@/components/blocks/ContactFormsBlock"; |
||||
|
||||
const BLOCK_PATH = "/blocks/contact-forms"; |
||||
|
||||
const LLM_FORMS_TEXT = ` |
||||
БЛОК: Формы записи и обратной связи |
||||
Источник: perm.oclinica.ru/lor — два блока форм на одной странице |
||||
Версия: v1.2 |
||||
|
||||
ФОРМА 1: «Будьте здоровы!» (форма записи на приём) |
||||
Позиция: после блока отзывов |
||||
Заголовок H2: «Будьте здоровы!» — 36px, bold, #000000, line-height 38px |
||||
Подзаголовок: «Запишитесь на приём к врачу!» |
||||
Фон секции: #d4f6f8 (светло-бирюзовый) |
||||
Поля формы: |
||||
1. Текстовый input: placeholder «Введите ваше имя» (height 50px) |
||||
2. Телефонный input: placeholder «Введите ваш телефон» (height 50px) |
||||
3. Select: «Выберите ЛОР врача» (height 50px) |
||||
4. Checkbox: «Отправляя данные, я даю согласие на обработку персональных данных» |
||||
Кнопка: «Запишите меня!» — стиль bb-btn-primary (#FFA39C) |
||||
Ширина формы: ~400px центрирована или в колонку |
||||
|
||||
ФОРМА 2: «Узнайте стоимость операции» (консультация) |
||||
Позиция: после блока новостей |
||||
Заголовок H2: «Узнайте стоимость операции» — 36px, bold, #000000, line-height 38px |
||||
Подзаголовок: «Проконсультируйтесь с ассистентом хирурга» |
||||
Фон секции: #d4f6f8 (тот же что и форма 1) |
||||
Поля формы: |
||||
1. Текстовый input: placeholder «каша» / «Введите ваше имя» (height 50px) |
||||
2. Телефонный input: placeholder «Введите ваш телефон» (height 50px) |
||||
3. Checkbox: «Отправляя данные, я даю согласие на обработку персональных данных» |
||||
Кнопка: «Перезвоните мне» — стиль bb-btn-primary (#FFA39C) |
||||
|
||||
ОБЩИЕ ПРАВИЛА: |
||||
— Оба поля input/select: bb-input / bb-select (height 50px, border 1px solid #ccc) |
||||
— Чекбокс обязателен в обеих формах |
||||
— Кнопка отправки: всегда bb-btn-primary (#FFA39C) |
||||
— Обе формы на бирюзовом фоне (#d4f6f8) |
||||
|
||||
ПРАВИЛА: |
||||
✓ Чекбокс согласия обязателен в каждой форме |
||||
✓ Кнопки отправки: bb-btn-primary (#FFA39C) |
||||
✓ Обе формы на фоне #d4f6f8 |
||||
✕ Не менять порядок полей |
||||
✕ Не убирать чекбокс согласия |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.2", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Кнопка: bb-btn-md заменена на bb-btn-lg (18px, bold, как на реальном сайте)", |
||||
"border-radius кнопки: 7px исправлен на 4px (исправлено в globals.css)", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"H2: размер на 36px, цвет на #000000, line-height 38px", |
||||
"Фон формы 1: с #b8e6ed на #d4f6f8", |
||||
"Фон формы 2: с #ffffff на #d4f6f8 (обе формы на одном фоне)", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function ContactFormsPageClient() { |
||||
const [snapshot, setSnapshot] = useState<SnapshotData | null>(null); |
||||
|
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Формы записи |
||||
</h1> |
||||
<BlockMetaBar |
||||
path={BLOCK_PATH} |
||||
defaultVersion="v1.2" |
||||
defaultIsInPreview={false} |
||||
defaultChangelog={CHANGELOG} |
||||
onSnapshotSelect={setSnapshot} |
||||
/> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Два блока форм с perm.oclinica.ru/lor — запись на приём и запрос стоимости операции. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
{snapshot ? ( |
||||
<> |
||||
<style dangerouslySetInnerHTML={{ __html: snapshot.css }} /> |
||||
<div dangerouslySetInnerHTML={{ __html: snapshot.html }} /> |
||||
</> |
||||
) : ( |
||||
<div data-block-capture={BLOCK_PATH}> |
||||
<ContactFormsBlock /> |
||||
</div> |
||||
)} |
||||
</section> |
||||
|
||||
{/* Сравнение двух форм */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Сравнение двух форм |
||||
</h2> |
||||
<div className="overflow-x-auto rounded-xl" style={{ border: "1px solid var(--bb-border)" }}> |
||||
<table className="w-full text-sm border-collapse"> |
||||
<thead> |
||||
<tr style={{ background: "var(--bb-sidebar-bg)" }}> |
||||
<th className="text-left px-4 py-3 text-xs font-semibold" style={{ color: "var(--bb-text-muted)", borderBottom: "1px solid var(--bb-border)" }}>Параметр</th> |
||||
<th className="text-left px-4 py-3 text-xs font-semibold" style={{ color: "var(--bb-text-muted)", borderBottom: "1px solid var(--bb-border)" }}>Форма 1 «Будьте здоровы!»</th> |
||||
<th className="text-left px-4 py-3 text-xs font-semibold" style={{ color: "var(--bb-text-muted)", borderBottom: "1px solid var(--bb-border)" }}>Форма 2 «Стоимость операции»</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
{[ |
||||
["Заголовок", "«Будьте здоровы!»", "«Узнайте стоимость операции»"], |
||||
["Подзаголовок", "«Запишитесь на приём к врачу!»", "«Проконсультируйтесь с ассистентом хирурга»"], |
||||
["Фон секции", "#d4f6f8", "#d4f6f8"], |
||||
["Поля", "Имя + Телефон + Select врача + Checkbox", "Имя + Телефон + Checkbox"], |
||||
["Кнопка", "«Запишите меня!»", "«Перезвоните мне»"], |
||||
["Стиль кнопки", "bb-btn-primary (#FFA39C)", "bb-btn-primary (#FFA39C)"], |
||||
].map(([param, f1, f2]) => ( |
||||
<tr key={param} style={{ borderTop: "1px solid var(--bb-border)" }}> |
||||
<td className="px-4 py-2 font-medium text-xs" style={{ color: "var(--bb-text)" }}>{param}</td> |
||||
<td className="px-4 py-2 text-xs" style={{ color: "var(--bb-text-muted)" }}>{f1}</td> |
||||
<td className="px-4 py-2 text-xs" style={{ color: "var(--bb-text-muted)" }}>{f2}</td> |
||||
</tr> |
||||
))} |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/contact-forms" version="v1.2" specText={LLM_FORMS_TEXT}> |
||||
<LlmSection title="Форма 1 — Запись на приём" /> |
||||
<LlmTable |
||||
headers={["Поле", "Тип", "Placeholder"]} |
||||
rows={[ |
||||
["Имя", "input[type=text]", "«Введите ваше имя»"], |
||||
["Телефон", "input[type=tel]", "«Введите ваш телефон»"], |
||||
["Врач", "select", "«Выберите ЛОР врача»"], |
||||
["Согласие", "checkbox", "«Отправляя данные, я даю согласие...»"], |
||||
["Кнопка", "button bb-btn-primary", "«Запишите меня!»"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Форма 2 — Запрос стоимости" /> |
||||
<LlmTable |
||||
headers={["Поле", "Тип", "Placeholder"]} |
||||
rows={[ |
||||
["Имя", "input[type=text]", "«Введите ваше имя»"], |
||||
["Телефон", "input[type=tel]", "«Введите ваш телефон»"], |
||||
["Согласие", "checkbox", "«Отправляя данные, я даю согласие...»"], |
||||
["Кнопка", "button bb-btn-primary", "«Перезвоните мне»"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "Обе формы: фон #d4f6f8" }, |
||||
{ ok: true, text: "Чекбокс согласия обязателен в каждой форме" }, |
||||
{ ok: true, text: "Кнопки: bb-btn-primary (#FFA39C)" }, |
||||
{ ok: true, text: "Все input/select: height 50px (bb-input, bb-select)" }, |
||||
{ ok: false, text: "Не убирать чекбокс согласия на обработку данных" }, |
||||
{ ok: false, text: "Не менять стиль кнопки на outline или teal" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
} |
||||
@ -1,173 +1,10 @@
|
||||
import type { Metadata } from "next"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { BlockMetaBar } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import { ContactFormsBlock } from "@/components/blocks/ContactFormsBlock"; |
||||
import ContactFormsPageClient from "./ContactFormsPageClient"; |
||||
|
||||
export const metadata: Metadata = { |
||||
title: "Формы записи. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой", |
||||
}; |
||||
|
||||
const LLM_FORMS_TEXT = ` |
||||
БЛОК: Формы записи и обратной связи |
||||
Источник: perm.oclinica.ru/lor — два блока форм на одной странице |
||||
Версия: v1.2 |
||||
|
||||
ФОРМА 1: «Будьте здоровы!» (форма записи на приём) |
||||
Позиция: после блока отзывов |
||||
Заголовок H2: «Будьте здоровы!» — 36px, bold, #000000, line-height 38px |
||||
Подзаголовок: «Запишитесь на приём к врачу!» |
||||
Фон секции: #d4f6f8 (светло-бирюзовый) |
||||
Поля формы: |
||||
1. Текстовый input: placeholder «Введите ваше имя» (height 50px) |
||||
2. Телефонный input: placeholder «Введите ваш телефон» (height 50px) |
||||
3. Select: «Выберите ЛОР врача» (height 50px) |
||||
4. Checkbox: «Отправляя данные, я даю согласие на обработку персональных данных» |
||||
Кнопка: «Запишите меня!» — стиль bb-btn-primary (#FFA39C) |
||||
Ширина формы: ~400px центрирована или в колонку |
||||
|
||||
ФОРМА 2: «Узнайте стоимость операции» (консультация) |
||||
Позиция: после блока новостей |
||||
Заголовок H2: «Узнайте стоимость операции» — 36px, bold, #000000, line-height 38px |
||||
Подзаголовок: «Проконсультируйтесь с ассистентом хирурга» |
||||
Фон секции: #d4f6f8 (тот же что и форма 1) |
||||
Поля формы: |
||||
1. Текстовый input: placeholder «каша» / «Введите ваше имя» (height 50px) |
||||
2. Телефонный input: placeholder «Введите ваш телефон» (height 50px) |
||||
3. Checkbox: «Отправляя данные, я даю согласие на обработку персональных данных» |
||||
Кнопка: «Перезвоните мне» — стиль bb-btn-primary (#FFA39C) |
||||
|
||||
ОБЩИЕ ПРАВИЛА: |
||||
— Оба поля input/select: bb-input / bb-select (height 50px, border 1px solid #ccc) |
||||
— Чекбокс обязателен в обеих формах |
||||
— Кнопка отправки: всегда bb-btn-primary (#FFA39C) |
||||
— Обе формы на бирюзовом фоне (#d4f6f8) |
||||
|
||||
ПРАВИЛА: |
||||
✓ Чекбокс согласия обязателен в каждой форме |
||||
✓ Кнопки отправки: bb-btn-primary (#FFA39C) |
||||
✓ Обе формы на фоне #d4f6f8 |
||||
✕ Не менять порядок полей |
||||
✕ Не убирать чекбокс согласия |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.2", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Кнопка: bb-btn-md заменена на bb-btn-lg (18px, bold, как на реальном сайте)", |
||||
"border-radius кнопки: 7px исправлен на 4px (исправлено в globals.css)", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"H2: размер на 36px, цвет на #000000, line-height 38px", |
||||
"Фон формы 1: с #b8e6ed на #d4f6f8", |
||||
"Фон формы 2: с #ffffff на #d4f6f8 (обе формы на одном фоне)", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function ContactFormsPage() { |
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Формы записи |
||||
</h1> |
||||
<BlockMetaBar path="/blocks/contact-forms" defaultVersion="v1.2" defaultIsInPreview={false} defaultChangelog={CHANGELOG} /> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Два блока форм с perm.oclinica.ru/lor — запись на приём и запрос стоимости операции. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
<ContactFormsBlock /> |
||||
</section> |
||||
|
||||
{/* Сравнение двух форм */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Сравнение двух форм |
||||
</h2> |
||||
<div className="overflow-x-auto rounded-xl" style={{ border: "1px solid var(--bb-border)" }}> |
||||
<table className="w-full text-sm border-collapse"> |
||||
<thead> |
||||
<tr style={{ background: "var(--bb-sidebar-bg)" }}> |
||||
<th className="text-left px-4 py-3 text-xs font-semibold" style={{ color: "var(--bb-text-muted)", borderBottom: "1px solid var(--bb-border)" }}>Параметр</th> |
||||
<th className="text-left px-4 py-3 text-xs font-semibold" style={{ color: "var(--bb-text-muted)", borderBottom: "1px solid var(--bb-border)" }}>Форма 1 «Будьте здоровы!»</th> |
||||
<th className="text-left px-4 py-3 text-xs font-semibold" style={{ color: "var(--bb-text-muted)", borderBottom: "1px solid var(--bb-border)" }}>Форма 2 «Стоимость операции»</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
{[ |
||||
["Заголовок", "«Будьте здоровы!»", "«Узнайте стоимость операции»"], |
||||
["Подзаголовок", "«Запишитесь на приём к врачу!»", "«Проконсультируйтесь с ассистентом хирурга»"], |
||||
["Фон секции", "#d4f6f8", "#d4f6f8"], |
||||
["Поля", "Имя + Телефон + Select врача + Checkbox", "Имя + Телефон + Checkbox"], |
||||
["Кнопка", "«Запишите меня!»", "«Перезвоните мне»"], |
||||
["Стиль кнопки", "bb-btn-primary (#FFA39C)", "bb-btn-primary (#FFA39C)"], |
||||
].map(([param, f1, f2]) => ( |
||||
<tr key={param} style={{ borderTop: "1px solid var(--bb-border)" }}> |
||||
<td className="px-4 py-2 font-medium text-xs" style={{ color: "var(--bb-text)" }}>{param}</td> |
||||
<td className="px-4 py-2 text-xs" style={{ color: "var(--bb-text-muted)" }}>{f1}</td> |
||||
<td className="px-4 py-2 text-xs" style={{ color: "var(--bb-text-muted)" }}>{f2}</td> |
||||
</tr> |
||||
))} |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/contact-forms" version="v1.2" specText={LLM_FORMS_TEXT}> |
||||
<LlmSection title="Форма 1 — Запись на приём" /> |
||||
<LlmTable |
||||
headers={["Поле", "Тип", "Placeholder"]} |
||||
rows={[ |
||||
["Имя", "input[type=text]", "«Введите ваше имя»"], |
||||
["Телефон", "input[type=tel]", "«Введите ваш телефон»"], |
||||
["Врач", "select", "«Выберите ЛОР врача»"], |
||||
["Согласие", "checkbox", "«Отправляя данные, я даю согласие...»"], |
||||
["Кнопка", "button bb-btn-primary", "«Запишите меня!»"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Форма 2 — Запрос стоимости" /> |
||||
<LlmTable |
||||
headers={["Поле", "Тип", "Placeholder"]} |
||||
rows={[ |
||||
["Имя", "input[type=text]", "«Введите ваше имя»"], |
||||
["Телефон", "input[type=tel]", "«Введите ваш телефон»"], |
||||
["Согласие", "checkbox", "«Отправляя данные, я даю согласие...»"], |
||||
["Кнопка", "button bb-btn-primary", "«Перезвоните мне»"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "Обе формы: фон #d4f6f8" }, |
||||
{ ok: true, text: "Чекбокс согласия обязателен в каждой форме" }, |
||||
{ ok: true, text: "Кнопки: bb-btn-primary (#FFA39C)" }, |
||||
{ ok: true, text: "Все input/select: height 50px (bb-input, bb-select)" }, |
||||
{ ok: false, text: "Не убирать чекбокс согласия на обработку данных" }, |
||||
{ ok: false, text: "Не менять стиль кнопки на outline или teal" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
return <ContactFormsPageClient />; |
||||
} |
||||
|
||||
@ -0,0 +1,204 @@
|
||||
"use client"; |
||||
|
||||
import { useState } from "react"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { BlockMetaBar, type SnapshotData } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import { FooterBlock } from "@/components/blocks/FooterBlock"; |
||||
|
||||
const BLOCK_PATH = "/blocks/contact"; |
||||
|
||||
const FOOTER_COLUMNS = [ |
||||
{ |
||||
title: "О клинике", |
||||
links: ["Лицензия", "Миссия", "Врачи", "Вакансии", "История", "Образовательная деятельность", "При инфо"], |
||||
}, |
||||
{ |
||||
title: "Заболевания", |
||||
links: ["Ринит", "Отит", "Гайморит", "Тонзиллит", "Полипы носа", "Искривление перегородки"], |
||||
}, |
||||
{ |
||||
title: "Вопрос-ответ", |
||||
links: [ |
||||
"Что нужно знать до операции на ухо", |
||||
"Что нужно знать до операции на нос", |
||||
"Отзывы до и после лечения у детей", |
||||
"Что нужно знать при лечении у детей", |
||||
], |
||||
}, |
||||
{ |
||||
title: "Операции", |
||||
links: ["Септопластика", "Турбинопластика", "Тонзиллэктомия", "Аденотомия", "Тимпанопластика", "Мирингопластика"], |
||||
}, |
||||
]; |
||||
|
||||
const LLM_FOOTER_TEXT = ` |
||||
БЛОК: Подвал сайта (Footer) |
||||
Источник: perm.oclinica.ru/lor — нижняя часть страницы |
||||
Версия: v1.1 |
||||
|
||||
СТРУКТУРА ПОДВАЛА: |
||||
|
||||
1. ОСНОВНАЯ ЧАСТЬ — 4 колонки ссылок: |
||||
Колонка 1 «О клинике»: Лицензия, Миссия, Врачи, Вакансии, История, Образовательная деятельность |
||||
Колонка 2 «Заболевания»: Ринит, Отит, Гайморит, Тонзиллит, Полипы носа, Искривление перегородки |
||||
Колонка 3 «Вопрос-ответ»: 4 вопроса об операциях и лечении |
||||
Колонка 4 «Операции»: Септопластика, Турбинопластика, Тонзиллэктомия, Аденотомия и др. |
||||
|
||||
2. НИЖНЯЯ ПОЛОСА: |
||||
Левая: Логотип «КЛИНИКА УХО ГОРЛО НОС ИМ. ПРОФ. Е.Н.ОЛЕНЕВОЙ» |
||||
Центр: |
||||
— «Мы находимся по адресу: Пермь, ул. Г. Звезда...» |
||||
— Иконки соцсетей: VK, OK, YouTube, Telegram и другие |
||||
Правая: Часы работы: |
||||
— Пн-пт: 9:00–21:00 |
||||
— Сб: 9:00–18:00 |
||||
— Вс: выходной |
||||
— Вторая клиника часы отдельно |
||||
|
||||
ЦВЕТА: |
||||
Фон подвала: #fff или светло-серый (#f8f9fa) |
||||
Заголовки колонок: #111827, font-weight 600 |
||||
Ссылки: #53514e (--brand-073m), hover: #0089c3 |
||||
Разделитель: border-top 1px solid #e5e7eb |
||||
Часы работы: #374151 |
||||
|
||||
ПРАВИЛА: |
||||
✓ 4 колонки ссылок в основной части |
||||
✓ Логотип в нижней полосе слева |
||||
✓ Адрес + соцсети в центре нижней полосы |
||||
✓ Часы работы справа |
||||
✕ Не менять структуру 4 колонок |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Колонка «О клинике»: обновлены все ссылки по реальному сайту (13 ссылок)", |
||||
"Колонка «Заболевания»: обновлены ссылки (5 категорий)", |
||||
"Колонка «Вопрос-ответ»: обновлены ссылки (6 пунктов)", |
||||
"Колонка «Операции»: обновлены ссылки (11 операций, было 6)", |
||||
"Два адреса: Клары Цеткин, 9 + Газеты Звезда, 31А", |
||||
"Два графика работы по филиалам", |
||||
"Соцсети: добавлен Дзен", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function ContactPageClient() { |
||||
const [snapshot, setSnapshot] = useState<SnapshotData | null>(null); |
||||
|
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Подвал (Footer) |
||||
</h1> |
||||
<BlockMetaBar |
||||
path={BLOCK_PATH} |
||||
defaultVersion="v1.1" |
||||
defaultIsInPreview={false} |
||||
defaultChangelog={CHANGELOG} |
||||
onSnapshotSelect={setSnapshot} |
||||
/> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Подвал сайта с perm.oclinica.ru — 4 колонки ссылок, логотип, адрес, часы работы, соцсети. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
{snapshot ? ( |
||||
<> |
||||
<style dangerouslySetInnerHTML={{ __html: snapshot.css }} /> |
||||
<div dangerouslySetInnerHTML={{ __html: snapshot.html }} /> |
||||
</> |
||||
) : ( |
||||
<div data-block-capture={BLOCK_PATH}> |
||||
<FooterBlock /> |
||||
</div> |
||||
)} |
||||
</section> |
||||
|
||||
{/* Колонки */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Четыре колонки ссылок |
||||
</h2> |
||||
<div className="grid grid-cols-4 gap-3"> |
||||
{FOOTER_COLUMNS.map((col) => ( |
||||
<div |
||||
key={col.title} |
||||
className="rounded-xl p-3 space-y-1" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<p className="font-semibold text-xs" style={{ color: "var(--bb-text)" }}> |
||||
{col.title} |
||||
</p> |
||||
<p className="text-[10px]" style={{ color: "var(--bb-text-muted)" }}> |
||||
{col.links.length} ссылок |
||||
</p> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/contact" version="v1.1" specText={LLM_FOOTER_TEXT}> |
||||
<LlmSection title="Структура подвала" /> |
||||
<LlmTable |
||||
headers={["Зона", "Содержимое", "Фон"]} |
||||
rows={[ |
||||
["4 колонки ссылок", "О клинике / Заболевания / Вопрос-ответ / Операции", "#f8f9fa"], |
||||
["Нижняя полоса — лево", "Логотип клиники (иконка + текст)", "#fff"], |
||||
["Нижняя полоса — центр", "Адрес + иконки соцсетей (VK, OK, YT, TG)", "#fff"], |
||||
["Нижняя полоса — право", "Часы работы Пн–пт / Сб / Вс", "#fff"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Часы работы" /> |
||||
<LlmTable |
||||
headers={["День", "Часы"]} |
||||
rows={[ |
||||
["Пн–пт", "9:00–21:00"], |
||||
["Сб", "9:00–18:00"], |
||||
["Вс", "выходной"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Цвета" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Цвет", "Токен"]} |
||||
rows={[ |
||||
["Заголовки колонок", "#111827, font-weight 600", "—"], |
||||
["Ссылки (default)", "#53514e", "--brand-073m"], |
||||
["Ссылки (hover)", "#0089c3", "--brand-053m"], |
||||
["Фон основной части", "#f8f9fa", "—"], |
||||
["Фон нижней полосы", "#ffffff", "—"], |
||||
["Разделитель", "1px solid #e5e7eb", "—"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "4 колонки: О клинике / Заболевания / Вопрос-ответ / Операции" }, |
||||
{ ok: true, text: "Логотип в нижней полосе слева" }, |
||||
{ ok: true, text: "Адрес и соцсети в центре" }, |
||||
{ ok: true, text: "Часы работы справа" }, |
||||
{ ok: false, text: "Не менять структуру и порядок 4 колонок" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
} |
||||
@ -1,187 +1,10 @@
|
||||
import type { Metadata } from "next"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { BlockMetaBar } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import { FooterBlock } from "@/components/blocks/FooterBlock"; |
||||
import ContactPageClient from "./ContactPageClient"; |
||||
|
||||
export const metadata: Metadata = { |
||||
title: "Подвал (Footer). Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой", |
||||
}; |
||||
|
||||
const FOOTER_COLUMNS = [ |
||||
{ |
||||
title: "О клинике", |
||||
links: ["Лицензия", "Миссия", "Врачи", "Вакансии", "История", "Образовательная деятельность", "При инфо"], |
||||
}, |
||||
{ |
||||
title: "Заболевания", |
||||
links: ["Ринит", "Отит", "Гайморит", "Тонзиллит", "Полипы носа", "Искривление перегородки"], |
||||
}, |
||||
{ |
||||
title: "Вопрос-ответ", |
||||
links: [ |
||||
"Что нужно знать до операции на ухо", |
||||
"Что нужно знать до операции на нос", |
||||
"Отзывы до и после лечения у детей", |
||||
"Что нужно знать при лечении у детей", |
||||
], |
||||
}, |
||||
{ |
||||
title: "Операции", |
||||
links: ["Септопластика", "Турбинопластика", "Тонзиллэктомия", "Аденотомия", "Тимпанопластика", "Мирингопластика"], |
||||
}, |
||||
]; |
||||
|
||||
const LLM_FOOTER_TEXT = ` |
||||
БЛОК: Подвал сайта (Footer) |
||||
Источник: perm.oclinica.ru/lor — нижняя часть страницы |
||||
Версия: v1.1 |
||||
|
||||
СТРУКТУРА ПОДВАЛА: |
||||
|
||||
1. ОСНОВНАЯ ЧАСТЬ — 4 колонки ссылок: |
||||
Колонка 1 «О клинике»: Лицензия, Миссия, Врачи, Вакансии, История, Образовательная деятельность |
||||
Колонка 2 «Заболевания»: Ринит, Отит, Гайморит, Тонзиллит, Полипы носа, Искривление перегородки |
||||
Колонка 3 «Вопрос-ответ»: 4 вопроса об операциях и лечении |
||||
Колонка 4 «Операции»: Септопластика, Турбинопластика, Тонзиллэктомия, Аденотомия и др. |
||||
|
||||
2. НИЖНЯЯ ПОЛОСА: |
||||
Левая: Логотип «КЛИНИКА УХО ГОРЛО НОС ИМ. ПРОФ. Е.Н.ОЛЕНЕВОЙ» |
||||
Центр: |
||||
— «Мы находимся по адресу: Пермь, ул. Г. Звезда...» |
||||
— Иконки соцсетей: VK, OK, YouTube, Telegram и другие |
||||
Правая: Часы работы: |
||||
— Пн-пт: 9:00–21:00 |
||||
— Сб: 9:00–18:00 |
||||
— Вс: выходной |
||||
— Вторая клиника часы отдельно |
||||
|
||||
ЦВЕТА: |
||||
Фон подвала: #fff или светло-серый (#f8f9fa) |
||||
Заголовки колонок: #111827, font-weight 600 |
||||
Ссылки: #53514e (--brand-073m), hover: #0089c3 |
||||
Разделитель: border-top 1px solid #e5e7eb |
||||
Часы работы: #374151 |
||||
|
||||
ПРАВИЛА: |
||||
✓ 4 колонки ссылок в основной части |
||||
✓ Логотип в нижней полосе слева |
||||
✓ Адрес + соцсети в центре нижней полосы |
||||
✓ Часы работы справа |
||||
✕ Не менять структуру 4 колонок |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Колонка «О клинике»: обновлены все ссылки по реальному сайту (13 ссылок)", |
||||
"Колонка «Заболевания»: обновлены ссылки (5 категорий)", |
||||
"Колонка «Вопрос-ответ»: обновлены ссылки (6 пунктов)", |
||||
"Колонка «Операции»: обновлены ссылки (11 операций, было 6)", |
||||
"Два адреса: Клары Цеткин, 9 + Газеты Звезда, 31А", |
||||
"Два графика работы по филиалам", |
||||
"Соцсети: добавлен Дзен", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function ContactFooterPage() { |
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Подвал (Footer) |
||||
</h1> |
||||
<BlockMetaBar path="/blocks/contact" defaultVersion="v1.1" defaultIsInPreview={false} defaultChangelog={CHANGELOG} /> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Подвал сайта с perm.oclinica.ru — 4 колонки ссылок, логотип, адрес, часы работы, соцсети. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
<FooterBlock /> |
||||
</section> |
||||
|
||||
{/* Колонки */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Четыре колонки ссылок |
||||
</h2> |
||||
<div className="grid grid-cols-4 gap-3"> |
||||
{FOOTER_COLUMNS.map((col) => ( |
||||
<div |
||||
key={col.title} |
||||
className="rounded-xl p-3 space-y-1" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<p className="font-semibold text-xs" style={{ color: "var(--bb-text)" }}> |
||||
{col.title} |
||||
</p> |
||||
<p className="text-[10px]" style={{ color: "var(--bb-text-muted)" }}> |
||||
{col.links.length} ссылок |
||||
</p> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/contact" version="v1.1" specText={LLM_FOOTER_TEXT}> |
||||
<LlmSection title="Структура подвала" /> |
||||
<LlmTable |
||||
headers={["Зона", "Содержимое", "Фон"]} |
||||
rows={[ |
||||
["4 колонки ссылок", "О клинике / Заболевания / Вопрос-ответ / Операции", "#f8f9fa"], |
||||
["Нижняя полоса — лево", "Логотип клиники (иконка + текст)", "#fff"], |
||||
["Нижняя полоса — центр", "Адрес + иконки соцсетей (VK, OK, YT, TG)", "#fff"], |
||||
["Нижняя полоса — право", "Часы работы Пн–пт / Сб / Вс", "#fff"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Часы работы" /> |
||||
<LlmTable |
||||
headers={["День", "Часы"]} |
||||
rows={[ |
||||
["Пн–пт", "9:00–21:00"], |
||||
["Сб", "9:00–18:00"], |
||||
["Вс", "выходной"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Цвета" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Цвет", "Токен"]} |
||||
rows={[ |
||||
["Заголовки колонок", "#111827, font-weight 600", "—"], |
||||
["Ссылки (default)", "#53514e", "--brand-073m"], |
||||
["Ссылки (hover)", "#0089c3", "--brand-053m"], |
||||
["Фон основной части", "#f8f9fa", "—"], |
||||
["Фон нижней полосы", "#ffffff", "—"], |
||||
["Разделитель", "1px solid #e5e7eb", "—"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "4 колонки: О клинике / Заболевания / Вопрос-ответ / Операции" }, |
||||
{ ok: true, text: "Логотип в нижней полосе слева" }, |
||||
{ ok: true, text: "Адрес и соцсети в центре" }, |
||||
{ ok: true, text: "Часы работы справа" }, |
||||
{ ok: false, text: "Не менять структуру и порядок 4 колонок" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
return <ContactPageClient />; |
||||
} |
||||
|
||||
@ -0,0 +1,187 @@
|
||||
"use client"; |
||||
|
||||
import { useState } from "react"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { DoctorsBlock, STATS, DOCTORS } from "@/components/blocks/DoctorsBlock"; |
||||
import { BlockMetaBar, type SnapshotData } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
|
||||
const BLOCK_PATH = "/blocks/doctors"; |
||||
|
||||
const LLM_DOCTORS_TEXT = ` |
||||
БЛОК: Наши врачи |
||||
Источник: perm.oclinica.ru/lor — блок под CEO-текстом |
||||
Версия: v1.2 |
||||
|
||||
СТРУКТУРА БЛОКА: |
||||
1. ЗАГОЛОВОК H2: «Приём ведут опытные ЛОР врачи» |
||||
Подзаголовок: описание принципа работы врачей клиники |
||||
Размер: 36px, font-bold, #000000, line-height: 38px |
||||
|
||||
2. БЛОК СТАТИСТИКИ (3 показателя в ряд): |
||||
— «Ежедневно 27 ЛОР врачей работают в клинике» |
||||
— «В том числе 6 кандидатов медицинских наук» |
||||
— «Свыше 12 000 успешно проведённых операций» |
||||
Стиль: весь текст #60959c, bold, ~18px. Фона НЕТ. Снизу border-bottom 3px solid #60959c |
||||
|
||||
3. СЕТКА КАРТОЧЕК ВРАЧЕЙ (6 штук, 6 в ряд): |
||||
Каждая карточка: |
||||
— Фото врача 110×150px, object-fit: cover, object-position: center top, border-radius 4px |
||||
— Имя (12px, font-weight 500, цвет #60959c) |
||||
— Специализация (11px, #374151) |
||||
Карточки без рамки, gap минимальный |
||||
|
||||
ЦВЕТА: |
||||
Заголовок H2: #000000, 36px, line-height 38px |
||||
Статистика текст: #60959c (серо-бирюзовый) |
||||
Статистика черта: border-bottom 3px solid #60959c |
||||
Имя врача: #60959c |
||||
Специализация: #374151 |
||||
|
||||
ПРАВИЛА: |
||||
✓ Заголовок H2 + описание обязательны |
||||
✓ 3 stat-блока в ряд, без фоновых блоков |
||||
✓ Сетка 6 колонок (6 карточек в ряд) |
||||
✕ Не отображать более 6 врачей в основном блоке |
||||
✕ Не убирать статистику |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.2", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"H2: размер с ~30px на 36px, цвет с #111827 на #000000", |
||||
"H2 line-height: 38px", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.1", |
||||
date: "23.03.2026", |
||||
changes: [ |
||||
"6 реальных фото врачей с сайта", |
||||
"Статистика без фона, только border-bottom #60959c", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function DoctorsPageClient() { |
||||
const [snapshot, setSnapshot] = useState<SnapshotData | null>(null); |
||||
|
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Блок «Наши врачи» |
||||
</h1> |
||||
<BlockMetaBar |
||||
path={BLOCK_PATH} |
||||
defaultVersion="v1.2" |
||||
defaultIsInPreview={true} |
||||
defaultChangelog={CHANGELOG} |
||||
onSnapshotSelect={setSnapshot} |
||||
/> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Блок на странице perm.oclinica.ru/lor — заголовок, 3 стат-блока, сетка из 6 карточек врачей. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
{snapshot ? ( |
||||
<div |
||||
className="rounded-xl p-8" |
||||
style={{ background: "#fff", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<style dangerouslySetInnerHTML={{ __html: snapshot.css }} /> |
||||
<div dangerouslySetInnerHTML={{ __html: snapshot.html }} /> |
||||
</div> |
||||
) : ( |
||||
<div |
||||
data-block-capture={BLOCK_PATH} |
||||
className="rounded-xl p-8" |
||||
style={{ background: "#fff", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<DoctorsBlock /> |
||||
</div> |
||||
)} |
||||
</section> |
||||
|
||||
{/* Стат-блоки — разбор */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Блок статистики |
||||
</h2> |
||||
<div className="grid grid-cols-3 gap-6"> |
||||
{STATS.map((s) => ( |
||||
<div |
||||
key={s.num} |
||||
className="pb-3 space-y-1" |
||||
style={{ borderBottom: "3px solid #60959c" }} |
||||
> |
||||
<p className="text-base font-bold leading-snug" style={{ color: "#60959c" }}> |
||||
{s.prefix} {s.num} {s.label} |
||||
</p> |
||||
<p className="text-[10px] font-mono" style={{ color: "var(--bb-text-muted)" }}> |
||||
цвет текста: #60959c · черта: 3px solid #60959c |
||||
</p> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/doctors" version="v1.2" specText={LLM_DOCTORS_TEXT}> |
||||
<LlmSection title="Структура блока" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Содержимое", "Стиль"]} |
||||
rows={[ |
||||
["H2", "«Приём ведут опытные ЛОР врачи»", "36px, font-bold, #000000, line-height 38px"], |
||||
["Подзаголовок", "Описание принципа работы", "14px, #374151, line-height 1.7"], |
||||
["Статистика", "3 блока в ряд, без фона", "#60959c bold + border-bottom 3px solid #60959c"], |
||||
["Сетка врачей", "6 карточек в 1 ряд", "фото 110×150px + имя #60959c + специализация #374151"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Три показателя" /> |
||||
<LlmTable |
||||
headers={["Число", "Описание"]} |
||||
rows={STATS.map((s) => [`${s.prefix} ${s.num}`, s.label])} |
||||
/> |
||||
<LlmSection title="6 врачей слайдера (perm.oclinica.ru/lor)" /> |
||||
<LlmTable |
||||
headers={["Имя", "Специализация"]} |
||||
rows={DOCTORS.map((d) => [d.name, d.spec])} |
||||
/> |
||||
<LlmSection title="Карточка врача" /> |
||||
<LlmTable |
||||
headers={["Поле", "Размер / Стиль"]} |
||||
rows={[ |
||||
["Фото", "110×150px, object-fit: cover, object-position: center top, border-radius 4px"], |
||||
["Имя", "12px, font-weight 500, #60959c"], |
||||
["Специализация", "11px, #374151"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "H2 + описание обязательны" }, |
||||
{ ok: true, text: "3 stat-блока в ряд, без фоновых рамок" }, |
||||
{ ok: true, text: "Сетка 6 карточек в 1 ряд (6 колонок)" }, |
||||
{ ok: false, text: "Не показывать более 6 врачей в основном блоке" }, |
||||
{ ok: false, text: "Не убирать статистику" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
} |
||||
@ -1,168 +1,10 @@
|
||||
import type { Metadata } from "next"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { DoctorsBlock, STATS, DOCTORS } from "@/components/blocks/DoctorsBlock"; |
||||
import { BlockMetaBar } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import DoctorsPageClient from "./DoctorsPageClient"; |
||||
|
||||
export const metadata: Metadata = { |
||||
title: "Блок «Наши врачи». Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой", |
||||
}; |
||||
|
||||
const LLM_DOCTORS_TEXT = ` |
||||
БЛОК: Наши врачи |
||||
Источник: perm.oclinica.ru/lor — блок под CEO-текстом |
||||
Версия: v1.2 |
||||
|
||||
СТРУКТУРА БЛОКА: |
||||
1. ЗАГОЛОВОК H2: «Приём ведут опытные ЛОР врачи» |
||||
Подзаголовок: описание принципа работы врачей клиники |
||||
Размер: 36px, font-bold, #000000, line-height: 38px |
||||
|
||||
2. БЛОК СТАТИСТИКИ (3 показателя в ряд): |
||||
— «Ежедневно 27 ЛОР врачей работают в клинике» |
||||
— «В том числе 6 кандидатов медицинских наук» |
||||
— «Свыше 12 000 успешно проведённых операций» |
||||
Стиль: весь текст #60959c, bold, ~18px. Фона НЕТ. Снизу border-bottom 3px solid #60959c |
||||
|
||||
3. СЕТКА КАРТОЧЕК ВРАЧЕЙ (6 штук, 6 в ряд): |
||||
Каждая карточка: |
||||
— Фото врача 110×150px, object-fit: cover, object-position: center top, border-radius 4px |
||||
— Имя (12px, font-weight 500, цвет #60959c) |
||||
— Специализация (11px, #374151) |
||||
Карточки без рамки, gap минимальный |
||||
|
||||
ЦВЕТА: |
||||
Заголовок H2: #000000, 36px, line-height 38px |
||||
Статистика текст: #60959c (серо-бирюзовый) |
||||
Статистика черта: border-bottom 3px solid #60959c |
||||
Имя врача: #60959c |
||||
Специализация: #374151 |
||||
|
||||
ПРАВИЛА: |
||||
✓ Заголовок H2 + описание обязательны |
||||
✓ 3 stat-блока в ряд, без фоновых блоков |
||||
✓ Сетка 6 колонок (6 карточек в ряд) |
||||
✕ Не отображать более 6 врачей в основном блоке |
||||
✕ Не убирать статистику |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.2", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"H2: размер с ~30px на 36px, цвет с #111827 на #000000", |
||||
"H2 line-height: 38px", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.1", |
||||
date: "23.03.2026", |
||||
changes: [ |
||||
"6 реальных фото врачей с сайта", |
||||
"Статистика без фона, только border-bottom #60959c", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function DoctorsBlockPage() { |
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Блок «Наши врачи» |
||||
</h1> |
||||
<BlockMetaBar path="/blocks/doctors" defaultVersion="v1.2" defaultIsInPreview={true} defaultChangelog={CHANGELOG} /> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Блок на странице perm.oclinica.ru/lor — заголовок, 3 стат-блока, сетка из 6 карточек врачей. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
<div |
||||
className="rounded-xl p-8" |
||||
style={{ background: "#fff", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<DoctorsBlock /> |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Стат-блоки — разбор */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Блок статистики |
||||
</h2> |
||||
<div className="grid grid-cols-3 gap-6"> |
||||
{STATS.map((s) => ( |
||||
<div |
||||
key={s.num} |
||||
className="pb-3 space-y-1" |
||||
style={{ borderBottom: "3px solid #60959c" }} |
||||
> |
||||
<p className="text-base font-bold leading-snug" style={{ color: "#60959c" }}> |
||||
{s.prefix} {s.num} {s.label} |
||||
</p> |
||||
<p className="text-[10px] font-mono" style={{ color: "var(--bb-text-muted)" }}> |
||||
цвет текста: #60959c · черта: 3px solid #60959c |
||||
</p> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/doctors" version="v1.2" specText={LLM_DOCTORS_TEXT}> |
||||
<LlmSection title="Структура блока" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Содержимое", "Стиль"]} |
||||
rows={[ |
||||
["H2", "«Приём ведут опытные ЛОР врачи»", "36px, font-bold, #000000, line-height 38px"], |
||||
["Подзаголовок", "Описание принципа работы", "14px, #374151, line-height 1.7"], |
||||
["Статистика", "3 блока в ряд, без фона", "#60959c bold + border-bottom 3px solid #60959c"], |
||||
["Сетка врачей", "6 карточек в 1 ряд", "фото 110×150px + имя #60959c + специализация #374151"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Три показателя" /> |
||||
<LlmTable |
||||
headers={["Число", "Описание"]} |
||||
rows={STATS.map((s) => [`${s.prefix} ${s.num}`, s.label])} |
||||
/> |
||||
<LlmSection title="6 врачей слайдера (perm.oclinica.ru/lor)" /> |
||||
<LlmTable |
||||
headers={["Имя", "Специализация"]} |
||||
rows={DOCTORS.map((d) => [d.name, d.spec])} |
||||
/> |
||||
<LlmSection title="Карточка врача" /> |
||||
<LlmTable |
||||
headers={["Поле", "Размер / Стиль"]} |
||||
rows={[ |
||||
["Фото", "110×150px, object-fit: cover, object-position: center top, border-radius 4px"], |
||||
["Имя", "12px, font-weight 500, #60959c"], |
||||
["Специализация", "11px, #374151"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "H2 + описание обязательны" }, |
||||
{ ok: true, text: "3 stat-блока в ряд, без фоновых рамок" }, |
||||
{ ok: true, text: "Сетка 6 карточек в 1 ряд (6 колонок)" }, |
||||
{ ok: false, text: "Не показывать более 6 врачей в основном блоке" }, |
||||
{ ok: false, text: "Не убирать статистику" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
return <DoctorsPageClient />; |
||||
} |
||||
|
||||
@ -0,0 +1,231 @@
|
||||
"use client"; |
||||
|
||||
import { useState } from "react"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { HeroBlock, HERO_CHECKS } from "@/components/blocks/HeroBlock"; |
||||
import { BlockMetaBar, type SnapshotData } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
|
||||
const BLOCK_PATH = "/blocks/hero"; |
||||
|
||||
const LLM_HERO_TEXT = ` |
||||
БЛОК: Hero-баннер (главный баннер страницы) |
||||
Источник: perm.oclinica.ru/lor — реальный баннер раздела ЛОР |
||||
Версия: v1.3 |
||||
|
||||
ЗАГОЛОВОК СТРАНИЦЫ (H1, над баннером): |
||||
«ЛОР Клиника ухо, горло, нос - медицинский центр лечения ЛОР заболеваний у детей и взрослых» |
||||
Шрифт: Fira Sans, 36px, weight 700, цвет: #cb9768 |
||||
|
||||
СТРУКТУРА БАННЕРА (двухколоночная, единый фон #f9f4e7): |
||||
Левая колонка (~50%): |
||||
— Заголовок: «ЭНДОСКОПИЧЕСКОЕ ХИРУРГИЧЕСКОЕ ЛЕЧЕНИЕ ЛОР ОРГАНОВ» |
||||
Шрифт: Fira Sans, 22px, weight 700, uppercase, цвет #333333 |
||||
— 3 пункта с галочками (✓ бежевый #bf9975): |
||||
1. «БЕЗОПАСНО – оперируют хирурги с 15-летним опытом работы» |
||||
2. «БЕЗ ВНЕШНИХ РАЗРЕЗОВ – хирургия сверхмалых размеров» |
||||
3. «БЫСТРО – под наблюдением врача пациент находится 1 сутки» |
||||
Ключевое слово: uppercase bold; описание: обычный текст, ~13px |
||||
— Кнопка «Узнать стоимость операции» — стиль: bb-btn-pill (кремовый фон #E9E4D4, radius 25px) |
||||
Правая колонка (~50%): |
||||
— Фото врача с пациентом |
||||
— Изображение занимает всю высоту блока |
||||
|
||||
ПОД БАННЕРОМ: |
||||
— Кнопки соцсетей (VK, FB, TW), цвет #9ca3af |
||||
— Счётчик просмотров |
||||
|
||||
ЦВЕТА: |
||||
Фон баннера: #f9f4e7 (светло-кремовый, единый для всего блока) |
||||
Кнопка CTA: pill-стиль (кремовый #E9E4D4, border #D5CFBD, radius 25px) |
||||
Заголовок блока: #333333 |
||||
Пункты: ключевое слово #111827 bold, описание #374151 |
||||
Галочка: #bf9975 (бежевый) |
||||
|
||||
ПРАВИЛА: |
||||
✓ Фон баннера всегда #f9f4e7 (светло-кремовый) — единый, без разделения на зоны |
||||
✓ Заголовок блока uppercase, жирный |
||||
✓ Три пункта с галочками обязательны |
||||
✕ Не менять фон баннера на другой цвет |
||||
✕ Не разбивать фон на два разных цвета |
||||
✕ Не убирать три пункта с галочками |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.3", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Счётчик: «98 573 просмотра» заменён на «Поделиться / 98572» (как на реальном сайте)", |
||||
"Убраны кнопки VK/FB/TW", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.2", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"H1: цвет исправлен с #53514e на #cb9768, размер с ~20px на 36px", |
||||
"Заголовок баннера: размер с ~16px на 22px, цвет с #111827 на #333333", |
||||
"CTA-кнопка: стиль изменён с outline на pill (фон #E9E4D4, radius 25px)", |
||||
"Дефис в H1: длинное тире «–» заменено на простой дефис «-» (как на сайте)", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.1", |
||||
date: "23.03.2026", |
||||
changes: [ |
||||
"Единый фон #f9f4e7 (ранее был разбит на две зоны)", |
||||
"Реальное фото врача с пациентом", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function HeroPageClient() { |
||||
const [snapshot, setSnapshot] = useState<SnapshotData | null>(null); |
||||
|
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Hero-баннер |
||||
</h1> |
||||
<BlockMetaBar |
||||
path={BLOCK_PATH} |
||||
defaultVersion="v1.3" |
||||
defaultIsInPreview={true} |
||||
defaultChangelog={CHANGELOG} |
||||
onSnapshotSelect={setSnapshot} |
||||
/> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Главный баннер страницы раздела ЛОР — perm.oclinica.ru/lor. Двухколоночный блок, единый светло-кремовый фон{" "} |
||||
<strong>#f9f4e7</strong>. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
{snapshot ? ( |
||||
<> |
||||
<style dangerouslySetInnerHTML={{ __html: snapshot.css }} /> |
||||
<div dangerouslySetInnerHTML={{ __html: snapshot.html }} /> |
||||
</> |
||||
) : ( |
||||
<div data-block-capture={BLOCK_PATH}> |
||||
<HeroBlock /> |
||||
</div> |
||||
)} |
||||
</section> |
||||
|
||||
{/* Анатомия */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Анатомия баннера |
||||
</h2> |
||||
<div |
||||
className="rounded-xl p-5 space-y-2" |
||||
style={{ background: "#f5f0e8", border: "1px solid #d5cfbd" }} |
||||
> |
||||
<p className="font-semibold text-sm" style={{ color: "var(--bb-text)" }}> |
||||
Весь баннер — единый фон <span className="font-mono">#f9f4e7</span> |
||||
</p> |
||||
<ul className="space-y-1"> |
||||
{[ |
||||
"Фон: #f9f4e7 (светло-кремовый) — одинаковый для всего блока", |
||||
"Левая половина (~50%): заголовок uppercase bold + 3 галочки + кнопка outline", |
||||
"Правая половина (~50%): фото врача с пациентом", |
||||
"Минимальная высота: ~280px", |
||||
].map((item) => ( |
||||
<li key={item} className="text-xs flex items-start gap-1.5"> |
||||
<span style={{ color: "var(--brand-053m)" }}>·</span> |
||||
<span style={{ color: "var(--bb-text-muted)" }}>{item}</span> |
||||
</li> |
||||
))} |
||||
</ul> |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Три пункта с галочками */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Три пункта баннера |
||||
</h2> |
||||
<div className="space-y-2"> |
||||
{HERO_CHECKS.map((c) => ( |
||||
<div |
||||
key={c.key} |
||||
className="flex items-start gap-3 p-3 rounded-lg" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<span className="font-bold text-lg shrink-0" style={{ color: "#bf9975" }}> |
||||
✓ |
||||
</span> |
||||
<div> |
||||
<span className="text-sm font-bold uppercase" style={{ color: "#111827" }}> |
||||
{c.key} |
||||
</span> |
||||
<span className="text-sm" style={{ color: "#374151" }}> |
||||
{" "}– {c.desc} |
||||
</span> |
||||
</div> |
||||
</div> |
||||
))} |
||||
</div> |
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> |
||||
Ключевое слово: uppercase + bold. Описание: обычный текст. Галочка: #bf9975 (бежевый). |
||||
</p> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/hero" version="v1.3" specText={LLM_HERO_TEXT}> |
||||
<LlmSection title="Структура баннера" /> |
||||
<LlmTable |
||||
headers={["Зона", "Ширина", "Фон", "Содержимое"]} |
||||
rows={[ |
||||
["Весь баннер", "100%", "#f9f4e7", "Единый светло-кремовый фон"], |
||||
["Левый блок", "~50%", "#f9f4e7 (общий)", "Заголовок uppercase + 3 галочки + кнопка pill"], |
||||
["Правый блок", "~50%", "#f9f4e7 (общий)", "Фото врача с пациентом"], |
||||
["Под баннером", "100%", "#fff", "Кнопки соцсетей + счётчик просмотров"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Три пункта баннера" /> |
||||
<LlmTable |
||||
headers={["Ключевое слово", "Описание"]} |
||||
rows={HERO_CHECKS.map((c) => [c.key, c.desc])} |
||||
/> |
||||
<LlmSection title="Цвета" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Цвет", "Токен"]} |
||||
rows={[ |
||||
["Фон баннера (единый)", "#f9f4e7", "Светло-кремовый фон"], |
||||
["Кнопка CTA", "pill-стиль (#E9E4D4, 25px)", "bb-btn-pill"], |
||||
["Заголовок блока", "#333333", "—"], |
||||
["H1 страницы", "#cb9768", "36px, bold"], |
||||
["Галочка ✓", "#bf9975", "Бежевый"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "Фон баннера: #f9f4e7 (светло-кремовый) — единый для всего блока" }, |
||||
{ ok: true, text: "Кнопка CTA: bb-btn-pill (кремовый #E9E4D4, radius 25px)" }, |
||||
{ ok: true, text: "Заголовок: uppercase, bold" }, |
||||
{ ok: true, text: "Три пункта с галочками ✓ (#bf9975)" }, |
||||
{ ok: false, text: "Не менять фон баннера на другой цвет" }, |
||||
{ ok: false, text: "Не разбивать баннер на два разных цвета фона" }, |
||||
{ ok: false, text: "Не убирать три пункта с галочками" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
} |
||||
@ -1,214 +1,10 @@
|
||||
import type { Metadata } from "next"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { HeroBlock, HERO_CHECKS } from "@/components/blocks/HeroBlock"; |
||||
import { BlockMetaBar } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import HeroPageClient from "./HeroPageClient"; |
||||
|
||||
export const metadata: Metadata = { |
||||
title: "Hero-баннер. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой", |
||||
}; |
||||
|
||||
const LLM_HERO_TEXT = ` |
||||
БЛОК: Hero-баннер (главный баннер страницы) |
||||
Источник: perm.oclinica.ru/lor — реальный баннер раздела ЛОР |
||||
Версия: v1.3 |
||||
|
||||
ЗАГОЛОВОК СТРАНИЦЫ (H1, над баннером): |
||||
«ЛОР Клиника ухо, горло, нос - медицинский центр лечения ЛОР заболеваний у детей и взрослых» |
||||
Шрифт: Fira Sans, 36px, weight 700, цвет: #cb9768 |
||||
|
||||
СТРУКТУРА БАННЕРА (двухколоночная, единый фон #f9f4e7): |
||||
Левая колонка (~50%): |
||||
— Заголовок: «ЭНДОСКОПИЧЕСКОЕ ХИРУРГИЧЕСКОЕ ЛЕЧЕНИЕ ЛОР ОРГАНОВ» |
||||
Шрифт: Fira Sans, 22px, weight 700, uppercase, цвет #333333 |
||||
— 3 пункта с галочками (✓ бежевый #bf9975): |
||||
1. «БЕЗОПАСНО – оперируют хирурги с 15-летним опытом работы» |
||||
2. «БЕЗ ВНЕШНИХ РАЗРЕЗОВ – хирургия сверхмалых размеров» |
||||
3. «БЫСТРО – под наблюдением врача пациент находится 1 сутки» |
||||
Ключевое слово: uppercase bold; описание: обычный текст, ~13px |
||||
— Кнопка «Узнать стоимость операции» — стиль: bb-btn-pill (кремовый фон #E9E4D4, radius 25px) |
||||
Правая колонка (~50%): |
||||
— Фото врача с пациентом |
||||
— Изображение занимает всю высоту блока |
||||
|
||||
ПОД БАННЕРОМ: |
||||
— Кнопки соцсетей (VK, FB, TW), цвет #9ca3af |
||||
— Счётчик просмотров |
||||
|
||||
ЦВЕТА: |
||||
Фон баннера: #f9f4e7 (светло-кремовый, единый для всего блока) |
||||
Кнопка CTA: pill-стиль (кремовый #E9E4D4, border #D5CFBD, radius 25px) |
||||
Заголовок блока: #333333 |
||||
Пункты: ключевое слово #111827 bold, описание #374151 |
||||
Галочка: #bf9975 (бежевый) |
||||
|
||||
ПРАВИЛА: |
||||
✓ Фон баннера всегда #f9f4e7 (светло-кремовый) — единый, без разделения на зоны |
||||
✓ Заголовок блока uppercase, жирный |
||||
✓ Три пункта с галочками обязательны |
||||
✕ Не менять фон баннера на другой цвет |
||||
✕ Не разбивать фон на два разных цвета |
||||
✕ Не убирать три пункта с галочками |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.3", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Счётчик: «98 573 просмотра» заменён на «Поделиться / 98572» (как на реальном сайте)", |
||||
"Убраны кнопки VK/FB/TW", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.2", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"H1: цвет исправлен с #53514e на #cb9768, размер с ~20px на 36px", |
||||
"Заголовок баннера: размер с ~16px на 22px, цвет с #111827 на #333333", |
||||
"CTA-кнопка: стиль изменён с outline на pill (фон #E9E4D4, radius 25px)", |
||||
"Дефис в H1: длинное тире «–» заменено на простой дефис «-» (как на сайте)", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.1", |
||||
date: "23.03.2026", |
||||
changes: [ |
||||
"Единый фон #f9f4e7 (ранее был разбит на две зоны)", |
||||
"Реальное фото врача с пациентом", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function HeroPage() { |
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Hero-баннер |
||||
</h1> |
||||
<BlockMetaBar path="/blocks/hero" defaultVersion="v1.3" defaultIsInPreview={true} defaultChangelog={CHANGELOG} /> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Главный баннер страницы раздела ЛОР — perm.oclinica.ru/lor. Двухколоночный блок, единый светло-кремовый фон{" "} |
||||
<strong>#f9f4e7</strong>. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
<HeroBlock /> |
||||
</section> |
||||
|
||||
{/* Анатомия */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Анатомия баннера |
||||
</h2> |
||||
<div |
||||
className="rounded-xl p-5 space-y-2" |
||||
style={{ background: "#f5f0e8", border: "1px solid #d5cfbd" }} |
||||
> |
||||
<p className="font-semibold text-sm" style={{ color: "var(--bb-text)" }}> |
||||
Весь баннер — единый фон <span className="font-mono">#f9f4e7</span> |
||||
</p> |
||||
<ul className="space-y-1"> |
||||
{[ |
||||
"Фон: #f9f4e7 (светло-кремовый) — одинаковый для всего блока", |
||||
"Левая половина (~50%): заголовок uppercase bold + 3 галочки + кнопка outline", |
||||
"Правая половина (~50%): фото врача с пациентом", |
||||
"Минимальная высота: ~280px", |
||||
].map((item) => ( |
||||
<li key={item} className="text-xs flex items-start gap-1.5"> |
||||
<span style={{ color: "var(--brand-053m)" }}>·</span> |
||||
<span style={{ color: "var(--bb-text-muted)" }}>{item}</span> |
||||
</li> |
||||
))} |
||||
</ul> |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Три пункта с галочками */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Три пункта баннера |
||||
</h2> |
||||
<div className="space-y-2"> |
||||
{HERO_CHECKS.map((c) => ( |
||||
<div |
||||
key={c.key} |
||||
className="flex items-start gap-3 p-3 rounded-lg" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<span className="font-bold text-lg shrink-0" style={{ color: "#bf9975" }}> |
||||
✓ |
||||
</span> |
||||
<div> |
||||
<span className="text-sm font-bold uppercase" style={{ color: "#111827" }}> |
||||
{c.key} |
||||
</span> |
||||
<span className="text-sm" style={{ color: "#374151" }}> |
||||
{" "}– {c.desc} |
||||
</span> |
||||
</div> |
||||
</div> |
||||
))} |
||||
</div> |
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> |
||||
Ключевое слово: uppercase + bold. Описание: обычный текст. Галочка: #bf9975 (бежевый). |
||||
</p> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/hero" version="v1.3" specText={LLM_HERO_TEXT}> |
||||
<LlmSection title="Структура баннера" /> |
||||
<LlmTable |
||||
headers={["Зона", "Ширина", "Фон", "Содержимое"]} |
||||
rows={[ |
||||
["Весь баннер", "100%", "#f9f4e7", "Единый светло-кремовый фон"], |
||||
["Левый блок", "~50%", "#f9f4e7 (общий)", "Заголовок uppercase + 3 галочки + кнопка pill"], |
||||
["Правый блок", "~50%", "#f9f4e7 (общий)", "Фото врача с пациентом"], |
||||
["Под баннером", "100%", "#fff", "Кнопки соцсетей + счётчик просмотров"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Три пункта баннера" /> |
||||
<LlmTable |
||||
headers={["Ключевое слово", "Описание"]} |
||||
rows={HERO_CHECKS.map((c) => [c.key, c.desc])} |
||||
/> |
||||
<LlmSection title="Цвета" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Цвет", "Токен"]} |
||||
rows={[ |
||||
["Фон баннера (единый)", "#f9f4e7", "Светло-кремовый фон"], |
||||
["Кнопка CTA", "pill-стиль (#E9E4D4, 25px)", "bb-btn-pill"], |
||||
["Заголовок блока", "#333333", "—"], |
||||
["H1 страницы", "#cb9768", "36px, bold"], |
||||
["Галочка ✓", "#bf9975", "Бежевый"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "Фон баннера: #f9f4e7 (светло-кремовый) — единый для всего блока" }, |
||||
{ ok: true, text: "Кнопка CTA: bb-btn-pill (кремовый #E9E4D4, radius 25px)" }, |
||||
{ ok: true, text: "Заголовок: uppercase, bold" }, |
||||
{ ok: true, text: "Три пункта с галочками ✓ (#bf9975)" }, |
||||
{ ok: false, text: "Не менять фон баннера на другой цвет" }, |
||||
{ ok: false, text: "Не разбивать баннер на два разных цвета фона" }, |
||||
{ ok: false, text: "Не убирать три пункта с галочками" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
return <HeroPageClient />; |
||||
} |
||||
|
||||
@ -0,0 +1,291 @@
|
||||
"use client"; |
||||
|
||||
import { useState } from "react"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { NavigationBlock } from "@/components/blocks/NavigationBlock"; |
||||
import { NAV_ITEMS } from "@/components/blocks/navData"; |
||||
import { BlockMetaBar, type SnapshotData } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
|
||||
const BLOCK_PATH = "/blocks/navigation"; |
||||
|
||||
const LLM_NAV_TEXT = ` |
||||
БЛОК: Шапка и навигация сайта (Header) |
||||
Источник: perm.oclinica.ru/lor — реальная шапка сайта |
||||
Версия: v1.3 |
||||
|
||||
СТРУКТУРА ШАПКИ (2 зоны сверху вниз): |
||||
|
||||
1. ВЕРХНЯЯ ПАНЕЛЬ (фон #fff, без рамок) |
||||
Три столбца: |
||||
— Столбец 1 (лево): Логотип клиники (logo-main.png, h-20) |
||||
— Столбец 2 (центр): Три ссылки вертикально с иконкой 📍: |
||||
· К. Цеткин, 9 (цвет #52b4bd, подчёркнутая) |
||||
· Клиника лечения кашля и аллергии (цвет #52b4bd, подчёркнутая) |
||||
· Центр диагностики и реабилитации (цвет #52b4bd, подчёркнутая) |
||||
— Столбец 3 (право): Телефон «/342/ 255-53-84» (25px, bold, #000) + кнопка «Заказать звонок» (bb-btn-md bb-btn-pill) |
||||
|
||||
2. ГЛАВНОЕ МЕНЮ (~46px, фон #fff, border-top 1px solid #e5e7eb) |
||||
Пункты (8 штук): Клиника | ЛОР врачи | Заболевания | Вопрос-ответ | ЛОР операции | Сурдология | Цены | Контакты |
||||
Шрифт: 18px, weight 400 |
||||
Цвет ссылок: #000 |
||||
Активный / Hover: #0089c3 |
||||
Разделители: border-right 1px solid #f3f4f6 между пунктами |
||||
|
||||
ПРАВИЛА: |
||||
✓ Логотип всегда кликабелен — ведёт на главную страницу раздела |
||||
✓ Кнопка «Заказать звонок» всегда видна, pill-стиль |
||||
✓ Телефон кликабелен (tel: ссылка) |
||||
✓ Активный пункт меню — цвет #0089c3, остальные #000 |
||||
✕ Не добавлять пункты меню, которых нет на сайте |
||||
✕ Не менять порядок пунктов меню |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.3", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Подменю: выпадающие списки при hover для Клиника, Заболевания, Вопрос-ответ, ЛОР операции", |
||||
"Hover-эффект: бежевый фон #f5f0e6 при наведении на пункт меню", |
||||
"Пункты меню подчёркнуты (underline), без разделителей между ними", |
||||
"Убрана рамка между шапкой и меню (border-top)", |
||||
"Все пункты меню чёрного цвета #000 (было: первый #0089c3)", |
||||
"Уменьшен padding пунктов (px-2.5 вместо px-4) — все помещаются в ширину экрана", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.2", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Убрана рамка и тень вокруг шапки (как на реальном сайте)", |
||||
"Структура: 3 столбца (логотип | ссылки вертикально | телефон+кнопка) вместо горизонтального ряда", |
||||
"Логотип: реальный logo-main.png вместо кружка, размер h-20 (было h-12)", |
||||
"Кнопка: bb-btn-md (было bb-btn-sm)", |
||||
"Анатомия шапки: обновлена — 2 зоны вместо 3", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Адрес: «Б. Цитная, 3» заменён на «К. Цеткин, 9»", |
||||
"Ссылки: обновлены на «Клиника лечения кашля и аллергии» и «Центр диагностики и реабилитации»", |
||||
"Телефон: размер 25px (было ~14px)", |
||||
"Меню: font-size 18px (было 14px), цвет ссылок #000 (было #53514e)", |
||||
"Цвет ссылки адреса: #52b4bd (было #0089c3)", |
||||
"«ЛОР операции» без дефиса (было «ЛОР-операции»)", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function NavigationPageClient() { |
||||
const [snapshot, setSnapshot] = useState<SnapshotData | null>(null); |
||||
|
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Шапка и навигация |
||||
</h1> |
||||
<BlockMetaBar |
||||
path={BLOCK_PATH} |
||||
defaultVersion="v1.3" |
||||
defaultIsInPreview={true} |
||||
defaultChangelog={CHANGELOG} |
||||
onSnapshotSelect={setSnapshot} |
||||
/> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Точное воспроизведение шапки с perm.oclinica.ru/lor. Три зоны: топ-бар, логотип, главное меню. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
{snapshot ? ( |
||||
<div className="rounded-xl" style={{ paddingBottom: 40 }}> |
||||
<style dangerouslySetInnerHTML={{ __html: snapshot.css }} /> |
||||
<div dangerouslySetInnerHTML={{ __html: snapshot.html }} /> |
||||
</div> |
||||
) : ( |
||||
<div data-block-capture={BLOCK_PATH} className="rounded-xl" style={{ paddingBottom: 40 }}> |
||||
<NavigationBlock /> |
||||
</div> |
||||
)} |
||||
</section> |
||||
|
||||
{/* Анатомия */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Анатомия шапки |
||||
</h2> |
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> |
||||
{[ |
||||
{ |
||||
zone: "1. Верхняя панель", |
||||
bg: "#f0f9ff", |
||||
desc: "Три столбца: логотип (logo-main.png) | три ссылки вертикально с 📍 (К. Цеткин 9, Клиника кашля, Центр диагностики) | телефон 25px + кнопка «Заказать звонок» (pill)", |
||||
details: "Фон: #fff · Без рамок · Ссылки: #52b4bd, подчёркнутые", |
||||
}, |
||||
{ |
||||
zone: "2. Главное меню", |
||||
bg: "#fefce8", |
||||
desc: "8 горизонтальных пунктов: Клиника | ЛОР врачи | Заболевания | Вопрос-ответ | ЛОР операции | Сурдология | Цены | Контакты", |
||||
details: "Фон: #fff · border-top · 18px · Активный: #0089c3, остальные: #000", |
||||
}, |
||||
].map((z) => ( |
||||
<div |
||||
key={z.zone} |
||||
className="rounded-xl p-4 space-y-2" |
||||
style={{ background: z.bg, border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<p className="font-semibold text-sm" style={{ color: "var(--bb-text)" }}> |
||||
{z.zone} |
||||
</p> |
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> |
||||
{z.desc} |
||||
</p> |
||||
<p className="text-[11px] font-mono mt-1" style={{ color: "var(--bb-text-muted)" }}> |
||||
{z.details} |
||||
</p> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Пункты меню */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Пункты главного меню |
||||
</h2> |
||||
<div className="rounded-xl border overflow-visible" style={{ borderColor: "var(--bb-border)", background: "#fff" }}> |
||||
<div className="flex items-center justify-center flex-wrap" style={{ borderTop: "1px solid #e5e7eb" }}> |
||||
{NAV_ITEMS.map((item) => ( |
||||
<div key={item.label} className="bb-nav-item-wrap relative group"> |
||||
<a |
||||
href="#" |
||||
className="bb-nav-item px-2.5 py-3 whitespace-nowrap inline-flex items-center gap-1" |
||||
style={{ |
||||
fontSize: 18, |
||||
color: "#000", |
||||
fontWeight: 400, |
||||
textDecoration: "underline", |
||||
textUnderlineOffset: "4px", |
||||
}} |
||||
> |
||||
{item.label} |
||||
{item.submenu.length > 0 && ( |
||||
<span className="text-xs" style={{ color: "#9ca3af", textDecoration: "none", display: "inline-block" }}>▾</span> |
||||
)} |
||||
</a> |
||||
{item.submenu.length > 0 && ( |
||||
<div |
||||
className="absolute left-0 top-full z-50 hidden group-hover:block min-w-[220px] py-1 rounded shadow-lg" |
||||
style={{ background: "#fff", border: "1px solid #e5e7eb" }} |
||||
> |
||||
{item.submenu.map((sub) => ( |
||||
<a |
||||
key={sub} |
||||
href="#" |
||||
className="block px-4 py-2 text-sm bb-nav-sub-item" |
||||
style={{ color: "#333", textDecoration: "none" }} |
||||
> |
||||
{sub} |
||||
</a> |
||||
))} |
||||
</div> |
||||
)} |
||||
</div> |
||||
))} |
||||
</div> |
||||
</div> |
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> |
||||
Порядок фиксирован. Все пункты чёрные (#000), подчёркнуты. Hover — бежевый фон #f5f0e6. ▾ — выпадающее подменю при наведении. |
||||
</p> |
||||
</section> |
||||
|
||||
{/* Токены */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Цвета и токены |
||||
</h2> |
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3"> |
||||
{[ |
||||
{ label: "Фон шапки", value: "#ffffff", token: "—" }, |
||||
{ label: "Пункты меню", value: "#000000", token: "—" }, |
||||
{ label: "Hover фон меню", value: "#f5f0e6", token: "bb-nav-item:hover" }, |
||||
{ label: "Ссылки адресов", value: "#52b4bd", token: "—" }, |
||||
{ label: "Кнопка «Заказать»", value: "#e9e4d4", token: "bb-btn-pill" }, |
||||
{ label: "Подменю фон", value: "#ffffff", token: "—" }, |
||||
{ label: "Подменю hover", value: "#f5f0e6", token: "bb-nav-sub-item:hover" }, |
||||
{ label: "Телефон", value: "#000000", token: "25px, bold" }, |
||||
].map((t) => ( |
||||
<div |
||||
key={t.label} |
||||
className="p-3 rounded-xl space-y-2" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<div |
||||
className="w-8 h-8 rounded border" |
||||
style={{ background: t.value, borderColor: "var(--bb-border)" }} |
||||
/> |
||||
<p className="text-xs font-medium" style={{ color: "var(--bb-text)" }}> |
||||
{t.label} |
||||
</p> |
||||
<p className="text-[10px] font-mono" style={{ color: "var(--bb-text-muted)" }}> |
||||
{t.value} |
||||
</p> |
||||
<p className="text-[10px]" style={{ color: "var(--bb-text-muted)" }}> |
||||
{t.token} |
||||
</p> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/navigation" version="v1.3" specText={LLM_NAV_TEXT}> |
||||
<LlmSection title="Зоны шапки" /> |
||||
<LlmTable |
||||
headers={["Зона", "Фон", "Содержимое"]} |
||||
rows={[ |
||||
["Верхняя панель", "#fff, без рамок", "Логотип (logo-main.png) | 3 ссылки вертикально с 📍 (#52b4bd) | Телефон 25px + кнопка pill"], |
||||
["Главное меню", "#fff, border-top", "8 пунктов 18px: Клиника / ЛОР врачи / Заболевания / Вопрос-ответ / ЛОР операции / Сурдология / Цены / Контакты"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Цвета" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Цвет", "Токен"]} |
||||
rows={[ |
||||
["Фон шапки", "#ffffff", "—"], |
||||
["Ссылки меню (default)", "#000000", "—"], |
||||
["Активный пункт / hover", "#0089c3", "--brand-053m"], |
||||
["Кнопка «Заказать звонок»", "#e9e4d4 / border #d5cfbd", "bb-btn-pill"], |
||||
["Телефон", "#111827, font-weight bold", "—"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "Логотип ведёт на главную страницу раздела" }, |
||||
{ ok: true, text: "Кнопка «Заказать звонок» всегда видна, pill-стиль" }, |
||||
{ ok: true, text: "Телефон — кликабельная ссылка tel:" }, |
||||
{ ok: true, text: "Активный пункт меню — цвет #0089c3" }, |
||||
{ ok: false, text: "Не добавлять пункты меню, которых нет на сайте" }, |
||||
{ ok: false, text: "Не менять порядок 8 пунктов меню" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
} |
||||
@ -1,276 +1,10 @@
|
||||
import type { Metadata } from "next"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { NavigationBlock } from "@/components/blocks/NavigationBlock"; |
||||
import { NAV_ITEMS } from "@/components/blocks/navData"; |
||||
import { BlockMetaBar } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import NavigationPageClient from "./NavigationPageClient"; |
||||
|
||||
export const metadata: Metadata = { |
||||
title: "Шапка и навигация. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой", |
||||
}; |
||||
|
||||
const LLM_NAV_TEXT = ` |
||||
БЛОК: Шапка и навигация сайта (Header) |
||||
Источник: perm.oclinica.ru/lor — реальная шапка сайта |
||||
Версия: v1.3 |
||||
|
||||
СТРУКТУРА ШАПКИ (2 зоны сверху вниз): |
||||
|
||||
1. ВЕРХНЯЯ ПАНЕЛЬ (фон #fff, без рамок) |
||||
Три столбца: |
||||
— Столбец 1 (лево): Логотип клиники (logo-main.png, h-20) |
||||
— Столбец 2 (центр): Три ссылки вертикально с иконкой 📍: |
||||
· К. Цеткин, 9 (цвет #52b4bd, подчёркнутая) |
||||
· Клиника лечения кашля и аллергии (цвет #52b4bd, подчёркнутая) |
||||
· Центр диагностики и реабилитации (цвет #52b4bd, подчёркнутая) |
||||
— Столбец 3 (право): Телефон «/342/ 255-53-84» (25px, bold, #000) + кнопка «Заказать звонок» (bb-btn-md bb-btn-pill) |
||||
|
||||
2. ГЛАВНОЕ МЕНЮ (~46px, фон #fff, border-top 1px solid #e5e7eb) |
||||
Пункты (8 штук): Клиника | ЛОР врачи | Заболевания | Вопрос-ответ | ЛОР операции | Сурдология | Цены | Контакты |
||||
Шрифт: 18px, weight 400 |
||||
Цвет ссылок: #000 |
||||
Активный / Hover: #0089c3 |
||||
Разделители: border-right 1px solid #f3f4f6 между пунктами |
||||
|
||||
ПРАВИЛА: |
||||
✓ Логотип всегда кликабелен — ведёт на главную страницу раздела |
||||
✓ Кнопка «Заказать звонок» всегда видна, pill-стиль |
||||
✓ Телефон кликабелен (tel: ссылка) |
||||
✓ Активный пункт меню — цвет #0089c3, остальные #000 |
||||
✕ Не добавлять пункты меню, которых нет на сайте |
||||
✕ Не менять порядок пунктов меню |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.3", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Подменю: выпадающие списки при hover для Клиника, Заболевания, Вопрос-ответ, ЛОР операции", |
||||
"Hover-эффект: бежевый фон #f5f0e6 при наведении на пункт меню", |
||||
"Пункты меню подчёркнуты (underline), без разделителей между ними", |
||||
"Убрана рамка между шапкой и меню (border-top)", |
||||
"Все пункты меню чёрного цвета #000 (было: первый #0089c3)", |
||||
"Уменьшен padding пунктов (px-2.5 вместо px-4) — все помещаются в ширину экрана", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.2", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Убрана рамка и тень вокруг шапки (как на реальном сайте)", |
||||
"Структура: 3 столбца (логотип | ссылки вертикально | телефон+кнопка) вместо горизонтального ряда", |
||||
"Логотип: реальный logo-main.png вместо кружка, размер h-20 (было h-12)", |
||||
"Кнопка: bb-btn-md (было bb-btn-sm)", |
||||
"Анатомия шапки: обновлена — 2 зоны вместо 3", |
||||
], |
||||
}, |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"Адрес: «Б. Цитная, 3» заменён на «К. Цеткин, 9»", |
||||
"Ссылки: обновлены на «Клиника лечения кашля и аллергии» и «Центр диагностики и реабилитации»", |
||||
"Телефон: размер 25px (было ~14px)", |
||||
"Меню: font-size 18px (было 14px), цвет ссылок #000 (было #53514e)", |
||||
"Цвет ссылки адреса: #52b4bd (было #0089c3)", |
||||
"«ЛОР операции» без дефиса (было «ЛОР-операции»)", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function NavigationPage() { |
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Шапка и навигация |
||||
</h1> |
||||
<BlockMetaBar path="/blocks/navigation" defaultVersion="v1.3" defaultIsInPreview={true} defaultChangelog={CHANGELOG} /> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Точное воспроизведение шапки с perm.oclinica.ru/lor. Три зоны: топ-бар, логотип, главное меню. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
<div className="rounded-xl" style={{ paddingBottom: 40 }}> |
||||
<NavigationBlock /> |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Анатомия */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Анатомия шапки |
||||
</h2> |
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> |
||||
{[ |
||||
{ |
||||
zone: "1. Верхняя панель", |
||||
bg: "#f0f9ff", |
||||
desc: "Три столбца: логотип (logo-main.png) | три ссылки вертикально с 📍 (К. Цеткин 9, Клиника кашля, Центр диагностики) | телефон 25px + кнопка «Заказать звонок» (pill)", |
||||
details: "Фон: #fff · Без рамок · Ссылки: #52b4bd, подчёркнутые", |
||||
}, |
||||
{ |
||||
zone: "2. Главное меню", |
||||
bg: "#fefce8", |
||||
desc: "8 горизонтальных пунктов: Клиника | ЛОР врачи | Заболевания | Вопрос-ответ | ЛОР операции | Сурдология | Цены | Контакты", |
||||
details: "Фон: #fff · border-top · 18px · Активный: #0089c3, остальные: #000", |
||||
}, |
||||
].map((z) => ( |
||||
<div |
||||
key={z.zone} |
||||
className="rounded-xl p-4 space-y-2" |
||||
style={{ background: z.bg, border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<p className="font-semibold text-sm" style={{ color: "var(--bb-text)" }}> |
||||
{z.zone} |
||||
</p> |
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> |
||||
{z.desc} |
||||
</p> |
||||
<p className="text-[11px] font-mono mt-1" style={{ color: "var(--bb-text-muted)" }}> |
||||
{z.details} |
||||
</p> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Пункты меню */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Пункты главного меню |
||||
</h2> |
||||
<div className="rounded-xl border overflow-visible" style={{ borderColor: "var(--bb-border)", background: "#fff" }}> |
||||
<div className="flex items-center justify-center flex-wrap" style={{ borderTop: "1px solid #e5e7eb" }}> |
||||
{NAV_ITEMS.map((item, i) => ( |
||||
<div key={item.label} className="bb-nav-item-wrap relative group"> |
||||
<a |
||||
href="#" |
||||
className="bb-nav-item px-2.5 py-3 whitespace-nowrap inline-flex items-center gap-1" |
||||
style={{ |
||||
fontSize: 18, |
||||
color: "#000", |
||||
fontWeight: 400, |
||||
textDecoration: "underline", |
||||
textUnderlineOffset: "4px", |
||||
}} |
||||
> |
||||
{item.label} |
||||
{item.submenu.length > 0 && ( |
||||
<span className="text-xs" style={{ color: "#9ca3af", textDecoration: "none", display: "inline-block" }}>▾</span> |
||||
)} |
||||
</a> |
||||
{item.submenu.length > 0 && ( |
||||
<div |
||||
className="absolute left-0 top-full z-50 hidden group-hover:block min-w-[220px] py-1 rounded shadow-lg" |
||||
style={{ background: "#fff", border: "1px solid #e5e7eb" }} |
||||
> |
||||
{item.submenu.map((sub) => ( |
||||
<a |
||||
key={sub} |
||||
href="#" |
||||
className="block px-4 py-2 text-sm bb-nav-sub-item" |
||||
style={{ color: "#333", textDecoration: "none" }} |
||||
> |
||||
{sub} |
||||
</a> |
||||
))} |
||||
</div> |
||||
)} |
||||
</div> |
||||
))} |
||||
</div> |
||||
</div> |
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> |
||||
Порядок фиксирован. Все пункты чёрные (#000), подчёркнуты. Hover — бежевый фон #f5f0e6. ▾ — выпадающее подменю при наведении. |
||||
</p> |
||||
</section> |
||||
|
||||
{/* Токены */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Цвета и токены |
||||
</h2> |
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3"> |
||||
{[ |
||||
{ label: "Фон шапки", value: "#ffffff", token: "—" }, |
||||
{ label: "Пункты меню", value: "#000000", token: "—" }, |
||||
{ label: "Hover фон меню", value: "#f5f0e6", token: "bb-nav-item:hover" }, |
||||
{ label: "Ссылки адресов", value: "#52b4bd", token: "—" }, |
||||
{ label: "Кнопка «Заказать»", value: "#e9e4d4", token: "bb-btn-pill" }, |
||||
{ label: "Подменю фон", value: "#ffffff", token: "—" }, |
||||
{ label: "Подменю hover", value: "#f5f0e6", token: "bb-nav-sub-item:hover" }, |
||||
{ label: "Телефон", value: "#000000", token: "25px, bold" }, |
||||
].map((t) => ( |
||||
<div |
||||
key={t.label} |
||||
className="p-3 rounded-xl space-y-2" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<div |
||||
className="w-8 h-8 rounded border" |
||||
style={{ background: t.value, borderColor: "var(--bb-border)" }} |
||||
/> |
||||
<p className="text-xs font-medium" style={{ color: "var(--bb-text)" }}> |
||||
{t.label} |
||||
</p> |
||||
<p className="text-[10px] font-mono" style={{ color: "var(--bb-text-muted)" }}> |
||||
{t.value} |
||||
</p> |
||||
<p className="text-[10px]" style={{ color: "var(--bb-text-muted)" }}> |
||||
{t.token} |
||||
</p> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/navigation" version="v1.3" specText={LLM_NAV_TEXT}> |
||||
<LlmSection title="Зоны шапки" /> |
||||
<LlmTable |
||||
headers={["Зона", "Фон", "Содержимое"]} |
||||
rows={[ |
||||
["Верхняя панель", "#fff, без рамок", "Логотип (logo-main.png) | 3 ссылки вертикально с 📍 (#52b4bd) | Телефон 25px + кнопка pill"], |
||||
["Главное меню", "#fff, border-top", "8 пунктов 18px: Клиника / ЛОР врачи / Заболевания / Вопрос-ответ / ЛОР операции / Сурдология / Цены / Контакты"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Цвета" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Цвет", "Токен"]} |
||||
rows={[ |
||||
["Фон шапки", "#ffffff", "—"], |
||||
["Ссылки меню (default)", "#000000", "—"], |
||||
["Активный пункт / hover", "#0089c3", "--brand-053m"], |
||||
["Кнопка «Заказать звонок»", "#e9e4d4 / border #d5cfbd", "bb-btn-pill"], |
||||
["Телефон", "#111827, font-weight bold", "—"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "Логотип ведёт на главную страницу раздела" }, |
||||
{ ok: true, text: "Кнопка «Заказать звонок» всегда видна, pill-стиль" }, |
||||
{ ok: true, text: "Телефон — кликабельная ссылка tel:" }, |
||||
{ ok: true, text: "Активный пункт меню — цвет #0089c3" }, |
||||
{ ok: false, text: "Не добавлять пункты меню, которых нет на сайте" }, |
||||
{ ok: false, text: "Не менять порядок 8 пунктов меню" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
return <NavigationPageClient />; |
||||
} |
||||
|
||||
@ -0,0 +1,195 @@
|
||||
"use client"; |
||||
|
||||
import { useState } from "react"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { BlockMetaBar, type SnapshotData } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import { NewsBlock } from "@/components/blocks/NewsBlock"; |
||||
|
||||
const BLOCK_PATH = "/blocks/news"; |
||||
|
||||
const MOCK_NEWS = [ |
||||
{ |
||||
date: "20.12.2025", |
||||
title: "Наша работа клиники и новогодние праздники", |
||||
href: "#", |
||||
}, |
||||
{ |
||||
date: "11.08.2025", |
||||
title: "СЕРВИС ОБНОВЛЕНИЕ: Обновление графика работы клиники", |
||||
href: "#", |
||||
}, |
||||
{ |
||||
date: "12.06.2025", |
||||
title: "СЕРВИС ОБНОВЛЕНИЕ: Временное изменение работы клиники 22.06.25 г.", |
||||
href: "#", |
||||
}, |
||||
{ |
||||
date: "11.06.2025", |
||||
title: "График работы клиники в ближайшие праздники", |
||||
href: "#", |
||||
}, |
||||
]; |
||||
|
||||
const LLM_NEWS_TEXT = ` |
||||
БЛОК: Новости |
||||
Источник: perm.oclinica.ru/lor — блок новостей внизу страницы |
||||
Версия: v1.1 |
||||
|
||||
СТРУКТУРА БЛОКА: |
||||
1. ЗАГОЛОВОК H2: «Новости» |
||||
Выравнивание: по центру или слева |
||||
|
||||
2. СЕТКА КАРТОЧЕК НОВОСТЕЙ (4 штуки в ряд): |
||||
Каждая карточка: |
||||
— Дата: малый текст сверху (формат DD.MM.YYYY), цвет #6b7280 |
||||
— Заголовок новости: синяя ссылка (#0089c3), 14px, font-weight 500, hover underline |
||||
— Без превью-изображения |
||||
— Без описания (только дата + заголовок-ссылка) |
||||
Hover на карточке: bg #eef4d1, box-shadow 0 0 16px 0 #9e9e9a (bb-news-card) |
||||
|
||||
3. КНОПКА «Все новости»: |
||||
Стиль: outline (#BF9975) или teal (#60959c), выровнена по центру под сеткой |
||||
На сайте: ссылка «Все новости» |
||||
|
||||
ЦВЕТА: |
||||
Дата: #6b7280 |
||||
Заголовок-ссылка: #0089c3 (--brand-053m) |
||||
Hover фон карточки: #eef4d1 |
||||
Hover тень: 0 0 16px 0 #9e9e9a |
||||
|
||||
ПРАВИЛА: |
||||
✓ 4 карточки в ряд |
||||
✓ Дата сверху карточки |
||||
✓ Заголовок = ссылка #0089c3 |
||||
✓ Hover: bg #eef4d1 (класс bb-news-card) |
||||
✓ Кнопка «Все новости» под сеткой |
||||
✕ Не добавлять изображения в карточки новостей главной страницы |
||||
✕ Не добавлять описание/анонс в карточку |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"H2: размер на 36px, цвет на #000000, line-height 38px", |
||||
"Фон секции: с белого на #f2fee6 (светло-зелёный, как на реальном сайте)", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function NewsPageClient() { |
||||
const [snapshot, setSnapshot] = useState<SnapshotData | null>(null); |
||||
|
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Блок «Новости» |
||||
</h1> |
||||
<BlockMetaBar |
||||
path={BLOCK_PATH} |
||||
defaultVersion="v1.1" |
||||
defaultIsInPreview={false} |
||||
defaultChangelog={CHANGELOG} |
||||
onSnapshotSelect={setSnapshot} |
||||
/> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Блок новостей с perm.oclinica.ru/lor — 4 карточки в ряд (дата + заголовок-ссылка), |
||||
кнопка «Все новости». |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
{snapshot ? ( |
||||
<> |
||||
<style dangerouslySetInnerHTML={{ __html: snapshot.css }} /> |
||||
<div dangerouslySetInnerHTML={{ __html: snapshot.html }} /> |
||||
</> |
||||
) : ( |
||||
<div data-block-capture={BLOCK_PATH}> |
||||
<NewsBlock /> |
||||
</div> |
||||
)} |
||||
</section> |
||||
|
||||
{/* Анатомия карточки */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Анатомия карточки новости |
||||
</h2> |
||||
<div |
||||
className="rounded-xl p-6 max-w-xs" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<div className="space-y-3"> |
||||
<div |
||||
className="flex items-center gap-2 px-2 py-1 rounded text-xs font-mono" |
||||
style={{ background: "#f0f9ff", color: "var(--bb-text-muted)" }} |
||||
> |
||||
① Дата: DD.MM.YYYY · цвет #6b7280 |
||||
</div> |
||||
<div |
||||
className="flex items-center gap-2 px-2 py-1 rounded text-xs font-mono" |
||||
style={{ background: "#f0f9ff", color: "var(--bb-text-muted)" }} |
||||
> |
||||
② Заголовок-ссылка · #0089c3 · font-weight 500 |
||||
</div> |
||||
<div |
||||
className="flex items-center gap-2 px-2 py-1 rounded text-xs font-mono" |
||||
style={{ background: "#fefce8", color: "var(--bb-text-muted)" }} |
||||
> |
||||
Hover: bg #eef4d1 · shadow 0 0 16px #9e9e9a |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> |
||||
Нет изображений, нет описания — только дата и заголовок. |
||||
CSS-класс <code className="font-mono">.bb-news-card</code> обрабатывает hover. |
||||
</p> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/news" version="v1.1" specText={LLM_NEWS_TEXT}> |
||||
<LlmSection title="Структура карточки новости" /> |
||||
<LlmTable |
||||
headers={["Поле", "Стиль", "Значение"]} |
||||
rows={[ |
||||
["Дата", "text-xs, #6b7280", "Формат DD.MM.YYYY"], |
||||
["Заголовок", "text-sm, font-weight 500, #0089c3", "Ссылка на новость"], |
||||
["Hover фон", ".bb-news-card:hover", "#eef4d1"], |
||||
["Hover тень", ".bb-news-card:hover", "0 0 16px 0 #9e9e9a"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Mock-данные (4 реальные новости с сайта)" /> |
||||
<LlmTable |
||||
headers={["Дата", "Заголовок"]} |
||||
rows={MOCK_NEWS.map((n) => [n.date, n.title])} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "4 карточки в ряд (grid-cols-4)" }, |
||||
{ ok: true, text: "Дата сверху, заголовок-ссылка ниже" }, |
||||
{ ok: true, text: "Hover: класс bb-news-card (bg #eef4d1)" }, |
||||
{ ok: true, text: "Кнопка «Все новости» под сеткой (bb-btn-outline)" }, |
||||
{ ok: false, text: "Не добавлять изображения в карточки" }, |
||||
{ ok: false, text: "Не добавлять текст-анонс в карточку" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
} |
||||
@ -1,178 +1,10 @@
|
||||
import type { Metadata } from "next"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { BlockMetaBar } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import { NewsBlock } from "@/components/blocks/NewsBlock"; |
||||
import NewsPageClient from "./NewsPageClient"; |
||||
|
||||
export const metadata: Metadata = { |
||||
title: "Блок «Новости». Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой", |
||||
}; |
||||
|
||||
const MOCK_NEWS = [ |
||||
{ |
||||
date: "20.12.2025", |
||||
title: "Наша работа клиники и новогодние праздники", |
||||
href: "#", |
||||
}, |
||||
{ |
||||
date: "11.08.2025", |
||||
title: "СЕРВИС ОБНОВЛЕНИЕ: Обновление графика работы клиники", |
||||
href: "#", |
||||
}, |
||||
{ |
||||
date: "12.06.2025", |
||||
title: "СЕРВИС ОБНОВЛЕНИЕ: Временное изменение работы клиники 22.06.25 г.", |
||||
href: "#", |
||||
}, |
||||
{ |
||||
date: "11.06.2025", |
||||
title: "График работы клиники в ближайшие праздники", |
||||
href: "#", |
||||
}, |
||||
]; |
||||
|
||||
const LLM_NEWS_TEXT = ` |
||||
БЛОК: Новости |
||||
Источник: perm.oclinica.ru/lor — блок новостей внизу страницы |
||||
Версия: v1.1 |
||||
|
||||
СТРУКТУРА БЛОКА: |
||||
1. ЗАГОЛОВОК H2: «Новости» |
||||
Выравнивание: по центру или слева |
||||
|
||||
2. СЕТКА КАРТОЧЕК НОВОСТЕЙ (4 штуки в ряд): |
||||
Каждая карточка: |
||||
— Дата: малый текст сверху (формат DD.MM.YYYY), цвет #6b7280 |
||||
— Заголовок новости: синяя ссылка (#0089c3), 14px, font-weight 500, hover underline |
||||
— Без превью-изображения |
||||
— Без описания (только дата + заголовок-ссылка) |
||||
Hover на карточке: bg #eef4d1, box-shadow 0 0 16px 0 #9e9e9a (bb-news-card) |
||||
|
||||
3. КНОПКА «Все новости»: |
||||
Стиль: outline (#BF9975) или teal (#60959c), выровнена по центру под сеткой |
||||
На сайте: ссылка «Все новости» |
||||
|
||||
ЦВЕТА: |
||||
Дата: #6b7280 |
||||
Заголовок-ссылка: #0089c3 (--brand-053m) |
||||
Hover фон карточки: #eef4d1 |
||||
Hover тень: 0 0 16px 0 #9e9e9a |
||||
|
||||
ПРАВИЛА: |
||||
✓ 4 карточки в ряд |
||||
✓ Дата сверху карточки |
||||
✓ Заголовок = ссылка #0089c3 |
||||
✓ Hover: bg #eef4d1 (класс bb-news-card) |
||||
✓ Кнопка «Все новости» под сеткой |
||||
✕ Не добавлять изображения в карточки новостей главной страницы |
||||
✕ Не добавлять описание/анонс в карточку |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"H2: размер на 36px, цвет на #000000, line-height 38px", |
||||
"Фон секции: с белого на #f2fee6 (светло-зелёный, как на реальном сайте)", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function NewsBlockPage() { |
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Блок «Новости» |
||||
</h1> |
||||
<BlockMetaBar path="/blocks/news" defaultVersion="v1.1" defaultIsInPreview={false} defaultChangelog={CHANGELOG} /> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Блок новостей с perm.oclinica.ru/lor — 4 карточки в ряд (дата + заголовок-ссылка), |
||||
кнопка «Все новости». |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
<NewsBlock /> |
||||
</section> |
||||
|
||||
{/* Анатомия карточки */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Анатомия карточки новости |
||||
</h2> |
||||
<div |
||||
className="rounded-xl p-6 max-w-xs" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<div className="space-y-3"> |
||||
<div |
||||
className="flex items-center gap-2 px-2 py-1 rounded text-xs font-mono" |
||||
style={{ background: "#f0f9ff", color: "var(--bb-text-muted)" }} |
||||
> |
||||
① Дата: DD.MM.YYYY · цвет #6b7280 |
||||
</div> |
||||
<div |
||||
className="flex items-center gap-2 px-2 py-1 rounded text-xs font-mono" |
||||
style={{ background: "#f0f9ff", color: "var(--bb-text-muted)" }} |
||||
> |
||||
② Заголовок-ссылка · #0089c3 · font-weight 500 |
||||
</div> |
||||
<div |
||||
className="flex items-center gap-2 px-2 py-1 rounded text-xs font-mono" |
||||
style={{ background: "#fefce8", color: "var(--bb-text-muted)" }} |
||||
> |
||||
Hover: bg #eef4d1 · shadow 0 0 16px #9e9e9a |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> |
||||
Нет изображений, нет описания — только дата и заголовок. |
||||
CSS-класс <code className="font-mono">.bb-news-card</code> обрабатывает hover. |
||||
</p> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/news" version="v1.1" specText={LLM_NEWS_TEXT}> |
||||
<LlmSection title="Структура карточки новости" /> |
||||
<LlmTable |
||||
headers={["Поле", "Стиль", "Значение"]} |
||||
rows={[ |
||||
["Дата", "text-xs, #6b7280", "Формат DD.MM.YYYY"], |
||||
["Заголовок", "text-sm, font-weight 500, #0089c3", "Ссылка на новость"], |
||||
["Hover фон", ".bb-news-card:hover", "#eef4d1"], |
||||
["Hover тень", ".bb-news-card:hover", "0 0 16px 0 #9e9e9a"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Mock-данные (4 реальные новости с сайта)" /> |
||||
<LlmTable |
||||
headers={["Дата", "Заголовок"]} |
||||
rows={MOCK_NEWS.map((n) => [n.date, n.title])} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "4 карточки в ряд (grid-cols-4)" }, |
||||
{ ok: true, text: "Дата сверху, заголовок-ссылка ниже" }, |
||||
{ ok: true, text: "Hover: класс bb-news-card (bg #eef4d1)" }, |
||||
{ ok: true, text: "Кнопка «Все новости» под сеткой (bb-btn-outline)" }, |
||||
{ ok: false, text: "Не добавлять изображения в карточки" }, |
||||
{ ok: false, text: "Не добавлять текст-анонс в карточку" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
return <NewsPageClient />; |
||||
} |
||||
|
||||
@ -0,0 +1,226 @@
|
||||
"use client"; |
||||
|
||||
import { useState } from "react"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { BlockMetaBar, type SnapshotData } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import { ReviewsBlock } from "@/components/blocks/ReviewsBlock"; |
||||
|
||||
const BLOCK_PATH = "/blocks/reviews"; |
||||
|
||||
const MOCK_REVIEWS = [ |
||||
{ |
||||
text: "Спасибо за приём, мне всё понравилось, спасибо за приём, мне всё понравилось. Врач очень внимательный и профессиональный.", |
||||
author: "Пациент клиники", |
||||
doctor: "Тимофеева Наталья Александровна", |
||||
}, |
||||
{ |
||||
text: "Очень довольна лечением! Прошла курс процедур, нос дышит отлично. Рекомендую клинику всем.", |
||||
author: "Наталья К.", |
||||
doctor: "Макарова Людмила Тимофеевна", |
||||
}, |
||||
]; |
||||
|
||||
const LLM_REVIEWS_TEXT = ` |
||||
БЛОК: Отзывы о нас |
||||
Источник: perm.oclinica.ru/lor — блок отзывов |
||||
Версия: v1.1 |
||||
|
||||
СТРУКТУРА БЛОКА: |
||||
1. ЗАГОЛОВОК H2: «Отзывы о нас» |
||||
Подзаголовок: «За 12 лет работы наши врачи оториноларингологи избавили от болезней ухо, горло, носа |
||||
более 50000 пациентов. Но дня сейчас мы высоко ценим каждый положительный отзыв и искренние слова благодарности» |
||||
|
||||
2. КАРУСЕЛЬ ОТЗЫВОВ: |
||||
— Большая открывающая кавычка (« «) в цвете #b8e6ed, font-size 80–100px |
||||
— Текст отзыва: курсив, 15–16px, #374151, background #eef4d1 |
||||
— Ссылка «Читать отзыв полностью» в цвете #0089c3 |
||||
— Стрелки навигации < > по бокам (round buttons) |
||||
— Карусель показывает 1 отзыв за раз |
||||
|
||||
ЦВЕТА: |
||||
Фон карточки отзыва: #eef4d1 (светло-жёлто-зелёный) |
||||
Кавычка: #b8e6ed (светло-бирюзовый) |
||||
Текст отзыва: #374151 |
||||
Ссылка «Читать полностью»: #0089c3 |
||||
|
||||
ПРАВИЛА: |
||||
✓ Фон карточки отзыва: #eef4d1 (тот же что у ReviewCard) |
||||
✓ Большая декоративная кавычка |
||||
✓ Ссылка «Читать отзыв полностью» обязательна |
||||
✓ Навигация карусели (стрелки) |
||||
✕ Не показывать более 1 отзыва за раз в карусели |
||||
✕ Не убирать навигационные стрелки |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"H2: размер с ~20px на 36px, цвет с #111827 на #000000, line-height 38px", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function ReviewsPageClient() { |
||||
const [snapshot, setSnapshot] = useState<SnapshotData | null>(null); |
||||
|
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Блок «Отзывы о нас» |
||||
</h1> |
||||
<BlockMetaBar |
||||
path={BLOCK_PATH} |
||||
defaultVersion="v1.1" |
||||
defaultIsInPreview={false} |
||||
defaultChangelog={CHANGELOG} |
||||
onSnapshotSelect={setSnapshot} |
||||
/> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Карусель отзывов с perm.oclinica.ru/lor — большая кавычка, текст, «Читать полностью», стрелки. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
{snapshot ? ( |
||||
<> |
||||
<style dangerouslySetInnerHTML={{ __html: snapshot.css }} /> |
||||
<div dangerouslySetInnerHTML={{ __html: snapshot.html }} /> |
||||
</> |
||||
) : ( |
||||
<div data-block-capture={BLOCK_PATH}> |
||||
<ReviewsBlock /> |
||||
</div> |
||||
)} |
||||
</section> |
||||
|
||||
{/* Несколько примеров */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Примеры отзывов (mock) |
||||
</h2> |
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> |
||||
{MOCK_REVIEWS.map((r, i) => ( |
||||
<div |
||||
key={i} |
||||
className="rounded-xl p-5 space-y-3" |
||||
style={{ background: "#eef4d1", border: "1px solid #d4e6a0" }} |
||||
> |
||||
<div |
||||
className="text-5xl leading-none font-serif" |
||||
style={{ color: "#b8e6ed" }} |
||||
> |
||||
« |
||||
</div> |
||||
<p |
||||
className="text-sm italic leading-relaxed" |
||||
style={{ color: "#374151" }} |
||||
> |
||||
{r.text} |
||||
</p> |
||||
<div className="pt-1"> |
||||
<a href="#" className="text-sm" style={{ color: "#0089c3" }}> |
||||
Читать отзыв полностью |
||||
</a> |
||||
</div> |
||||
<div className="border-t pt-2" style={{ borderColor: "#c8d8a0" }}> |
||||
<p className="text-xs font-medium" style={{ color: "#374151" }}> |
||||
{r.author} |
||||
</p> |
||||
<p className="text-[11px]" style={{ color: "#6b7280" }}> |
||||
о враче: {r.doctor} |
||||
</p> |
||||
</div> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Анатомия */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Анатомия карточки отзыва |
||||
</h2> |
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3"> |
||||
{[ |
||||
{ label: "Фон карточки", value: "#eef4d1", note: "светло-жёлто-зелёный" }, |
||||
{ label: "Кавычка декоративная", value: "#b8e6ed", note: "80–100px, font-serif" }, |
||||
{ label: "Текст отзыва", value: "#374151", note: "14px, italic" }, |
||||
{ label: "Ссылка", value: "#0089c3", note: "--brand-053m" }, |
||||
].map((t) => ( |
||||
<div |
||||
key={t.label} |
||||
className="p-3 rounded-xl space-y-1" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<div |
||||
className="w-8 h-8 rounded border" |
||||
style={{ background: t.value, borderColor: "var(--bb-border)" }} |
||||
/> |
||||
<p className="text-xs font-medium" style={{ color: "var(--bb-text)" }}> |
||||
{t.label} |
||||
</p> |
||||
<p className="text-[10px] font-mono" style={{ color: "var(--bb-text-muted)" }}> |
||||
{t.value} |
||||
</p> |
||||
<p className="text-[10px]" style={{ color: "var(--bb-text-muted)" }}> |
||||
{t.note} |
||||
</p> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/reviews" version="v1.1" specText={LLM_REVIEWS_TEXT}> |
||||
<LlmSection title="Структура блока" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Содержимое", "Стиль"]} |
||||
rows={[ |
||||
["H2", "«Отзывы о нас»", "36px, font-bold, #000000, line-height 38px"], |
||||
["Подзаголовок", "Описание достижений клиники за 12 лет", "14px, #374151"], |
||||
["Кавычка", "Декоративная «", "80–100px, #b8e6ed, font-serif"], |
||||
["Текст отзыва", "Полный текст отзыва пациента", "14px, italic, #374151"], |
||||
["Ссылка", "«Читать отзыв полностью»", "#0089c3"], |
||||
["Стрелки карусели", "‹ ›", "Round buttons, фон var(--bb-sidebar-bg)"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Цвета" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Цвет", "Токен"]} |
||||
rows={[ |
||||
["Фон карточки отзыва", "#eef4d1", "—"], |
||||
["Декоративная кавычка", "#b8e6ed", "—"], |
||||
["Текст отзыва", "#374151", "—"], |
||||
["Ссылка", "#0089c3", "--brand-053m"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "Фон карточки отзыва: #eef4d1" }, |
||||
{ ok: true, text: "Декоративная кавычка цвет #b8e6ed" }, |
||||
{ ok: true, text: "Ссылка «Читать отзыв полностью» обязательна" }, |
||||
{ ok: true, text: "Навигация карусели: стрелки ‹ ›" }, |
||||
{ ok: false, text: "Не показывать несколько отзывов одновременно" }, |
||||
{ ok: false, text: "Не убирать навигационные стрелки" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
} |
||||
@ -1,209 +1,10 @@
|
||||
import type { Metadata } from "next"; |
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; |
||||
import { BlockMetaBar } from "@/components/ui/BlockMetaBar"; |
||||
import { type ChangelogEntry } from "@/components/ui/BlockChangelog"; |
||||
import { ReviewsBlock } from "@/components/blocks/ReviewsBlock"; |
||||
import ReviewsPageClient from "./ReviewsPageClient"; |
||||
|
||||
export const metadata: Metadata = { |
||||
title: "Блок «Отзывы». Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой", |
||||
}; |
||||
|
||||
const MOCK_REVIEWS = [ |
||||
{ |
||||
text: "Спасибо за приём, мне всё понравилось, спасибо за приём, мне всё понравилось. Врач очень внимательный и профессиональный.", |
||||
author: "Пациент клиники", |
||||
doctor: "Тимофеева Наталья Александровна", |
||||
}, |
||||
{ |
||||
text: "Очень довольна лечением! Прошла курс процедур, нос дышит отлично. Рекомендую клинику всем.", |
||||
author: "Наталья К.", |
||||
doctor: "Макарова Людмила Тимофеевна", |
||||
}, |
||||
]; |
||||
|
||||
const LLM_REVIEWS_TEXT = ` |
||||
БЛОК: Отзывы о нас |
||||
Источник: perm.oclinica.ru/lor — блок отзывов |
||||
Версия: v1.1 |
||||
|
||||
СТРУКТУРА БЛОКА: |
||||
1. ЗАГОЛОВОК H2: «Отзывы о нас» |
||||
Подзаголовок: «За 12 лет работы наши врачи оториноларингологи избавили от болезней ухо, горло, носа |
||||
более 50000 пациентов. Но дня сейчас мы высоко ценим каждый положительный отзыв и искренние слова благодарности» |
||||
|
||||
2. КАРУСЕЛЬ ОТЗЫВОВ: |
||||
— Большая открывающая кавычка (« «) в цвете #b8e6ed, font-size 80–100px |
||||
— Текст отзыва: курсив, 15–16px, #374151, background #eef4d1 |
||||
— Ссылка «Читать отзыв полностью» в цвете #0089c3 |
||||
— Стрелки навигации < > по бокам (round buttons) |
||||
— Карусель показывает 1 отзыв за раз |
||||
|
||||
ЦВЕТА: |
||||
Фон карточки отзыва: #eef4d1 (светло-жёлто-зелёный) |
||||
Кавычка: #b8e6ed (светло-бирюзовый) |
||||
Текст отзыва: #374151 |
||||
Ссылка «Читать полностью»: #0089c3 |
||||
|
||||
ПРАВИЛА: |
||||
✓ Фон карточки отзыва: #eef4d1 (тот же что у ReviewCard) |
||||
✓ Большая декоративная кавычка |
||||
✓ Ссылка «Читать отзыв полностью» обязательна |
||||
✓ Навигация карусели (стрелки) |
||||
✕ Не показывать более 1 отзыва за раз в карусели |
||||
✕ Не убирать навигационные стрелки |
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [ |
||||
{ |
||||
version: "v1.1", |
||||
date: "24.03.2026", |
||||
changes: [ |
||||
"H2: размер с ~20px на 36px, цвет с #111827 на #000000, line-height 38px", |
||||
], |
||||
}, |
||||
]; |
||||
|
||||
export default function ReviewsBlockPage() { |
||||
return ( |
||||
<div className="p-8 max-w-5xl mx-auto space-y-10"> |
||||
{/* Заголовок страницы */} |
||||
<div> |
||||
<p |
||||
className="text-xs font-semibold uppercase tracking-widest mb-1" |
||||
style={{ color: "var(--brand-053m)" }} |
||||
> |
||||
Блоки |
||||
</p> |
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}> |
||||
Блок «Отзывы о нас» |
||||
</h1> |
||||
<BlockMetaBar path="/blocks/reviews" defaultVersion="v1.1" defaultIsInPreview={false} defaultChangelog={CHANGELOG} /> |
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}> |
||||
Карусель отзывов с perm.oclinica.ru/lor — большая кавычка, текст, «Читать полностью», стрелки. |
||||
</p> |
||||
</div> |
||||
|
||||
{/* Живой пример */} |
||||
<section className="space-y-4"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Живой пример |
||||
</h2> |
||||
<ReviewsBlock /> |
||||
</section> |
||||
|
||||
{/* Несколько примеров */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Примеры отзывов (mock) |
||||
</h2> |
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> |
||||
{MOCK_REVIEWS.map((r, i) => ( |
||||
<div |
||||
key={i} |
||||
className="rounded-xl p-5 space-y-3" |
||||
style={{ background: "#eef4d1", border: "1px solid #d4e6a0" }} |
||||
> |
||||
<div |
||||
className="text-5xl leading-none font-serif" |
||||
style={{ color: "#b8e6ed" }} |
||||
> |
||||
« |
||||
</div> |
||||
<p |
||||
className="text-sm italic leading-relaxed" |
||||
style={{ color: "#374151" }} |
||||
> |
||||
{r.text} |
||||
</p> |
||||
<div className="pt-1"> |
||||
<a href="#" className="text-sm" style={{ color: "#0089c3" }}> |
||||
Читать отзыв полностью |
||||
</a> |
||||
</div> |
||||
<div className="border-t pt-2" style={{ borderColor: "#c8d8a0" }}> |
||||
<p className="text-xs font-medium" style={{ color: "#374151" }}> |
||||
{r.author} |
||||
</p> |
||||
<p className="text-[11px]" style={{ color: "#6b7280" }}> |
||||
о враче: {r.doctor} |
||||
</p> |
||||
</div> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* Анатомия */} |
||||
<section className="space-y-3"> |
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}> |
||||
Анатомия карточки отзыва |
||||
</h2> |
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3"> |
||||
{[ |
||||
{ label: "Фон карточки", value: "#eef4d1", note: "светло-жёлто-зелёный" }, |
||||
{ label: "Кавычка декоративная", value: "#b8e6ed", note: "80–100px, font-serif" }, |
||||
{ label: "Текст отзыва", value: "#374151", note: "14px, italic" }, |
||||
{ label: "Ссылка", value: "#0089c3", note: "--brand-053m" }, |
||||
].map((t) => ( |
||||
<div |
||||
key={t.label} |
||||
className="p-3 rounded-xl space-y-1" |
||||
style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }} |
||||
> |
||||
<div |
||||
className="w-8 h-8 rounded border" |
||||
style={{ background: t.value, borderColor: "var(--bb-border)" }} |
||||
/> |
||||
<p className="text-xs font-medium" style={{ color: "var(--bb-text)" }}> |
||||
{t.label} |
||||
</p> |
||||
<p className="text-[10px] font-mono" style={{ color: "var(--bb-text-muted)" }}> |
||||
{t.value} |
||||
</p> |
||||
<p className="text-[10px]" style={{ color: "var(--bb-text-muted)" }}> |
||||
{t.note} |
||||
</p> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</section> |
||||
|
||||
{/* LLM блок */} |
||||
<LlmBlock path="/blocks/reviews" version="v1.1" specText={LLM_REVIEWS_TEXT}> |
||||
<LlmSection title="Структура блока" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Содержимое", "Стиль"]} |
||||
rows={[ |
||||
["H2", "«Отзывы о нас»", "36px, font-bold, #000000, line-height 38px"], |
||||
["Подзаголовок", "Описание достижений клиники за 12 лет", "14px, #374151"], |
||||
["Кавычка", "Декоративная «", "80–100px, #b8e6ed, font-serif"], |
||||
["Текст отзыва", "Полный текст отзыва пациента", "14px, italic, #374151"], |
||||
["Ссылка", "«Читать отзыв полностью»", "#0089c3"], |
||||
["Стрелки карусели", "‹ ›", "Round buttons, фон var(--bb-sidebar-bg)"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Цвета" /> |
||||
<LlmTable |
||||
headers={["Элемент", "Цвет", "Токен"]} |
||||
rows={[ |
||||
["Фон карточки отзыва", "#eef4d1", "—"], |
||||
["Декоративная кавычка", "#b8e6ed", "—"], |
||||
["Текст отзыва", "#374151", "—"], |
||||
["Ссылка", "#0089c3", "--brand-053m"], |
||||
]} |
||||
/> |
||||
<LlmSection title="Правила" /> |
||||
<LlmRules |
||||
rules={[ |
||||
{ ok: true, text: "Фон карточки отзыва: #eef4d1" }, |
||||
{ ok: true, text: "Декоративная кавычка цвет #b8e6ed" }, |
||||
{ ok: true, text: "Ссылка «Читать отзыв полностью» обязательна" }, |
||||
{ ok: true, text: "Навигация карусели: стрелки ‹ ›" }, |
||||
{ ok: false, text: "Не показывать несколько отзывов одновременно" }, |
||||
{ ok: false, text: "Не убирать навигационные стрелки" }, |
||||
]} |
||||
/> |
||||
</LlmBlock> |
||||
</div> |
||||
); |
||||
return <ReviewsPageClient />; |
||||
} |
||||
|
||||
Loading…
Reference in new issue