Browse Source

feat(sprint2): реальные фото из PDF, тайтлы страниц, убрана Печать

- Навигация: макет Кабинет 04 + карточка врача из PDF, фото дверей 13 и 31, указатели по этажам
- Транспорт: рендер макета трамвая из PDF вместо CSS-заглушки
- Тайтлы: единый формат «Раздел. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой» на всех страницах
- Сайдбар: убран пункт «Печать» (нет данных из брендбука)
- SPRINTS.md: обновлены фактические результаты Sprint 2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sprint/3
AR 15 M4 1 week ago
parent
commit
52acdc98e2
  1. 2
      apps/web/app/foundation/logo/page.tsx
  2. 2
      apps/web/app/foundation/typography/page.tsx
  3. 235
      apps/web/app/offline/navigation/page.tsx
  4. 2
      apps/web/app/offline/print/page.tsx
  5. 112
      apps/web/app/offline/transport/page.tsx
  6. 1
      apps/web/components/layout/Sidebar.tsx
  7. BIN
      apps/web/public/offline/navigation/nav-3.jpeg
  8. BIN
      apps/web/public/offline/navigation/nav-door-31.png
  9. BIN
      apps/web/public/offline/navigation/nav-mockup-signs.jpeg
  10. BIN
      apps/web/public/offline/navigation/nav-render-p13.jpeg
  11. BIN
      apps/web/public/offline/transport/tram-mockup.jpeg
  12. 18
      docs/SPRINTS.md

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

@ -2,7 +2,7 @@ import type { Metadata } from "next";
import Image from "next/image"; import Image from "next/image";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Логотип | Брендбук О!Клиника", title: "Логотип. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
}; };
function RuleTag({ children }: { children: React.ReactNode }) { function RuleTag({ children }: { children: React.ReactNode }) {

2
apps/web/app/foundation/typography/page.tsx

@ -1,7 +1,7 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Типографика | Брендбук О!Клиника", title: "Типографика. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
}; };
/* ─── Шкала типографики ────────────────────────────────────────────── */ /* ─── Шкала типографики ────────────────────────────────────────────── */

235
apps/web/app/offline/navigation/page.tsx

