RUEN
Обсудить проект

Клиентский проект

DanceHub

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

строк — весь сайт
5 613
строк — весь бот
226
пакетов npm
0

Открыть сайт — dancehub.by ↗

Первый экран сайта DanceHub

Студия танцев в Боровлянах под Минском: четыре направления, расписание на неделю, запись на занятие. Сайт — один самодостаточный файл, заявки принимает Telegram-бот на двести с небольшим строк.

Весь сайт — один файл

Разметка, стили, скрипты и вся графика лежат в одном index.html на 5 613 строк. Ни сборки, ни npm, ни единого пакета. Внешних запросов ровно два: шрифты и виджет карты в контактах.

Это не упражнение в минимализме. У владелицы студии нет разработчика на подписке, и сайт должен открываться и через три года — а через три года нечего пересобирать и нечему протухать.

Владелица правит сайт, не открывая код

В конце файла — один блок настроек. Телефон, ники в Telegram и мессенджерах, адрес и ориентир, часы работы, шаблоны сообщений, переключатели секций. Значения сами разъезжаются по странице: разметка помечена атрибутами, скрипт подставляет.

Пустое поле не оставляет битую ссылку, а убирает элемент целиком — вместе со строкой в контактах. Кнопки «Позвонить», WhatsApp и Viber исчезают, если номера нет.

Запись в одно касание из расписания

Главное, ради чего всё делалось. Касание строки расписания подставляет в форму направление, день и время — человек не выбирает их заново из выпадающих списков.

Здесь есть деталь, которую видно только при проверке скринридером: имя кнопки собирается из данных самой строки. «19:00 BODY FLEX» встречается в неделе дважды, и без этого незрячий человек молча записался бы не на тот день.

Расписание живёт в разметке, а не в скрипте

Занятия — обычная разметка, а не массив в JavaScript. Фильтр по четырём направлениям пересчитывает результат («Показано N занятий», со склонением), сегодняшний день подсвечен, а если сегодня занятий нет — строка про ближайший день вычисляется из самого расписания, а не пишется руками.

Побочный выигрыш: расписание читают поисковые роботы, даже когда скрипт не выполнился.

Подбор направления тремя вопросами

Человек, который никогда не танцевал, не знает разницы между POLE EXOT и DANCE PLASTIC. Три вопроса, очки считаются прямо в браузере, результат ведёт либо в форму с уже выбранным направлением, либо в расписание, отфильтрованное под ответ.

Бот отвечает сайту раньше, чем Telegram отвечает боту

Заявка уходит на POST /api/lead. Бот отвечает сайту сразу, а доставку операторам отправляет в фоновый поток: посетительница не ждёт, пока ответит Telegram.

Если сервер недоступен, сайт не показывает «ошибку отправки», а даёт кнопку «Открыть Telegram» — человек не должен теряться из-за того, что у нас упал процесс.

Форму прикрывает скрытое поле-ловушка: заполнено — тихий успешный ответ, и никуда ничего не уходит.

Переписка через бота, а не в общем чате

Клиент пишет боту → сообщение уходит всем операторам с пометкой, от кого оно → оператор отвечает обычным Reply на пересланное сообщение → бот доставляет ответ ровно этому клиенту.

Никакого интерфейса операторам учить не нужно: они работают в Telegram так, как привыкли. Связка сообщений живёт в файле состояния и подрезается до последних четырёх тысяч записей, чтобы файл не рос бесконечно.

Бот написан на голой стандартной библиотеке Python: три попытки на вызов Telegram, обработка retry_after при ограничении частоты, атомарное сохранение состояния, смещение опроса переживает перезапуск, а исключение в обработке одного сообщения не роняет цикл.

Поиск заложен в разметку, а не в скрипт

Два блока schema.org: связанный граф «сайт → страница → студия → тренер» и отдельный блок вопросов и ответов. В карточке студии прописаны адрес, индекс, телефон, координаты, карта, районы обслуживания и описания всех четырёх направлений.

Скрипт приводит телефон и адрес в соответствие с настройками, если владелица их поменяет, но источник данных — сама разметка: робот Яндекса читает исходный код и JavaScript выполняет не всегда. По этой же причине адрес и телефон написаны на странице прямым текстом, а не подставляются скриптом.

У всех двенадцати вопросов есть свои якоря — такую ссылку удобно дать прямо в переписке.

Графика нарисована, а не снята

На сайте ни одной растровой фотографии: первый экран, карточки направлений и зал — векторная графика в том же файле. Это осознанный шаг: студия молодая, стоковые снимки чужих залов узнаются сразу и работают против доверия.

Когда появятся настоящие кадры, менять код не придётся: файлы подхватываются по фиксированным именам, а если файла нет — картинка убирается и остаётся графика.

Печатный флаер

К сайту сделан флаер A6 для раздачи: два артборда плюс экономичный вариант под светлую бумагу, вылеты по 3 мм, текст не ближе 4,9 мм от линии реза. В типографию уходят готовые PDF и PNG.

HTML · CSS · JavaScript · SVG · Python (стандартная библиотека) · Telegram Bot API · nginx · systemd