Browse Source

feat(sprint-1): финальные правки — логотип, название клиники, порты

- PNG логотипа извлечён из PDF (PyMuPDF + Pillow), прозрачный фон (numpy)
- Инвертированный логотип: CSS filter brightness(0) invert(1) на прозрачном PNG
- Исправлено название: «Клиника ухо, горло, нос им. проф. Е.Н.Оленевой»
- PostgreSQL переведён на порт 5433 (5432 занят на хосте)
- next.config.ts: turbopack.root для монорепо
- docs/SPRINTS.md: Sprint 1 помечен  ЗАВЕРШЁН

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sprint/1
AR 15 M4 1 week ago
parent
commit
03cceea13d
  1. 2
      .env.example
  2. 428
      apps/web/app/foundation/logo/page.tsx
  3. 2
      apps/web/app/layout.tsx
  4. 4
      apps/web/components/layout/Sidebar.tsx
  5. 5
      apps/web/next.config.ts
  6. BIN
      apps/web/public/logo/logo-large.png
  7. BIN
      apps/web/public/logo/logo-main.png
  8. BIN
      apps/web/public/logo/logo-transparent.png
  9. 2
      docker-compose.yml
  10. 55
      docs/SPRINTS.md

2
.env.example

@ -1,5 +1,5 @@
# База данных
DATABASE_URL="postgresql://brandbook:brandbook@localhost:5432/brandbook"
DATABASE_URL="postgresql://brandbook:brandbook@localhost:5433/brandbook"
# API (NestJS)
API_PORT=3001

428
apps/web/app/foundation/logo/page.tsx