@ -1,7 +1,8 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import Image from "next/image";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Внутренняя навигация | Брендбук О!Клиника", title: "Внутренняя навигация. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
}; };
function Section({ function Section({
@ -30,74 +31,6 @@ function Section({
); );
} }
/* Макет настенной таблички */
function SignMockup({
type,
text,
subtext,
bgColor,
textColor,
accentColor,
size,
}: {
type: string;
text: string;
subtext?: string;
bgColor: string;
textColor: string;
accentColor: string;
size: string;
}) {
return (
<div className="flex flex-col items-start gap-2">
<div
className="rounded-lg px-6 py-4 flex items-center gap-4"
style={{
background: bgColor,
width: 260,
border: bgColor === "#ffffff" ? "1px solid #e5e7eb" : "none",
}}
>
{/* Цветовая полоса */}
<div
className="w-1.5 self-stretch rounded-full"
style={{ background: accentColor, minHeight: 32 }}
/>
<div>
<p
style={{
fontFamily: "'DINPro', Arial, sans-serif",
fontSize: 14,
fontWeight: 700,
color: textColor,
lineHeight: 1.3,
}}
>
{text}
</p>
{subtext && (
<p
style={{
fontFamily: "'DINPro', Arial, sans-serif",
fontSize: 10,
color: textColor,
opacity: 0.65,
marginTop: 3,
}}
>
{subtext}
</p>
)}
</div>
</div>
<div className="pl-1">
<p className="text-xs font-medium" style={{ color: "var(--bb-text)" }}>{type}</p>
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}>{size}</p>
</div>
</div>
);
}
export default function NavigationPage() { export default function NavigationPage() {
return ( return (
<div className="max-w-4xl mx-auto px-8 py-10"> <div className="max-w-4xl mx-auto px-8 py-10">
@ -115,52 +48,111 @@ export default function NavigationPage() {
</h1> </h1>
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}> <p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
Система навигационных табличек и указателей внутри клиники. Система навигационных табличек и указателей внутри клиники.
Единый стиль с фирменными цветами и шрифтом DINPro. На оргстекле, наклейки из плёнок Оракл 053M и 073M.
</p> </p>
</div> </div>
{/* Шаблоны табличек */} {/* Макеты таблечек */}
<Section <Section
title="Типы табличек" title="Макеты навигационных табличек"
subtitle="Четыре базовых шаблона для разных зон клиники." subtitle="Два типа: табличка кабинета с бирюзовым заголовком и карточка врача с логотипом и QR-кодом."
>
<div className="max-w-lg">
<div
className="rounded-xl overflow-hidden border mb-4"
style={{ borderColor: "var(--bb-border)" }}
> >
<div className="flex flex-wrap gap-6"> <Image
<SignMockup src="/offline/navigation/nav-mockup-signs.jpeg"
type="Кабинет врача" alt="Макеты навигационных табличек: Кабинет 04 с бирюзовым заголовком и карточка врача Лебединской"
text="Кабинет № 101" width={620}
subtext="Оториноларингология" height={570}
bgColor="#ffffff" className="w-full object-cover"
textColor="#1b4c72"
accentColor="#7ecfca"
size="200 × 80 мм"
/> />
<SignMockup </div>
type="Направляющий указатель" <p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
text="→ Регистратура" Макет из брендбука
subtext="2-й этаж" </p>
bgColor="#5b7b87" <p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
textColor="#ffffff" Табличка «Кабинет 04»: бирюзовый заголовок (Oracal 053M), специализация и ФИО врачей.
accentColor="#7ecfca" Карточка врача: логотип клиники, имя, должности, QR-код на страницу врача.
size="300 × 80 мм" </p>
</div>
</Section>
{/* Нумерация дверей */}
<Section
title="Нумерация кабинетов на дверях"
subtitle="Номер кабинета размещается непосредственно на двери — крупный шрифт, плёнка Oracal."
>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div>
<div
className="rounded-xl overflow-hidden border mb-4"
style={{ borderColor: "var(--bb-border)" }}
>
<Image
src="/offline/navigation/nav-door-31.png"
alt="Белая дверь кабинета 31 с крупным номером из плёнки"
width={770}
height={963}
className="w-full object-cover"
style={{ maxHeight: 420, objectPosition: "top" }}
/> />
<SignMockup </div>
type="Зона ожидания" <p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
text="Зона ожидания" Кабинет 31 белая дверь
subtext="Пожалуйста, соблюдайте тишину" </p>
bgColor="#e0f5f4" <p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
textColor="#1b4c72" Крупный номер в верхней части двери. Тёмная плёнка на белом фоне.
accentColor="#5bb5ad" </p>
size="250 × 80 мм" </div>
<div>
<div
className="rounded-xl overflow-hidden border mb-4"
style={{ borderColor: "var(--bb-border)" }}
>
<Image
src="/offline/navigation/nav-3.jpeg"
alt="Серая дверь кабинета 13 с крупным номером"
width={800}
height={463}
className="w-full object-cover"
style={{ maxHeight: 420, objectPosition: "left" }}
/> />
<SignMockup </div>
type="Запрещающий" <p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
text="Вход только для персонала" Кабинет 13 серая дверь
bgColor="#1b4c72" </p>
textColor="#ffffff" <p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
accentColor="#c4a882" Тёмная дверь с крупным номером. Рядом табличка кабинета на стене.
size="250 × 60 мм" </p>
</div>
</div>
</Section>
{/* Указатели по этажам */}
<Section
title="Указатели по этажам"
subtitle="Навигационные панели в холлах — показывают расположение кабинетов и специализации на каждом этаже."
>
<div
className="rounded-xl overflow-hidden border"
style={{ borderColor: "var(--bb-border)" }}
>
<Image
src="/offline/navigation/nav-render-p13.jpeg"
alt="Навигационные панели по этажам: кабинеты 01-06, 21-25, 31-37, 41-45"
width={2105}
height={1489}
className="w-full"
/> />
</div> </div>
<p className="mt-3 text-sm" style={{ color: "var(--bb-text-muted)" }}>
Слева указатель с этажом и направлением, справа панель с полным перечнем кабинетов.
Активный этаж выделяется бирюзовым фоном (Oracal 053M).
</p>
</Section> </Section>
{/* Технические требования */} {/* Технические требования */}
@ -188,12 +180,10 @@ export default function NavigationPage() {
</thead> </thead>
<tbody> <tbody>
{[ {[
["Основной материал", "ПВХ 3мм / ПС зеркальный / акрил"], ["Основной материал", "Оргстекло"],
["Покрытие фона", "Oracal плёнка (053M / 073M / 050M)"], ["Покрытие", "Наклейка из плёнок Oracal"],
["Шрифт", "DINPro Bold / Regular (DXF для фрезеровки)"], ["Шрифт", "DINPro Bold / Regular"],
["Крепление", "Двусторонний скотч / шурупы с дистанционным держателем"], ["Крепление", "Дистанционные держатели"],
["Толщина букв (фрезеровка)", "3 мм от основы"],
["Минимальный размер текста", "10 мм по высоте"],
].map(([param, value]) => ( ].map(([param, value]) => (
<tr <tr
key={param} key={param}
@ -218,24 +208,25 @@ export default function NavigationPage() {
title="Цвета Oracal для навигации" title="Цвета Oracal для навигации"
subtitle="Допустимые цвета плёнки по коду Oracal." subtitle="Допустимые цвета плёнки по коду Oracal."
> >
<div className="grid grid-cols-2 gap-4 sm:grid-cols-4"> <div className="flex gap-6">
{[ {[
{ code: "053M", hex: "#7ecfca", name: "Акцент / полоса" }, { code: "053M", hex: "#7ecfca", name: "Заголовок таблички / активный этаж" },
{ code: "073M", hex: "#5b7b87", name: "Фон указателей" }, { code: "073M", hex: "#5b7b87", name: "Дополнительный акцент" },
{ code: "050M", hex: "#1b4c72", name: "Фон запрещающих" },
{ code: "081M", hex: "#c4a882", name: "Акцент на тёмном" },
].map(c => ( ].map(c => (
<div <div
key={c.code} key={c.code}
className="rounded-xl overflow-hidden border" className="flex items-center gap-4 rounded-xl border p-4"
style={{ borderColor: "var(--bb-border)" }} style={{ borderColor: "var(--bb-border)", background: "var(--bb-sidebar-bg)" }}
> >
<div className="h-16" style={{ background: c.hex }} /> <div
<div className="p-3"> className="w-12 h-12 rounded-lg shrink-0"
<p className="font-medium text-xs" style={{ color: "var(--bb-text)" }}> style={{ background: c.hex }}
/>
<div>
<p className="font-medium text-sm" style={{ color: "var(--bb-text)" }}>
Oracal {c.code} Oracal {c.code}
</p> </p>
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}> <p className="text-xs mt-0.5" style={{ color: "var(--bb-text-muted)" }}>
{c.name} {c.name}
</p> </p>
<p className="font-mono text-xs mt-1" style={{ color: "var(--bb-text-muted)" }}> <p className="font-mono text-xs mt-1" style={{ color: "var(--bb-text-muted)" }}>

2
apps/web/app/offline/print/page.tsx

@ -2,7 +2,7 @@ import type { Metadata } from "next";
import Image from "next/image"; import Image from "next/image";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Печатные материалы | Брендбук О!Клиника", title: "Печатные материалы. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
}; };
function Section({ function Section({

112
apps/web/app/offline/transport/page.tsx

@ -2,7 +2,7 @@ import type { Metadata } from "next";
import Image from "next/image"; import Image from "next/image";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Брендирование транспорта | Брендбук О!Клиника", title: "Брендирование транспорта. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
}; };
function Section({ function Section({
@ -31,101 +31,6 @@ function Section({
); );
} }
/* Макет трамвая (упрощённый силуэт) */
function TramMockup() {
return (
<div
className="relative rounded-2xl overflow-hidden"
style={{
background: "#f8f9fa",
border: "1px solid var(--bb-border)",
padding: "32px 24px",
}}
>
{/* Кузов трамвая */}
<div
className="relative mx-auto rounded-xl overflow-hidden"
style={{
width: "100%",
maxWidth: 560,
height: 160,
background: "#ffffff",
border: "2px solid #e5e7eb",
}}
>
{/* Верхняя полоса — бирюзовая */}
<div
className="absolute top-0 left-0 right-0"
style={{ height: 28, background: "#7ecfca" }}
/>
{/* Основная бежевая полоса */}
<div
className="absolute left-0 right-0"
style={{ top: 28, height: 76, background: "#c4a882" }}
/>
{/* Нижняя полоса — серо-голубая */}
<div
className="absolute left-0 right-0"
style={{ top: 104, height: 56, background: "#5b7b87" }}
/>
{/* Логотип по центру бежевой полосы */}
<div
className="absolute flex items-center justify-center"
style={{ top: 28, left: 0, right: 0, height: 76 }}
>
<Image
src="/logo/logo-transparent.png"
alt="Логотип на трамвае"
width={180}
height={64}
className="object-contain"
style={{
filter: "brightness(0) sepia(1) saturate(2) hue-rotate(330deg) brightness(0.45)",
}}
/>
</div>
{/* Окна (декоративные) */}
{[80, 180, 280, 380, 460].map(x => (
<div
key={x}
className="absolute rounded"
style={{
left: x,
top: 40,
width: 50,
height: 44,
background: "rgba(255,255,255,0.6)",
border: "1px solid rgba(255,255,255,0.8)",
}}
/>
))}
{/* Колёса (декоративные) */}
{[60, 200, 360, 500].map(x => (
<div
key={x}
className="absolute rounded-full"
style={{
left: x,
bottom: -8,
width: 24,
height: 24,
background: "#374151",
}}
/>
))}
</div>
<p className="mt-4 text-center text-xs" style={{ color: "var(--bb-text-muted)" }}>
Схема цветового решения (превью, не финальный макет)
</p>
</div>
);
}
export default function TransportPage() { export default function TransportPage() {
return ( return (
@ -151,9 +56,20 @@ export default function TransportPage() {
{/* Макет */} {/* Макет */}
<Section <Section
title="Макет трамвая" title="Макет трамвая"
subtitle="Трёхполосная схема брендирования: верхняя бирюзовая, центральная бежевая с логотипом, нижняя серо-голубая." subtitle="Вид с обеих сторон: бирюзовая полоса, логотип клиники, фотографии пациентов и врачей, контактная информация."
> >
<TramMockup /> <div
className="rounded-xl overflow-hidden border"
style={{ borderColor: "var(--bb-border)" }}
>
<Image
src="/offline/transport/tram-mockup.jpeg"
alt="Макет брендирования трамвая: вид спереди и сзади с логотипом Клиники ухо горло нос им. проф. Е.Н.Оленевой"
width={1884}
height={977}
className="w-full"
/>
</div>
</Section> </Section>
{/* Цветовая схема */} {/* Цветовая схема */}

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

@ -68,7 +68,6 @@ const NAV: NavSection[] = [
{ label: "Бейджи", href: "/offline/badges" }, { label: "Бейджи", href: "/offline/badges" },
{ label: "Навигация", href: "/offline/navigation" }, { label: "Навигация", href: "/offline/navigation" },
{ label: "Транспорт", href: "/offline/transport" }, { label: "Транспорт", href: "/offline/transport" },
{ label: "Печать", href: "/offline/print" },
], ],
}, },
{ {

BIN
apps/web/public/offline/navigation/nav-3.jpeg

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
apps/web/public/offline/navigation/nav-door-31.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 KiB

BIN
apps/web/public/offline/navigation/nav-mockup-signs.jpeg

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
apps/web/public/offline/navigation/nav-render-p13.jpeg

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

BIN
apps/web/public/offline/transport/tram-mockup.jpeg

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

18
docs/SPRINTS.md

@ -81,22 +81,24 @@
### Фактические результаты ### Фактические результаты
- Страница `/foundation/colors` — 7 цветов с HEX/RGB/HSL/CSS-var и копированием, WCAG-контраст 7 пар, экспорт JSON - Страница `/foundation/colors` — 7 цветов с HEX/RGB/HSL/CSS-var и копированием, WCAG-контраст 7 пар, экспорт JSON
- Страница `/foundation/typography` — DINPro (оффлайн) + Fira Sans (веб), таблица применения, полные шкалы, живой пример - Страница `/foundation/typography` — DINPro (оффлайн) + Fira Sans (веб), таблица применения, полные шкалы, живой пример
- Страница `/offline/uniform`схема формы, таблица размеров логотипа, правила использования - Страница `/offline/uniform`реальные фото из PDF (беж + синий вариант), таблица размеров, правила
- Страница `/offline/badges`макеты бейджей 70×30 мм (светлый/тёмный), состав текста, применение - Страница `/offline/badges`реальные фото из PDF (лицевая + оборотная), состав текста, пример
- Страница `/offline/navigation`4 шаблона табличек, технические требования, цвета Oracal - Страница `/offline/navigation`макеты из PDF (Кабинет 04, карточка врача), фото дверей с номерами (13, 31), указатели по этажам
- Страница `/offline/transport`CSS-макет трамвая с трёхполосной схемой, таблица зон - Страница `/offline/transport`макет трамвая из PDF (оба вида, реальный рендер), таблица зон, цвета Oracal
- Страница `/offline/print`макеты визитки (лицо/оборот) и листовки А5, Telegram-бот - Страница `/offline/print`убрана из навигации (нет данных из брендбука)
- Sidebar: убраны «скоро» для Цветов, Типографики и всех 5 страниц Оффлайн - Sidebar: убраны «скоро» для Цветов, Типографики и всех страниц Оффлайн кроме Печати
- Версия обновлена до **Sprint 2 · v0.2.0** - Версия обновлена до **Sprint 2 · v0.2.0**
- **Деплой на Vercel:** https://web-oclinica.vercel.app (production, бесплатно) - **Деплой на Vercel:** https://web-oclinica.vercel.app (production, бесплатно)
- **Тайтлы страниц:** единый формат «Раздел. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой»
### Технические решения Sprint 2 ### Технические решения Sprint 2
- Страница «Цвета» — `"use client"` для clipboard API и экспорта JSON - Страница «Цвета» — `"use client"` для clipboard API и экспорта JSON
- WCAG relative luminance вычисляется на клиенте, без зависимостей - WCAG relative luminance вычисляется на клиенте, без зависимостей
- DINPro отображается с фоллбэком Arial (лицензионный шрифт) - DINPro отображается с фоллбэком Arial (лицензионный шрифт)
- Макеты (бейджи, трамвай, визитки) — чистый CSS/Tailwind без внешних зависимостей - Реальные фото и макеты из PDF: PyMuPDF (fitz) — извлечение растровых изображений и рендер векторных страниц
- Рендер PDF страниц: 2.5–3.0x масштаб → JPEG, кроп до нужной области через Pillow
**Результат спринта:** Разделы «Цвета», «Типографика» и «Оффлайн элементы» полностью готовы. **Результат спринта:** Разделы «Цвета», «Типографика» и «Оффлайн элементы» полностью готовы с реальными материалами из брендбука.
--- ---

Loading…
Cancel
Save