Все проекты

Кейс · 2026 · Сайт и CMS

Порса Про

Сайт петербургского завода медицинской, лабораторной и музейной мебели: каталог на 353 позиции с фильтрами, спецификация в PDF, конструктор помещений на нейросети и админка, из которой ведут весь контент.

304 коммита
353 позиции каталога
6 месяцев
Роль Сайт и CMS целикомДизайн-система, фронтенд, бэкенд, миграция каталога, деплой
Срок 6 месяцевМарт — сентябрь 2026, от первого коммита до приёмки этапа
Состав 25 экранов14 публичных страниц и админка на 11 разделов
Стек Angular · NestJS · PostgresСерверный рендер, 20 модулей API, 85 эндпоинтов

Задача

Контекст

Шкаф для операционной не покупают в один клик. По ту сторону экрана — снабженец больницы или тендерный специалист: ему нужны габариты, материал, срок изготовления и документы для закупки по 44-ФЗ. Сайт должен работать как инструмент закупщика, а не как витрина.

Старый сайт жил двумя несвязанными ветками (медицина и фармацевтика), а каталог был списком без фильтров: подобрать шкаф по материалу корпуса или типу помещения было невозможно. Завод с тридцатилетней историей и объектами вроде Эрмитажа и НМИЦ Блохина выглядел как безымянный поставщик.

Отдельно договорились, чего на сайте не будет: «медицинского» шаблона белый + бирюзовый, стоковых врачей с планшетами, маркетплейсной пестроты со скидками и таймерами. Тон — инженерная уверенность: факты, цифры, чертежи.

353 позиции перенесены в единую структуру из 24 категорий
44/223-ФЗ сценарий закупки, под который собран весь сайт
0 UI-фреймворков: своя дизайн-система на SCSS

Путь
закупщика

Ключевой сценарий

Каталог продукции: панель фильтров слева и сетка карточек товара

Найти

Один каталог вместо двух разделов. Фильтры по категории, материалу корпуса и типу изделия, у каждого пункта — счётчик позиций. Поиск понимает и название, и артикул: закупщик обычно приходит с готовым кодом из сметы.

Карточка товара: фото изделия, габариты, материалы и срок изготовления

Проверить

Карточка отвечает на вопросы сметы: габариты, материал корпуса и столешницы, покрытие, срок изготовления. Отдельным блоком — изготовление под размер с шагом 50 мм: на заводе это норма, а не исключение.

Корзина с четырьмя позициями: количество, кнопки «Скачать PDF» и «Оформить»

Забрать спецификацию

Корзина собирает не заказ, а спецификацию: PDF формируется прямо в браузере, с артикулами и габаритами, и уходит вместе с заявкой на КП. Менеджер получает готовый документ, а не письмо «интересует ваша мебель».

Биография
завода

Публичные страницы

Главная — не список услуг, а доказательства. Шесть направлений продукции, реальные объекты, полный производственный цикл из шести шагов и четыре производства под одной крышей.

Секция главной страницы с шестью направлениями продукции Шесть направлений вместо одного «каталога»
Блок реализованных объектов на главной: Эрмитаж в виде чертежа Объекты подаются чертежом, а не стоковым фото
Страница производства: шесть шагов производственного цикла От ТЗ и замеров до монтажа и приёмки
Готовые решения: комплекты мебели под тип помещения Комплект под помещение, а не список позиций

Ещё на сайте: о компании с таймлайном 1994 → 2020, реализованные проекты с фильтром по отрасли, контакты с реквизитами для договора, поиск по всему сайту и три юридические страницы.

Помещение
по фото

Нейросеть в B2B-каталоге

Конструктор помещений: мастер из четырёх шагов, первый шаг — загрузка фотографии помещения
Конструктор помещений · шаг 1 из 4 OpenAI Images · gpt-image-2

Заказчик хотел «показать, как мебель встанет в кабинет». Обычно это 3D-визуализация за неделю работы дизайнера. Сделали за четыре шага и полторы минуты.

Клиент загружает фотографию своего помещения (или берёт шаблон), выбирает тип комнаты и мебель прямо из каталога, а сервис возвращает визуализацию и состав для расчёта. На бэкенде фотография и до четырёх референсов товара нормализуются через sharp и уходят в OpenAI Images.

Ключ на фронт не попадает: запрос идёт либо напрямую с сервера, либо через приватный релей на Deno. Режим переключается переменной окружения, прямой вызов остаётся рабочим значением по умолчанию.