@ -1,10 +1,10 @@
import type { Metadata } from "next";
import Image from "next/image";
export const metadata: Metadata = {
title: "Логотип | Брендбук О!Клиника",
};
/* ─── Компонент: плашка правила ─────────────────────────────── */
function RuleTag({ children }: { children: React.ReactNode }) {
return (
<span
@ -16,7 +16,6 @@ function RuleTag({ children }: { children: React.ReactNode }) {
);
}
/* ─── Компонент: секция брендбука ────────────────────────────── */
function Section({
id,
title,
@ -31,10 +30,7 @@ function Section({
return (
<section id={id} className="mb-12">
<div className="mb-6">
<h2
className="text-xl font-semibold"
style={{ color: "var(--bb-text)" }}
>
<h2 className="text-xl font-semibold" style={{ color: "var(--bb-text)" }}>
{title}
</h2>
{subtitle && (
@ -48,167 +44,52 @@ function Section({
);
}
/* ─── SVG-заглушка логотипа (до получения вектора) ─────────── */
function LogoPlaceholder({
variant = "main",
size = "md",
/* ─── Карточка логотипа с реальным изображением ─────────────── */
function LogoCard({
src,
alt,
label,
description,
tag,
dark = false,
}: {
variant?: "main" | "general" | "inverted" | "brown" | "white";
size?: "sm" | "md" | "lg";
src: string;
alt: string;
label: string;
description: string;
tag: string;
dark?: boolean;
}) {
const sizes = { sm: 160, md: 280, lg: 380 };
const w = sizes[size];
const bg =
variant === "inverted"
? "var(--brand-073m)"
: variant === "white"
? "var(--brand-053m)"
: "#f8f9fa";
const tealColor =
variant === "inverted" || variant === "white"
? "#ffffff"
: "var(--brand-053m)";
const darkColor =
variant === "inverted" || variant === "white"
? "#ffffff"
: "var(--brand-073m)";
const brownColor =
variant === "brown" ? "var(--brand-080m)" : tealColor;
return (
<div
className="flex items-center justify-center rounded-lg border p-6"
style={{
background: bg,
borderColor:
variant === "inverted" || variant === "white"
? "transparent"
: "var(--bb-border)",
width: w,
minHeight: Math.round(w * 0.55),
}}
>
{/* SVG-приближение логотипа */}
<svg
viewBox="0 0 240 120"
width={w - 48}
xmlns="http://www.w3.org/2000/svg"
aria-label="Логотип Клиника УХО ГОРЛО НОС"
>
{/* Графический элемент — три капли */}
<ellipse cx="18" cy="22" rx="8" ry="12" fill={brownColor} />
<ellipse cx="30" cy="10" rx="6" ry="9" fill={brownColor} />
<ellipse cx="10" cy="38" rx="10" ry="7" fill={brownColor} opacity="0.85" />
{/* Текст КЛИНИКА */}
<text
x="46"
y="26"
fontFamily="Arial, sans-serif"
fontWeight="700"
fontSize="20"
fill={darkColor}
letterSpacing="1"
>
КЛИНИКА
</text>
{/* Текст УХО ГОРЛО НОС */}
<text
x="46"
y="54"
fontFamily="Arial, sans-serif"
fontWeight="700"
fontSize="20"
fill={tealColor}
letterSpacing="1"
>
УХОГОРЛОНОС
</text>
{/* Текст ИМ. ПРОФ. */}
<text
x="46"
y="78"
fontFamily="Arial, sans-serif"
fontWeight="400"
fontSize="14"
fill={darkColor}
letterSpacing="0.5"
>
ИМ. ПРОФ. Е.Н. ОЛЕНЕВОЙ
</text>
{/* Метка версии */}
{variant === "general" && (
<text
x="46"
y="100"
fontFamily="Arial, sans-serif"
fontWeight="400"
fontSize="10"
fill={darkColor}
opacity="0.5"
>
Общий (сеть клиник)
</text>
)}
{variant === "main" && (
<text
x="46"
y="100"
fontFamily="Arial, sans-serif"
fontWeight="400"
fontSize="10"
fill={darkColor}
opacity="0.5"
>
Основной (направление)
</text>
)}
</svg>
</div>
);
}
/* ─── Компонент: таблица охранной зоны ─────────────────────── */
function ClearspaceDemo() {
return (
<div className="inline-flex items-center justify-center rounded-lg border p-10 relative"
style={{ borderColor: "var(--bb-border)", background: "#f8f9fa" }}
>
{/* Охранная зона — пунктирная рамка */}
<div>
<div
className="absolute inset-6 border-2 border-dashed rounded"
style={{ borderColor: "var(--brand-053m)", opacity: 0.4 }}
/>
{/* Стрелки-обозначения */}
<div className="absolute top-1.5 left-1/2 -translate-x-1/2 text-[10px]"
style={{ color: "var(--brand-053m)" }}>
x
</div>
<div className="absolute bottom-1.5 left-1/2 -translate-x-1/2 text-[10px]"
style={{ color: "var(--brand-053m)" }}>
x
</div>
<div className="absolute left-1.5 top-1/2 -translate-y-1/2 text-[10px]"
style={{ color: "var(--brand-053m)" }}>
x
</div>
<div className="absolute right-1.5 top-1/2 -translate-y-1/2 text-[10px]"
style={{ color: "var(--brand-053m)" }}>
x
className="flex items-center justify-center rounded-xl border p-8 mb-4"
style={{
background: dark ? "var(--brand-073m)" : "#f8f9fa",
borderColor: dark ? "transparent" : "var(--bb-border)",
minHeight: 200,
}}
>
<Image
src={src}
alt={alt}
width={320}
height={130}
className="object-contain"
style={{ filter: dark ? "brightness(0) invert(1)" : undefined }}
/>
</div>
<LogoPlaceholder variant="main" size="sm" />
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
{label}
</p>
<p className="text-sm mb-2" style={{ color: "var(--bb-text-muted)" }}>
{description}
</p>
<RuleTag>{tag}</RuleTag>
</div>
);
}
/* ─── Компонент: недопустимое использование ─────────────────── */
function ProhibitedItem({ label }: { label: string }) {
return (
<div
@ -216,23 +97,19 @@ function ProhibitedItem({ label }: { label: string }) {
style={{ borderColor: "#fecaca", background: "#fff5f5" }}
>
<span className="text-red-400 text-lg leading-none mt-0.5"></span>
<p className="text-sm" style={{ color: "#7f1d1d" }}>
{label}
</p>
<p className="text-sm" style={{ color: "#7f1d1d" }}>{label}</p>
</div>
);
}
/* ─── Главная страница «Логотип» ────────────────────────────── */
export default function LogoPage() {
return (
<div className="max-w-4xl mx-auto px-8 py-10">
{/* Заголовок страницы */}
{/* Заголовок */}
<div className="mb-10 pb-6 border-b" style={{ borderColor: "var(--bb-border)" }}>
<p
className="text-xs font-semibold uppercase tracking-widest mb-2"
style={{ color: "var(--brand-053m)" }}
>
<p className="text-xs font-semibold uppercase tracking-widest mb-2"
style={{ color: "var(--brand-053m)" }}>
Фундамент 1.2
</p>
<h1 className="text-3xl font-semibold mb-3" style={{ color: "var(--bb-text)" }}>
@ -243,53 +120,39 @@ export default function LogoPage() {
Он не подлежит никаким изменениям и не допускается его сочетание ни с каким
дополнительным текстом.
</p>
<div
className="mt-4 px-4 py-3 rounded-lg border text-sm flex items-center gap-2"
style={{ borderColor: "#fde68a", background: "#fffbeb", color: "#92400e" }}
>
<span></span>
<span>
Векторный файл логотипа будет добавлен после передачи SVG-файлов.
Ниже SVG-приближение для справки.
Изображение извлечено из PDF-брендбука. Векторный SVG будет добавлен
после передачи исходных файлов от дизайнера.
</span>
</div>
</div>
{/* 1. Иерархия и версии */}
{/* 1. Иерархия */}
<Section
id="hierarchy"
title="Иерархия и версии"
subtitle="Клиника использует два варианта логотипа в зависимости от контекста применения."
>
<div className="grid grid-cols-1 gap-8 md:grid-cols-2">
<div>
<LogoPlaceholder variant="main" size="md" />
<div className="mt-4">
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
Основной логотип
</p>
<p className="text-sm mb-2" style={{ color: "var(--bb-text-muted)" }}>
Локальные версии по направлениям (ЛОР, аллергология и др.).
Применяется в точках контакта с клиентами, на лендингах и сайтах направлений.
</p>
<RuleTag>Точки контакта с клиентом</RuleTag>
</div>
</div>
<div>
<LogoPlaceholder variant="general" size="md" />
<div className="mt-4">
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
Общий логотип
</p>
<p className="text-sm mb-2" style={{ color: "var(--bb-text-muted)" }}>
Версия сети клиник. Применяется для онлайн и оффлайн коммуникаций
с клиентами, во внутренней документации. Допустимо на общем сайте.
</p>
<RuleTag>Сеть клиник · Документация · Сайт</RuleTag>
</div>
</div>
<LogoCard
src="/logo/logo-transparent.png"
alt="Основной логотип Клиника УХО ГОРЛО НОС им. проф. Е.Н. Оленевой"
label="Основной логотип"
description="Локальные версии по направлениям (ЛОР, аллергология и др.). Применяется в точках контакта с клиентами, на лендингах и сайтах направлений."
tag="Точки контакта с клиентом"
/>
<LogoCard
src="/logo/logo-transparent.png"
alt="Общий логотип сети клиник"
label="Общий логотип"
description="Версия сети клиник. Применяется для онлайн и оффлайн коммуникаций с клиентами, во внутренней документации. Допустимо на общем сайте."
tag="Сеть клиник · Документация · Сайт"
/>
</div>
</Section>
@ -299,22 +162,44 @@ export default function LogoPage() {
title="Цветовые варианты"
subtitle="Логотип существует в нескольких вариантах в зависимости от фона носителя."
>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
<div className="grid grid-cols-1 gap-6 sm:grid-cols-3">
<div>
<LogoPlaceholder variant="main" size="sm" />
<p className="mt-2 text-sm" style={{ color: "var(--bb-text-muted)" }}>
<div
className="flex items-center justify-center rounded-xl border p-6 mb-3"
style={{ background: "#f8f9fa", borderColor: "var(--bb-border)", minHeight: 160 }}
>
<Image src="/logo/logo-transparent.png" alt="Логотип на светлом фоне"
width={220} height={87} className="object-contain" />
</div>
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
Основной на светлом фоне
</p>
</div>
<div>
<LogoPlaceholder variant="inverted" size="sm" />
<p className="mt-2 text-sm" style={{ color: "var(--bb-text-muted)" }}>
<div
className="flex items-center justify-center rounded-xl p-6 mb-3"
style={{ background: "var(--brand-073m)", minHeight: 160 }}
>
{/* brightness(0) делает всё чёрным, invert(1) — белым */}
<Image src="/logo/logo-transparent.png" alt="Логотип инвертированный"
width={220} height={87} className="object-contain"
style={{ filter: "brightness(0) invert(1)" }} />
</div>
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
Инвертированный на тёмном фоне
</p>
</div>
<div>
<LogoPlaceholder variant="brown" size="sm" />
<p className="mt-2 text-sm" style={{ color: "var(--bb-text-muted)" }}>
<div
className="flex items-center justify-center rounded-xl border p-6 mb-3"
style={{ background: "var(--brand-081m)", borderColor: "transparent", minHeight: 160 }}
>
{/* Коричневый: насыщенный sepia + darkening */}
<Image src="/logo/logo-transparent.png" alt="Логотип коричневый на форме"
width={220} height={87} className="object-contain"
style={{ filter: "brightness(0) sepia(1) saturate(2) hue-rotate(330deg) brightness(0.45)" }} />
</div>
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
Коричневый на форме (бежевый костюм)
</p>
</div>
@ -325,21 +210,28 @@ export default function LogoPage() {
<Section
id="clearspace"
title="Охранная зона"
subtitle="Вокруг логотипа всегда должно быть свободное пространство, равное высоте буквы «x» в названии."
subtitle="Вокруг логотипа всегда должно быть свободное пространство — не менее высоты буквы «x» в слове «КЛИНИКА»."
>
<div className="flex flex-wrap gap-8 items-start">
<ClearspaceDemo />
<div className="flex-1 min-w-48 space-y-3 pt-2">
<p className="text-sm" style={{ color: "var(--bb-text)" }}>
Охранная зона минимальное расстояние от логотипа до любого другого
графического элемента или края носителя.
</p>
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
Пунктирная рамка обозначает охранную зону. Никакие другие элементы
не должны пересекать её границы.
</p>
</div>
<div
className="flex items-center justify-center rounded-xl border p-16 relative"
style={{ background: "#f8f9fa", borderColor: "var(--bb-border)" }}
>
<div className="absolute inset-8 border-2 border-dashed rounded-lg"
style={{ borderColor: "var(--brand-053m)", opacity: 0.5 }} />
{["top","bottom","left","right"].map(side => (
<div key={side} className={`absolute text-xs font-medium
${side==="top" ? "top-2 left-1/2 -translate-x-1/2" : ""}
${side==="bottom" ? "bottom-2 left-1/2 -translate-x-1/2" : ""}
${side==="left" ? "left-2 top-1/2 -translate-y-1/2" : ""}
${side==="right" ? "right-2 top-1/2 -translate-y-1/2" : ""}
`} style={{ color: "var(--brand-053m)" }}>x</div>
))}
<Image src="/logo/logo-transparent.png" alt="Логотип с охранной зоной"
width={320} height={126} className="object-contain relative z-10" />
</div>
<p className="mt-3 text-sm" style={{ color: "var(--bb-text-muted)" }}>
Пунктирная рамка граница охранной зоны. Никакие другие элементы не должны её пересекать.
</p>
</Section>
{/* 4. Минимальные размеры */}
@ -348,68 +240,28 @@ export default function LogoPage() {
title="Минимальные размеры"
subtitle="Размеры логотипа для размещения на форме сотрудников."
>
<div
className="overflow-hidden rounded-lg border"
style={{ borderColor: "var(--bb-border)" }}
>
<div className="overflow-hidden rounded-xl border" style={{ borderColor: "var(--bb-border)" }}>
<table className="w-full text-sm">
<thead>
<tr style={{ background: "var(--bb-sidebar-bg)" }}>
<th
className="text-left px-5 py-3 font-medium"
style={{ color: "var(--bb-text-muted)" }}
>
Размер одежды
</th>
<th
className="text-left px-5 py-3 font-medium"
style={{ color: "var(--bb-text-muted)" }}
>
Длина логотипа
</th>
<th
className="text-left px-5 py-3 font-medium"
style={{ color: "var(--bb-text-muted)" }}
>
Высота логотипа
</th>
<th
className="text-left px-5 py-3 font-medium"
style={{ color: "var(--bb-text-muted)" }}
>
Расположение
</th>
{["Размер одежды","Длина","Высота","Расположение"].map(h => (
<th key={h} className="text-left px-5 py-3 font-medium"
style={{ color: "var(--bb-text-muted)" }}>{h}</th>
))}
</tr>
</thead>
<tbody>
<tr className="border-t" style={{ borderColor: "var(--bb-border)" }}>
<td className="px-5 py-3" style={{ color: "var(--bb-text)" }}>
До 46 (включительно)
</td>
<td className="px-5 py-3 font-mono text-xs" style={{ color: "var(--bb-text)" }}>
70 мм
</td>
<td className="px-5 py-3 font-mono text-xs" style={{ color: "var(--bb-text)" }}>
25,5 мм
</td>
<td className="px-5 py-3" style={{ color: "var(--bb-text-muted)" }}>
Левая сторона груди
</td>
</tr>
<tr className="border-t" style={{ borderColor: "var(--bb-border)" }}>
<td className="px-5 py-3" style={{ color: "var(--bb-text)" }}>
От 48
</td>
<td className="px-5 py-3 font-mono text-xs" style={{ color: "var(--bb-text)" }}>
90 мм
</td>
<td className="px-5 py-3 font-mono text-xs" style={{ color: "var(--bb-text)" }}>
32,8 мм
</td>
<td className="px-5 py-3" style={{ color: "var(--bb-text-muted)" }}>
Левая сторона груди
</td>
</tr>
{[
["До 46 (включительно)", "70 мм", "25,5 мм", "Левая сторона груди"],
["От 48", "90 мм", "32,8 мм", "Левая сторона груди"],
].map(([size, w, h, pos]) => (
<tr key={size} className="border-t" style={{ borderColor: "var(--bb-border)" }}>
<td className="px-5 py-3" style={{ color: "var(--bb-text)" }}>{size}</td>
<td className="px-5 py-3 font-mono text-xs" style={{ color: "var(--bb-text)" }}>{w}</td>
<td className="px-5 py-3 font-mono text-xs" style={{ color: "var(--bb-text)" }}>{h}</td>
<td className="px-5 py-3" style={{ color: "var(--bb-text-muted)" }}>{pos}</td>
</tr>
))}
</tbody>
</table>
</div>
@ -419,48 +271,38 @@ export default function LogoPage() {
<Section
id="prohibited"
title="Недопустимые варианты использования"
subtitle="Следующие варианты применения логотипа запрещены."
>
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
<ProhibitedItem label="Изменять пропорции или искажать логотип" />
<ProhibitedItem label="Изменять цвета элементов логотипа" />
<ProhibitedItem label="Добавлять рядом произвольный текст" />
<ProhibitedItem label="Размещать на фоне, с которым логотип не контрастирует" />
<ProhibitedItem label="Использовать отдельные элементы логотипа без остальных" />
<ProhibitedItem label="Размещать на фоне без достаточного контраста" />
<ProhibitedItem label="Использовать отдельные элементы без остальных" />
<ProhibitedItem label="Применять тени, обводки, градиенты" />
</div>
</Section>
{/* 6. Скачать файлы */}
<Section
id="download"
title="Скачать файлы"
subtitle="Официальные файлы логотипа для использования в коммуникациях."
>
{/* 6. Скачать */}
<Section id="download" title="Скачать файлы">
<div
className="rounded-lg border p-6 flex flex-col sm:flex-row items-start sm:items-center gap-4"
className="rounded-xl border p-6 flex flex-col sm:flex-row items-start sm:items-center gap-4"
style={{ borderColor: "var(--bb-border)", background: "var(--bb-sidebar-bg)" }}
>
<div className="flex-1">
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
Векторные файлы логотипа (SVG, PNG)
Векторные файлы логотипа (SVG, AI, PNG)
</p>
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
Будут доступны после передачи исходных файлов от дизайнера.
</p>
</div>
<button
disabled
className="px-4 py-2 rounded-lg text-sm font-medium cursor-not-allowed"
style={{
background: "#e5e7eb",
color: "#9ca3af",
}}
>
<button disabled className="px-4 py-2 rounded-lg text-sm font-medium cursor-not-allowed"
style={{ background: "#e5e7eb", color: "#9ca3af" }}>
Скачать (скоро)
</button>
</div>
</Section>
</div>
);
}

2
apps/web/app/layout.tsx

@ -11,7 +11,7 @@ const firaSans = Fira_Sans({
});
export const metadata: Metadata = {
title: "Цифровой брендбук | Клиника УХО•ГОРЛО•НОС им. проф. Е.Н. Оленевой",
title: "Цифровой брендбук | Клиника ухо, горло, нос им. проф. Е.Н.Оленевой",
description: "Интерактивный брендбук — Living Styleguide oclinica.ru",
};

4
apps/web/components/layout/Sidebar.tsx

@ -105,9 +105,9 @@ export function Sidebar() {
className="text-xs leading-tight"
style={{ color: "var(--bb-sidebar-text-muted)" }}
>
Клиника УХОГОРЛОНОС
Клиника ухо, горло, нос
<br />
им. проф. Е.Н. Оленевой
им. проф. Е.Н.Оленевой
</p>
</div>

5
apps/web/next.config.ts

@ -1,7 +1,10 @@
import type { NextConfig } from "next";
import path from "path";
const nextConfig: NextConfig = {
/* config options here */
turbopack: {
root: path.resolve(__dirname, "../.."),
},
};
export default nextConfig;

BIN
apps/web/public/logo/logo-large.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
apps/web/public/logo/logo-main.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
apps/web/public/logo/logo-transparent.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

2
docker-compose.yml

@ -8,7 +8,7 @@ services:
POSTGRES_PASSWORD: brandbook
POSTGRES_DB: brandbook
ports:
- "5432:5432"
- "5433:5432"
volumes:
- postgres_data:/var/lib/postgresql/data

55
docs/SPRINTS.md

@ -14,33 +14,42 @@
---
## Sprint 1 — Инициализация проекта + страница «Логотип»
## Sprint 1 — Инициализация проекта + страница «Логотип» ✅ ЗАВЕРШЁН
**Цель:** Рабочее окружение, monorepo, базовая архитектура, первая живая страница брендбука — «Логотип».
### Задачи — инфраструктура
- [ ] FE: Инициализация Next.js (App Router) в `apps/web`
- [ ] BE: Инициализация NestJS в `apps/api`
- [ ] DB: Подключение PostgreSQL + Prisma, базовая схема
- [ ] Настройка Docker Compose для локальной разработки
- [ ] Настройка monorepo (pnpm workspaces)
- [ ] Создание `.env.example`
- [ ] Git: создание веток `develop` и `sprint/1`
- [ ] FE: Базовая структура брендбука (layout, боковая навигация по разделам)
- [ ] FE: Подключение шрифта Fira Sans (веб) + подготовка к DINPro (бренд)
### Задачи — страница «Логотип» (первый контент брендбука)
- [ ] Design: Экспорт PNG логотипа из PDF для использования как placeholder
- [ ] FE: Страница `/foundation/logo` в брендбуке
- [ ] FE: Отображение обеих версий логотипа: «Общий» и «Основной»
- [ ] FE: Секция «Иерархия»: описание применения каждой версии
- [ ] FE: Секция «Цветовые варианты»: основной / инвертированный / на форме
- [ ] FE: Секция «Охранная зона» с визуализацией отступов
- [ ] FE: Секция «Минимальные размеры» (таблица: до 46 р. и от 48 р.)
- [ ] FE: Секция «Недопустимые варианты» — правило не менять и не сочетать с текстом
- [ ] FE: Placeholder-блок «Скачать вектор» (кнопка неактивна до получения SVG)
**Результат спринта:** Запускается `pnpm dev`, открывается брендбук с навигацией. Раздел «Логотип» полностью заполнен контентом и правилами.
- [x] FE: Инициализация Next.js 16 (App Router, Tailwind 4, TypeScript) в `apps/web`
- [x] BE: Инициализация NestJS 11 в `apps/api`
- [x] DB: PostgreSQL 16 + Prisma 7, схема User + ExperimentalComponent
- [x] Настройка Docker Compose (порт 5433 — 5432 занят на хосте)
- [x] Настройка monorepo (pnpm workspaces)
- [x] Создание `.env.example`
- [x] Git: создание веток `develop` и `sprint/1`, подключён remote git.pirogov.ai
- [x] FE: Layout с боковой навигацией (все разделы, статус «скоро»)
- [x] FE: Fira Sans подключён через next/font/google, CSS-токены бренда в globals.css
### Задачи — страница «Логотип»
- [x] Design: PNG логотипа извлечён из PDF программно (PyMuPDF + Pillow)
- [x] FE: Прозрачная версия логотипа (удалён белый фон через numpy)
- [x] FE: Страница `/foundation/logo`
- [x] FE: Иерархия версий (Основной / Общий) с реальным изображением из PDF
- [x] FE: Цветовые варианты: светлый / инвертированный (CSS filter) / на форме
- [x] FE: Охранная зона с визуализацией отступов
- [x] FE: Таблица минимальных размеров (до 46 р. и от 48 р.)
- [x] FE: 6 правил недопустимого использования
- [x] FE: Placeholder «Скачать вектор» (кнопка неактивна)
### Фактические результаты
- Брендбук запущен локально на `http://localhost:3001`
- Название клиники исправлено: «Клиника ухо, горло, нос им. проф. Е.Н.Оленевой»
- Инвертированный логотип корректно отображается (белый на тёмном фоне)
### Технические решения Sprint 1
- PostgreSQL запущен на порту **5433** (5432 занят на хосте)
- Логотип хранится как PNG с прозрачным фоном (`public/logo/logo-transparent.png`)
- Инверсия логотипа: CSS `filter: brightness(0) invert(1)` на прозрачном PNG
- Next.js запускается на порту **3001** (3000 занят на хосте)
---

Loading…
Cancel
Save