Реалізація бронювання спеціалістів (лікар, майстер, тренер) на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація бронювання спеціалістів (лікар, майстер, тренер) на сайті
Складна
~2-4 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація бронювання спеціалістів (лікар, майстер, тренер) на веб-сайту

Бронювання спеціаліста складніше, ніж бронювання зали. У спеціаліста може бути нерегулярний розклад, перерви між прийомами, відпустки, замінники. Клієнт хоче бачити найближчий вільний слот, а не перебирати дати вручну.

Модель розкладу спеціаліста

Розклад — це кілька шарів з різним пріоритетом:

-- Базовий тижневий шаблон (повторюється щотижня)
CREATE TABLE specialist_schedules (
    id              SERIAL PRIMARY KEY,
    specialist_id   INTEGER NOT NULL,
    weekday         SMALLINT NOT NULL,  -- 0=пн ... 6=нд
    start_time      TIME NOT NULL,
    end_time        TIME NOT NULL,
    slot_duration   INTERVAL DEFAULT '60 minutes',
    break_after     INTERVAL DEFAULT '0',  -- перерва після кожного слота
    valid_from      DATE,
    valid_until     DATE
);

-- Переопределення на конкретні дати (відпустки, зміни, відпуски)
CREATE TABLE specialist_overrides (
    id              SERIAL PRIMARY KEY,
    specialist_id   INTEGER NOT NULL,
    override_date   DATE NOT NULL,
    override_type   VARCHAR(20),  -- 'day_off', 'custom_hours', 'vacation'
    start_time      TIME,
    end_time        TIME,
    reason          VARCHAR(255)
);

-- Заблоковані інтервали (обід, внутрішнє засідання)
CREATE TABLE specialist_blocks (
    id              SERIAL PRIMARY KEY,
    specialist_id   INTEGER NOT NULL,
    starts_at       TIMESTAMP NOT NULL,
    ends_at         TIMESTAMP NOT NULL,
    reason          VARCHAR(100)
);

Алгоритм генерації доступних слотів

def get_available_slots(specialist_id: int, date: date) -> list[TimeSlot]:
    # 1. Перевірити переопределення на цю дату
    override = get_override(specialist_id, date)
    if override and override.type == 'day_off':
        return []
    if override and override.type == 'vacation':
        return []

    # 2. Отримати базовий розклад
    if override and override.type == 'custom_hours':
        work_start, work_end = override.start_time, override.end_time
        slot_duration = override.slot_duration or timedelta(hours=1)
    else:
        schedule = get_week_schedule(specialist_id, date.weekday())
        if not schedule:
            return []
        work_start = schedule.start_time
        work_end = schedule.end_time
        slot_duration = schedule.slot_duration
        break_after = schedule.break_after

    # 3. Згенерувати теоретичні слоти
    slots = []
    current = datetime.combine(date, work_start)
    end_dt = datetime.combine(date, work_end)

    while current + slot_duration <= end_dt:
        slots.append(TimeSlot(start=current, end=current + slot_duration))
        current += slot_duration + (break_after or timedelta(0))

    # 4. Видалити заблоковані інтервали
    blocks = get_blocks(specialist_id, date)
    bookings = get_confirmed_bookings(specialist_id, date)
    busy = blocks + bookings

    return [
        slot for slot in slots
        if not any(slot.overlaps(b) for b in busy)
        and slot.start >= datetime.utcnow() + timedelta(minutes=30)  # не можна бронювати «прямо зараз»
    ]

Найближчий доступний слот

Клієнти часто не знають, коли записатися — вони хочуть «як можна скоріше»:

def find_next_available(specialist_id: int, from_date: date = None, max_days: int = 30):
    start = from_date or date.today()
    for delta in range(max_days):
        check_date = start + timedelta(days=delta)
        slots = get_available_slots(specialist_id, check_date)
        if slots:
            return slots[0], check_date
    return None, None

Заміна спеціаліста

Якщо клієнт хоче попасти до конкретного спеціаліста, але найближчий слот через 2 тижні, система може запропонувати аналогічного спеціаліста:

def find_same_service_specialists(specialist_id: int) -> list[int]:
    # Спеціалісти з тими ж послугами
    return db.query("""
        SELECT DISTINCT s2.id
        FROM specialist_services ss1
        JOIN specialist_services ss2 ON ss1.service_id = ss2.service_id
        JOIN specialists s2 ON ss2.specialist_id = s2.id
        WHERE ss1.specialist_id = %s AND s2.id != %s AND s2.is_active = true
    """, [specialist_id, specialist_id])

Інтерфейс запису

Потік клієнта:

  1. Вибір послуги → фільтрування спеціалістів за послугою
  2. Вибір спеціаліста (або «будь-хто доступний»)
  3. Вибір дати — показується лише якщо є хоча б один слот
  4. Вибір часу — слоти конкретної дати
  5. Форма контактів → hold → оплата / підтвердження

На кроці вибору дати зручно використовувати inline calendar, де сірі дати — немає слотів, зелені — есть:

// react-day-picker + користувацькі modifiers
const disabledDays = dates.filter(d => !d.hasSlots);
const availableDays = dates.filter(d => d.hasSlots);

<DayPicker
  disabled={disabledDays}
  modifiers={{ available: availableDays }}
  modifiersClassNames={{ available: 'day--available' }}
  onDayClick={(day) => fetchSlots(specialist.id, day)}
/>

Сповіщення та нагадування

Створення брони → email клієнту (підтвердження) + email спеціалісту
За 24 години → SMS + email клієнту
За 2 години → SMS клієнту
Скасування клієнтом → email спеціалісту
Скасування спеціалістом → email + SMS клієнту + пропозиція запису до іншого

Відгуки після візиту

Через 24 години після закінчення прийому — автоматичний лист із проханням залишити відгук. Посилання містить підписаний токен, дійсний 7 днів:

token = jwt.encode({
    'booking_id': booking.id,
    'exp': datetime.utcnow() + timedelta(days=7),
}, SECRET_KEY, algorithm='HS256')
review_url = f"https://example.com/review?token={token}"

Строки реалізації

Один спеціаліст, базовий розклад, без оплати — 7–9 робочих днів. Декілька спеціалістів, гнучкий розклад із переопределеннями, заміна спеціаліста, SMS-сповіщення, відгуки — 13–16 робочих днів.