Оптовый каталог стройхимии на 617 позиций
Сайт-каталог для оптового склада: прайс из Excel превращается в рабочий каталог одной командой, заявка собирается списком, а фотографии не нужны, чтобы всё заработало.
- Год
- 2026
- Роль
- Дизайн, разработка, инструменты
- Стек
- HTML · CSS · JavaScript · Python · Cloudflare Workers
- 617
- позиций в каталоге, 19 товарных групп
- 5 мс
- фильтрация всего прайса при вводе
- 1
- команда для обновления цен из Excel
- 0
- фотографий нужно, чтобы запуститься
Что это
Сайт оптового поставщика строительной химии: монтажные пены, герметики, клеи, скотчи — 617 позиций, девятнадцать товарных групп, тридцать один бренд. Клиенты — розничные магазины, монтажные бригады и подрядчики, которые закупаются коробами, а не штуками.
Заказчик и его реквизиты в этом разборе не называются: проект клиентский, и я показываю только инженерную часть — с чем пришли, что мешало и как решилось. Скриншоты сделаны на копии с обезличенными данными.
С чем пришли
Прайс жил в Excel и рассылался файлом. Клиент открывал таблицу на 617 строк, искал нужное глазами, выписывал позиции в мессенджер, менеджер вручную считал сумму. На каждой заявке терялось время с обеих сторон, а половина запросов приходила в виде «а есть пена 65-я? а сколько?».
Отдельная сложность: фотографий товара не было почти ни на что. Ждать, пока поставщики пришлют контент на шестьсот позиций, значило не запуститься никогда.
Что сделано
Каталог собирается из прайса скриптом. Обновились цены — запускается одна команда, и скрипт на Python перечитывает исходный Excel, разбирает категории, фасовку и наличие, и перезаписывает файл данных. Никакой ручной переноски: файл прайса остаётся единственным источником правды, а сайт — его отражением.
Поиск и фильтры работают мгновенно. Весь каталог лежит в одном файле данных и фильтруется прямо в браузере: ввод в строку поиска отсекает лишнее за пять миллисекунд. Никакого сервера, никаких запросов при каждом нажатии клавиши. Есть фильтр по брендам, по группам и переключатель «только то, что на складе».
Заявка собирается списком. Клиент отмечает позиции кнопкой «В заявку», проставляет количество коробов и отправляет весь список одним сообщением — менеджер получает готовый перечень, а не переписку в двадцать реплик. Заявка уходит в Telegram через ретранслятор на Cloudflare Workers, токен бота при этом в код сайта не попадает.
Фотографии перестали быть условием запуска. Каждой позиции рисуется этикетка её товарной группы: цветная пиктограмма с цифрой фасовки. Выглядит намеренно, весит ноль, каталог выглядит целым с первого дня. Реальные фото добавляются по одной — файл кладётся в папку, скрипт сам сопоставляет его с позицией по имени или по названию товара.
Где было больно
Прайс оказался не таблицей, а документом. В нём встречались строки-заголовки категорий, объединённые ячейки, разнобой в написании фасовки и позиции без цены. Разбор пришлось делать терпимым к грязи: строка, где заполнено только наименование, считается заголовком группы; позиция без цены не выбрасывается, а помечается как «под заказ». Скрипт, который падает на реальном файле заказчика, бесполезен, каким бы правильным он ни был.
Две разные аудитории на одном сайте. Главная должна продавать — там тёмная сцена склада с паллетой и крупные цифры. Каталог должен работать — там светлый плотный список, где на экран влезает максимум строк. Попытка сделать их в едином оформлении портила и то, и другое, поэтому страницы намеренно разошлись по стилю: витрина отдельно, рабочий инструмент отдельно.
Что получилось
Каталог грузится меньше чем за секунду и весит около 140 КБ вместе со шрифтами — на телефоне в поле, где связь плохая, это разница между «открылось» и «закрыл».
Обновление цен занимает одну команду вместо ручной правки шестисот строк. Сайт остаётся статикой: его можно положить на любой хостинг, он не требует базы данных, панели администратора и обслуживания.
Отдельно сделаны оферта и политика обработки данных — для оптовой торговли с юрлицами это не формальность, а условие работы.
Экраны
Снимки рабочей системы, а не макеты. Нажмите на любой — откроется во весь экран.