Реализация календаря доступности для бронирования на сайте

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

Разработка и обслуживание любых видов сайтов:

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

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация календаря доступности для бронирования на сайте
Средняя
~5 рабочих дней
Часто задаваемые вопросы

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

Этапы разработки

Последние работы

  • 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

Реализация календаря доступности для бронирования на сайте

Календарь доступности — центральный UI-элемент системы бронирования. Показывает, какие даты открыты для записи, какие заняты, какие заблокированы. От корректности отображения зависит пользовательский опыт и количество ошибочных броней.

Логика доступности

Доступность слота определяется пересечением нескольких источников:

interface AvailabilitySlot {
  datetime:  Date;
  available: boolean;
  reason?:   'booked' | 'blocked' | 'outside_hours' | 'capacity_full';
  capacity?: number;    // для группового бронирования
  remaining?: number;
}

class AvailabilityService {
  async getAvailability(resourceId: number, from: Date, to: Date): Promise<AvailabilitySlot[]> {
    const [schedule, bookings, blocks] = await Promise.all([
      this.getWorkingSchedule(resourceId),  // часы работы
      this.getBookings(resourceId, from, to),
      this.getBlocks(resourceId, from, to), // ручные блокировки
    ]);

    return this.generateSlots(from, to, schedule, bookings, blocks);
  }
}

Компонент календаря (React)

import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { startOfMonth, endOfMonth, eachDayOfInterval, format, isSameDay } from 'date-fns';
import { ru } from 'date-fns/locale';

interface DayAvailability {
  date:      string;
  hasSlots:  boolean;
  allBooked: boolean;
}

export function AvailabilityCalendar({ resourceId, onDateSelect }: Props) {
  const [currentMonth, setCurrentMonth] = useState(new Date());

  const { data: availability } = useQuery({
    queryKey: ['availability', resourceId, format(currentMonth, 'yyyy-MM')],
    queryFn:  () => fetchMonthAvailability(resourceId, currentMonth),
    staleTime: 60_000,
  });

  const days = eachDayOfInterval({
    start: startOfMonth(currentMonth),
    end:   endOfMonth(currentMonth),
  });

  return (
    <div className="grid grid-cols-7 gap-1">
      {/* Заголовки дней недели */}
      {['Пн','Вт','Ср','Чт','Пт','Сб','Вс'].map(d => (
        <div key={d} className="text-center text-xs text-gray-500 py-2">{d}</div>
      ))}

      {/* Дни месяца */}
      {days.map(day => {
        const dateStr  = format(day, 'yyyy-MM-dd');
        const dayData  = availability?.find(a => a.date === dateStr);
        const isToday  = isSameDay(day, new Date());

        return (
          <button
            key={dateStr}
            disabled={!dayData?.hasSlots || dayData?.allBooked}
            onClick={() => onDateSelect(day)}
            className={cn(
              'aspect-square rounded-lg text-sm font-medium transition-colors',
              isToday && 'ring-2 ring-blue-500',
              dayData?.hasSlots && !dayData?.allBooked
                ? 'bg-green-50 text-green-700 hover:bg-green-100'
                : 'bg-gray-50 text-gray-300 cursor-not-allowed'
            )}
          >
            {format(day, 'd')}
          </button>
        );
      })}
    </div>
  );
}

Оптимизация загрузки

Доступность загружается помесячно и кэшируется 60 секунд. При выборе даты грузятся временные слоты конкретного дня. Инвалидация кэша — через WebSocket-событие при создании нового бронирования.

Синхронизация нескольких ресурсов

Для сервисов с несколькими специалистами/залами — выбор конкретного ресурса или показ первого доступного слота среди всех:

async function getFirstAvailableSlot(date: Date, serviceId: number): Promise<Slot | null> {
  const resources = await getServiceResources(serviceId);
  const slots = await Promise.all(
    resources.map(r => getAvailableSlots(r.id, date))
  );
  return slots.flat().sort((a, b) => a.datetime - b.datetime)[0] ?? null;
}

Сроки

Календарь доступности с API и компонентом: 4–6 рабочих дней.