4 шага мастера: фото, помещение, мебель, готово
4 референса товара уходят в модель вместе с кадром

Кабинет
редактора

11 разделов · desktop и мобильный

Сайт живёт без разработчика: товары, категории, готовые решения, проекты и тексты правятся из админки. Заявки видно в том же интерфейсе, со статусами и источником.

Дашборд админки: сводка по каталогу и последние заявки Сводка: каталог, заявки, быстрые действия
Список товаров в админке с фильтрами и пагинацией 353 позиции: фильтры, сортировка, массовые действия
Редактор карточки товара в админке: основные поля и паспорт изделия Карточка с защитой от потери несохранённых правок
Аналитика: просмотры страниц, динамика заявок, топ категорий Своя аналитика: просмотры, заявки, топ категорий

Как это
устроено

Архитектура

Браузер закупщика десктоп основной, мобильный вторичен nginx · TLS gzip · webp 320/640/960 · кеш изображений Angular 21 · SSR standalone · signals · lazy routes серверный рендер на Node NestJS 11 · API 20 модулей · 85 эндпоинтов JWT-админка · валидация · Swagger PostgreSQL 16 · TypeORM 353 товара · 24 категории · 333 изображения Хранилище файлов sharp · webp-производные · документы и PDF-каталог OpenAI Images gpt-image-2 · конструктор Deno-релей ключ не уходит на фронт Megaplan CRM клиент и сделка из заявки Telegram уведомление менеджеру sitemap · robots 353 адреса товаров Аналитика просмотры и источники заявок HTML с сервера /api · JSON SSR запрашивает API TypeORM sharp

Выкатка на VPS: systemd-юниты для API и SSR, nginx впереди, обновление одним скриптом. Без контейнеров на прикладном слое.

Что было
сложным

Инженерия

Исходник каталога — заводской прайс в Excel: артикулы, габариты и материалы в свободном формате, картинки вложены прямо в ячейки. Разбирать руками 353 позиции никто не собирался, поэтому импорт стал отдельным модулем: парсер листа, извлечение изображений, нормализация значений ячеек, валидатор, план публикации и отчёт о том, что изменится до записи в базу. Каждый шаг закрыт юнит-тестами — именно они позволяли гонять импорт повторно, не боясь испортить живой каталог.

Excel-парсерВалидацияDry-runsharp

У каталога из 353 карточек весь смысл в поисковой выдаче, а обычное SPA отдаёт роботу пустую страницу. Приложение собрано с серверным рендером: страница приходит готовой, дальше подхватывается на клиенте. К этому добавились человекопонятные адреса товаров, canonical, карта сайта, которую отдаёт сам API, и заголовки страниц из данных. Анимации при этом не мешают первому кадру: скрытие блоков включается только после гидратации, поэтому серверный HTML виден целиком даже без JavaScript.

Angular SSRsitemap.xmlcanonicalЧПУ

Заявка с сайта бесполезна, если лежит в базе. Подключили Мегаплан: из формы создаётся клиент и сделка в нужной воронке, параллельно уходит уведомление в Telegram. У Мегаплана нет API-ключей: авторизация идёт по логину и паролю, поэтому пароль хранится зашифрованным и на фронт не отдаётся, а воронка выбирается списком в админке, чтобы никто не вводил идентификаторы руками. Если CRM недоступна, заявка всё равно сохраняется и видна в админке.

Megaplan API v3ШифрованиеTelegram BotFallback

Стек

Технологии

Фронтенд

  • Angular 21 · standalone, signals
  • TypeScript 5.9
  • SCSS · 48 токенов, 16 миксинов
  • SSR · серверный рендер на Express
  • pdfmake · спецификация в браузере
  • stylelint · запрет hex вне токенов

Бэкенд

  • NestJS 11 · Node.js 22
  • 20 модулей · 85 эндпоинтов
  • JWT · Passport · class-validator
  • Swagger · helmet · compression
  • sharp · генерация webp-производных

Данные

  • PostgreSQL 16 · TypeORM
  • 353 товара · 24 категории
  • Импорт из Excel с отчётом и dry-run
  • Собственная аналитика просмотров
  • 101 файл тестов на фронте и бэке

Интеграции

  • OpenAI Images · gpt-image-2
  • Приватный релей на Deno
  • Megaplan CRM API v3
  • Telegram Bot API
  • Яндекс.Карты · cookie-согласие

Экраны сняты на локальной сборке проекта с реальными данными каталога.