Клиентский проект
DanceHub
Сайт студии танцев и бот заявок: запись в одно касание прямо из расписания, без формы и переписки.
- строк — весь сайт
- 5 613
- строк — весь бот
- 226
- пакетов npm
- 0

Студия танцев в Боровлянах под Минском: четыре направления, расписание на неделю, запись на занятие. Сайт — один самодостаточный файл, заявки принимает 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