feat(sprint-5.5): store block metadata (version, changelog) in PostgreSQL
- Prisma schema: added `changelog Json @default("[]")` to Block model
- Migration: 20260324141120_add_changelog_field
- Seed: 8 blocks with actual versions (v1.0–v1.2) and changelog entries
- API: PATCH /blocks/by-path accepts changelog field
- CORS: accept any localhost port (regex pattern)
- BlockChangelog component: renders version history from API or fallback
- BlockMetaBar: loads changelog from API, passes to BlockChangelog
- Removed "API офлайн" text, replaced with subtle gray dot
- Added defaultChangelog prop for offline fallback
- Block pages: removed hardcoded changelog JSX, use defaultChangelog prop
- Updated SPRINTS.md with completed tasks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
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";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -50,6 +51,18 @@ const LLM_FORMS_TEXT = `
|
||||
✕ Не убирать чекбокс согласия
|
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [
|
||||
{
|
||||
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">
|
||||
@@ -64,7 +77,7 @@ export default function ContactFormsPage() {
|
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||
Формы записи
|
||||
</h1>
|
||||
<BlockMetaBar path="/blocks/contact-forms" defaultVersion="v1.1" defaultIsInPreview={false} />
|
||||
<BlockMetaBar path="/blocks/contact-forms" defaultVersion="v1.1" defaultIsInPreview={false} defaultChangelog={CHANGELOG} />
|
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Два блока форм с perm.oclinica.ru/lor — запись на приём и запрос стоимости операции.
|
||||
</p>
|
||||
@@ -112,21 +125,6 @@ export default function ContactFormsPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Changelog */}
|
||||
<section className="space-y-3">
|
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
||||
История версий
|
||||
</h2>
|
||||
<div className="p-3 rounded-lg" style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }}>
|
||||
<p className="font-semibold text-xs mb-1" style={{ color: "var(--bb-text)" }}>v1.1 — 24.03.2026</p>
|
||||
<ul className="list-disc list-inside space-y-0.5 text-xs" style={{ color: "var(--bb-text-muted)" }}>
|
||||
<li>H2: размер на <strong>36px</strong>, цвет на <strong>#000000</strong>, line-height 38px</li>
|
||||
<li>Фон формы 1: с #b8e6ed на <strong>#d4f6f8</strong></li>
|
||||
<li>Фон формы 2: с #ffffff на <strong>#d4f6f8</strong> (обе формы на одном фоне)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* LLM блок */}
|
||||
<LlmBlock path="/blocks/contact-forms" version="v1.1" specText={LLM_FORMS_TEXT}>
|
||||
<LlmSection title="Форма 1 — Запись на приём" />
|
||||
|
||||
@@ -2,6 +2,7 @@ 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";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Блок «Наши врачи». Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||
@@ -45,6 +46,25 @@ const LLM_DOCTORS_TEXT = `
|
||||
✕ Не убирать статистику
|
||||
`.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">
|
||||
@@ -59,7 +79,7 @@ export default function DoctorsBlockPage() {
|
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||
Блок «Наши врачи»
|
||||
</h1>
|
||||
<BlockMetaBar path="/blocks/doctors" defaultVersion="v1.2" defaultIsInPreview={true} />
|
||||
<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>
|
||||
@@ -101,29 +121,6 @@ export default function DoctorsBlockPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Changelog */}
|
||||
<section className="space-y-3">
|
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
||||
История версий
|
||||
</h2>
|
||||
<div className="space-y-2 text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
<div className="p-3 rounded-lg" style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }}>
|
||||
<p className="font-semibold text-xs mb-1" style={{ color: "var(--bb-text)" }}>v1.2 — 24.03.2026</p>
|
||||
<ul className="list-disc list-inside space-y-0.5 text-xs">
|
||||
<li>H2: размер с ~30px на <strong>36px</strong>, цвет с #111827 на <strong>#000000</strong></li>
|
||||
<li>H2 line-height: <strong>38px</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg" style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }}>
|
||||
<p className="font-semibold text-xs mb-1" style={{ color: "var(--bb-text)" }}>v1.1 — 23.03.2026</p>
|
||||
<ul className="list-disc list-inside space-y-0.5 text-xs">
|
||||
<li>6 реальных фото врачей с сайта</li>
|
||||
<li>Статистика без фона, только border-bottom #60959c</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* LLM блок */}
|
||||
<LlmBlock path="/blocks/doctors" version="v1.2" specText={LLM_DOCTORS_TEXT}>
|
||||
<LlmSection title="Структура блока" />
|
||||
|
||||
@@ -2,6 +2,7 @@ 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";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Hero-баннер. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||
@@ -50,6 +51,27 @@ const LLM_HERO_TEXT = `
|
||||
✕ Не убирать три пункта с галочками
|
||||
`.trim();
|
||||
|
||||
const CHANGELOG: ChangelogEntry[] = [
|
||||
{
|
||||
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">
|
||||
@@ -64,7 +86,7 @@ export default function HeroPage() {
|
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||
Hero-баннер
|
||||
</h1>
|
||||
<BlockMetaBar path="/blocks/hero" defaultVersion="v1.2" defaultIsInPreview={true} />
|
||||
<BlockMetaBar path="/blocks/hero" defaultVersion="v1.2" defaultIsInPreview={true} defaultChangelog={CHANGELOG} />
|
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Главный баннер страницы раздела ЛОР — perm.oclinica.ru/lor. Двухколоночный блок, единый светло-кремовый фон{" "}
|
||||
<strong>#f9f4e7</strong>.
|
||||
@@ -138,31 +160,6 @@ export default function HeroPage() {
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Changelog */}
|
||||
<section className="space-y-3">
|
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
||||
История версий
|
||||
</h2>
|
||||
<div className="space-y-2 text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
<div className="p-3 rounded-lg" style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }}>
|
||||
<p className="font-semibold text-xs mb-1" style={{ color: "var(--bb-text)" }}>v1.2 — 24.03.2026</p>
|
||||
<ul className="list-disc list-inside space-y-0.5 text-xs">
|
||||
<li>H1: цвет исправлен с #53514e на <strong>#cb9768</strong>, размер с ~20px на <strong>36px</strong></li>
|
||||
<li>Заголовок баннера: размер с ~16px на <strong>22px</strong>, цвет с #111827 на <strong>#333333</strong></li>
|
||||
<li>CTA-кнопка: стиль изменён с outline на <strong>pill</strong> (фон #E9E4D4, radius 25px)</li>
|
||||
<li>Дефис в H1: длинное тире «–» заменено на простой дефис «-» (как на сайте)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg" style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }}>
|
||||
<p className="font-semibold text-xs mb-1" style={{ color: "var(--bb-text)" }}>v1.1 — 23.03.2026</p>
|
||||
<ul className="list-disc list-inside space-y-0.5 text-xs">
|
||||
<li>Единый фон #f9f4e7 (ранее был разбит на две зоны)</li>
|
||||
<li>Реальное фото врача с пациентом</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* LLM блок */}
|
||||
<LlmBlock path="/blocks/hero" version="v1.2" specText={LLM_HERO_TEXT}>
|
||||
<LlmSection title="Структура баннера" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -67,6 +68,17 @@ const LLM_NEWS_TEXT = `
|
||||
✕ Не добавлять описание/анонс в карточку
|
||||
`.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">
|
||||
@@ -81,7 +93,7 @@ export default function NewsBlockPage() {
|
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||
Блок «Новости»
|
||||
</h1>
|
||||
<BlockMetaBar path="/blocks/news" defaultVersion="v1.1" defaultIsInPreview={false} />
|
||||
<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 карточки в ряд (дата + заголовок-ссылка),
|
||||
кнопка «Все новости».
|
||||
@@ -132,20 +144,6 @@ export default function NewsBlockPage() {
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Changelog */}
|
||||
<section className="space-y-3">
|
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
||||
История версий
|
||||
</h2>
|
||||
<div className="p-3 rounded-lg" style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }}>
|
||||
<p className="font-semibold text-xs mb-1" style={{ color: "var(--bb-text)" }}>v1.1 — 24.03.2026</p>
|
||||
<ul className="list-disc list-inside space-y-0.5 text-xs" style={{ color: "var(--bb-text-muted)" }}>
|
||||
<li>H2: размер на <strong>36px</strong>, цвет на <strong>#000000</strong>, line-height 38px</li>
|
||||
<li>Фон секции: с белого на <strong>#f2fee6</strong> (светло-зелёный, как на реальном сайте)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* LLM блок */}
|
||||
<LlmBlock path="/blocks/news" version="v1.1" specText={LLM_NEWS_TEXT}>
|
||||
<LlmSection title="Структура карточки новости" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -52,6 +53,16 @@ const LLM_REVIEWS_TEXT = `
|
||||
✕ Не убирать навигационные стрелки
|
||||
`.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">
|
||||
@@ -66,7 +77,7 @@ export default function ReviewsBlockPage() {
|
||||
<h1 className="text-2xl font-bold mb-2" style={{ color: "var(--bb-text)" }}>
|
||||
Блок «Отзывы о нас»
|
||||
</h1>
|
||||
<BlockMetaBar path="/blocks/reviews" defaultVersion="v1.1" defaultIsInPreview={false} />
|
||||
<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>
|
||||
@@ -157,19 +168,6 @@ export default function ReviewsBlockPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Changelog */}
|
||||
<section className="space-y-3">
|
||||
<h2 className="font-semibold text-base" style={{ color: "var(--bb-text)" }}>
|
||||
История версий
|
||||
</h2>
|
||||
<div className="p-3 rounded-lg" style={{ background: "var(--bb-sidebar-bg)", border: "1px solid var(--bb-border)" }}>
|
||||
<p className="font-semibold text-xs mb-1" style={{ color: "var(--bb-text)" }}>v1.1 — 24.03.2026</p>
|
||||
<ul className="list-disc list-inside space-y-0.5 text-xs" style={{ color: "var(--bb-text-muted)" }}>
|
||||
<li>H2: размер с ~20px на <strong>36px</strong>, цвет с #111827 на <strong>#000000</strong>, line-height 38px</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* LLM блок */}
|
||||
<LlmBlock path="/blocks/reviews" version="v1.1" specText={LLM_REVIEWS_TEXT}>
|
||||
<LlmSection title="Структура блока" />
|
||||
|
||||
Reference in New Issue
Block a user