Реалізація розкладу відеоконсультацій на сайті

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

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

Інформаційні сайти або веб-програми
Сайти візитки, 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

Розробка системи записи на відеоконсультації

Система записи — це не просто форма. Потрібно відображати реальну занятість спеціаліста, запобігати двійному бронюванню, враховувати часові пояси, управляти відмовами та переносами, синхронізувати з Google Calendar або Outlook.

Структура доступності

CREATE TABLE availability_schedules (
  id UUID PRIMARY KEY,
  specialist_id UUID REFERENCES specialists(id),
  day_of_week SMALLINT NOT NULL,
  start_time TIME NOT NULL,
  end_time TIME NOT NULL,
  is_active BOOLEAN DEFAULT true
);

CREATE TABLE bookings (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  specialist_id UUID REFERENCES specialists(id),
  client_id UUID REFERENCES users(id),
  starts_at TIMESTAMPTZ NOT NULL,
  ends_at TIMESTAMPTZ NOT NULL,
  status VARCHAR(50) DEFAULT 'confirmed',
  google_event_id VARCHAR(255),
  created_at TIMESTAMPTZ DEFAULT now()
);

Алгоритм вільних слотів

async function getAvailableSlots(
  specialistId: string,
  date: string,
  durationMinutes: number,
  userTimezone: string
): Promise<Array<{ start: string; end: string }>> {
  const schedule = await db.query(
    `SELECT start_time, end_time FROM availability_schedules
     WHERE specialist_id = $1 AND day_of_week = $2 AND is_active = true`,
    [specialistId, getISODayOfWeek(new Date(date))]
  );

  if (!schedule.rows.length) return [];

  const booked = await db.query(
    `SELECT starts_at, ends_at FROM bookings
     WHERE specialist_id = $1 AND DATE(starts_at AT TIME ZONE $3) = $2 AND status = 'confirmed'`,
    [specialistId, date, userTimezone]
  );

  const slots: Array<{ start: string; end: string }> = [];
  let current = parseTime(date, schedule.rows[0].start_time, userTimezone);
  const end = parseTime(date, schedule.rows[0].end_time, userTimezone);

  while (current < end) {
    const slotEnd = addMinutes(current, durationMinutes);
    if (slotEnd > end) break;

    const isBusy = booked.rows.some(b =>
      current < new Date(b.ends_at) && slotEnd > new Date(b.starts_at)
    );

    if (!isBusy) {
      slots.push({
        start: current.toISOString(),
        end: slotEnd.toISOString(),
      });
    }

    current = addMinutes(current, durationMinutes);
  }

  return slots;
}

API бронювання з захистом від двійного бронювання

app.post('/api/bookings', authenticate, async (req, res) => {
  const { specialistId, startsAt, durationMinutes } = req.body;
  const endsAt = addMinutes(new Date(startsAt), durationMinutes);

  try {
    const booking = await db.transaction(async (trx) => {
      const conflict = await trx.query(
        `SELECT id FROM bookings
         WHERE specialist_id = $1 AND status = 'confirmed'
         AND tstzrange(starts_at, ends_at) && tstzrange($2::timestamptz, $3::timestamptz)
         FOR UPDATE NOWAIT`,
        [specialistId, startsAt, endsAt.toISOString()]
      );

      if (conflict.rows.length > 0) {
        throw Object.assign(new Error('Слот займають'), { code: 'CONFLICT' });
      }

      const [booking] = await trx.query(
        `INSERT INTO bookings (specialist_id, client_id, starts_at, ends_at)
         VALUES ($1, $2, $3, $4) RETURNING *`,
        [specialistId, req.user.id, startsAt, endsAt.toISOString()]
      );

      return booking;
    });

    await syncToGoogleCalendar(booking);
    await sendBookingConfirmation(booking, req.user);

    res.json(booking);
  } catch (err: any) {
    if (err.code === 'CONFLICT') {
      return res.status(409).json({ error: 'Слот недоступний' });
    }
    throw err;
  }
});

Синхронізація з Google Calendar

async function syncToGoogleCalendar(booking: Booking) {
  const specialist = await db.specialists.findById(booking.specialist_id);
  if (!specialist.google_calendar_token) return;

  const oauth2Client = new google.auth.OAuth2(...);
  oauth2Client.setCredentials(specialist.google_calendar_token);

  const calendar = google.calendar({ version: 'v3', auth: oauth2Client });
  const client = await db.users.findById(booking.client_id);

  await calendar.events.insert({
    calendarId: 'primary',
    requestBody: {
      summary: `Консультація з ${client.name}`,
      start: { dateTime: booking.starts_at.toISOString() },
      end: { dateTime: booking.ends_at.toISOString() },
      attendees: [{ email: client.email }],
    },
  });
}

Терміни

Система записи з календарем доступності, захистом від двійного бронювання, напоминаннями та синхронізацією з Google Calendar — 1.5–2 тижня.