Расписание теннисного клуба онлайн — без двойных броней
letscourt /admin/schedule — главный экран администратора. Сетка дня по кортам, цветовая кодировка покрытий (хард / грунт / трава / падл), click-to-create бронь за ≤15 секунд, hover-popover с быстрым взглядом, drawer с inline-редактированием. Двойные брони на одном корте физически невозможны — гарантия на уровне Postgres.
Подать заявку →Сетка дня — главный экран
На одном экране 6-10 кортов × 14 рабочих часов. Цветовое кодирование — глядя на сетку видишь занятость без чтения текста.
Стандартное хард-покрытие. Indoor или outdoor — отдельные значки.
Глиняный корт. Часто требует обработки между занятиями — летscourt поддерживает блок «технический перерыв».
Реже встречается, в основном на открытых клубах. Свой ценник.
Технический блок (уборка, ремонт, тренировка детей). Не считается в выручке.
Click-to-create — бронь за ≤15 секунд
Клиент звонит, говорит «хочу корт на 19:00 на час». Время от клика админа до отправленного клиенту подтверждения — меньше 15 секунд.
- Click в свободную клетку «19:00 на корте 2»
- Открывается popover с поиском клиента — начинаешь набирать «леб…», подсказка «Лебедева Мария +7…»
- Click в подсказку — клиент выбран
- (Опционально) Выбор тренера и длительности 1-4 часа
- Enter / Click «Создать»
- Бронь создана, клиенту автоматически уходит SMS/Telegram с подтверждением
Если клиента нет в базе — кнопка «+ Создать клиента» прямо в попапе с pre-fill введённого имени. Не нужно открывать отдельную форму.
Hover на броне — быстрый взгляд
Через 250 мс после наведения курсора появляется компактный popover с ключевой информацией:
- ·Клиент: ФИО + телефон
- ·Тип: игра / тренировка / турнир / блок
- ·Тренер: имя если назначен
- ·Стоимость и статус оплаты
- ·Длительность
Удобно когда нужно глянуть «что у меня в 18:00» не открывая полноценный drawer. Снижает количество кликов в админке.
Click на броне — drawer редактирования
Полная информация и возможность изменить любое поле без открытия отдельной страницы.
Стоимость, способ оплаты (онлайн/наличные/перевод), кнопка «Отметить оплачено» если клиент пришёл с наличкой
Inline-замена через select. Назначить, заменить, убрать.
Ссылка на карточку клиента. Клик → открывается профиль с историей всех его броней.
Создание (когда, кем) → оплата → если отменена то когда и кем. Полный audit-trail.
Редактировать (открыть форму) / Отменить (с auto-refund если была оплачена)
Защита от двойных броней — гарантия на БД
Postgres EXCLUDE constraint физически не даёт создать две пересекающиеся брони на одном корте. Это не «постараемся не пересечь» в коде — это constraint в базе данных. Если в один момент времени два администратора создают бронь на одно время одного корта — второй получит ошибку.
CONSTRAINT bookings_no_overlap EXCLUDE USING gist ( court_id WITH =, tstzrange(start_at, end_at) WITH && ) WHERE (status != 'cancelled');
В переводе: «не позволять две записи где court_id одинаковый И диапазоны времени пересекаются (исключая отменённые брони)». btree-gist индекс делает это быстро даже на миллионах записей.
Hold с таймером 10 минут
Клиент через виджет/бот начал бронировать но не оплатил — слот блокируется на 10 минут. Если за это время не пришла оплата — бронь автоматически снимается, слот возвращается в продажу. Воркер expire-holds проверяет каждые 30 секунд.
В админ-инбоксе видны все hold-брони с обратным таймером (обновляется каждую секунду): <2 мин = красная полоска, 2-5 мин = жёлтая, >5 мин = зелёная. Админ видит «вот клиент уже 7 минут оплачивает» и может позвонить.
Запустить расписание клуба
5 минут на регистрацию и добавление кортов. Бронь принимаете в тот же день. Импорт существующих броней из Excel — в один клик.
Подать заявку →