Rendering Course Dates with Velo and wix-bookings v2

Question:
How can I get all of the dates of a scheduled course session using velo and the booking API? Right now I can only retrieve the start and end dates.

Product:
Wix Studio Editor, Wix Velo, Wix Booking V2 API

What are you trying to achieve:
I am making a site for a pottery studio. They offer several courses at a time. For example, they have 3 Intro to Pottery Classes going on right now. Each Intro to Pottery class is set up as its own course service in my Bookings.

I have a collection where all courses of the same kind are linked, and a dynamic page for each. For example, I have an “Intro to Pottery” page with a multi-reference with each Intro to Pottery Course.

I want to make a dropdown where a customer can choose a specific course and see all the dates that will be included with that course.

What have you already tried:

Dataset Query

On the dynamic page, using $w('#dynamicDataset').getCurrentItem() doesn’t show any scheduling information.

Services Query - Bookings v2

If I query the specific service ID like this:

    const courseDetail = services.queryServices().eq("name", serviceName).find().then((res) => {
                            console.log(res.items)
}

returns the following schedule info:

type: "COURSE",
...
schedule: {
  "firstSessionStart": [DATE]
  "lastSessionEnd": [DATE]
  "availabilityConstraints": {
    "sessionDurations": [
      150
    ],
    "timeBetweenSessions": 0
  },
  "_id": [ID]

getServiceAvailability - Bookings Frontend

Using getServiceAvailability from wix-bookings-frontend like this:

getServiceAvailability(ID).then((availability) => {
                            console.log(availability.slots)

returns and array with one object:

_id: SERVICE_ID
startDateTime: FIRST_CLASS_DATE
endDateTime: LAST_CLASS_DATE
serviceId: SERVICE_ID
capacity: 9
remainingSpots: 9
staffMemberId: STAFF_MEMBER_ID
bookable: true

Any help or insight would be much appreciated!

I heard back from Wix support and they suggested I use wix-booking-backend API. It’s frustrating that I need to do two queries and write a parser just to display what dates a course takes place on.

Here is my code if anyone else needs it:

  1. Make a file in the backend folder. I titled mine getSessions.web.js
import { sessions } from "wix-bookings-backend";
import { Permissions, webMethod } from "wix-web-module";

export const getCourseDates = webMethod(
  Permissions.Anyone, 
  (scheduleId) => { 
    let today = new Date(Date.now())
    let endDate = new Date(today);
    endDate.setMonth(endDate.getMonth() + 3);

    return sessions
    .querySessions()
    .eq("scheduleId", scheduleId)
    .ne("recurrence",null)
    .lt("start.timestamp", today)
    .ge("end.timestamp", endDate)
    .find()
    .then((results) => {
      if (results.items.length > 0) {
        const items = results.items;

        return results.items;
      } else {
        console.log('No matches')
      }
    })
    .catch((error) => {
      console.error(error);
    });  }
);
  1. On your front end, import this function:
import { getCourseDates } from "backend/getSessions.web.js";
  1. The returned JSON doesn’t have the dates, just a start date, end date, and frequency. This is my function to return the dates the course takes place.
function parseClassSchedule(scheduleData) {
    const dayMap = {
        'mo': 'monday',
        'tu': 'tuesday',
        'we': 'wednesday',
        'th': 'thursday',
        'fr': 'friday',
        'sa': 'saturday',
        'su': 'sunday'
      };

    const schedule = [];
    const daysOfWeekArray = []

    for (const event of scheduleData) {
        const { start, end, recurrence } = event;
        // Parse the start and end times
        const startDate = new Date(start.timestamp);

        // Parse the recurrence pattern
        const recurrencePattern = /FREQ=([^;]+);INTERVAL=(\d+);BYDAY=([^;]+);UNTIL=(\d{8})T\d{6}Z/;
        const [, freq, interval, dayOfWeek, until] = recurrence.match(recurrencePattern);
        const endDate = new Date(until.slice(0, 4), parseInt(until.slice(4, 6)) - 1, until.slice(6, 8));
        
        // Convert the days of week to an array
        daysOfWeekArray.push(...dayOfWeek.split(',').map(day => dayMap[day.toLowerCase()]));

        // Get the start time
        const startTime = startDate.getHours() + ':' + startDate.getMinutes().toString().padStart(2, '0');

        // Calculate all the class dates
        let currentDate = new Date(startDate);
        const classDates = [];

        while (currentDate <= endDate) {
            if (daysOfWeekArray.includes(currentDate.toLocaleDateString('en-US', { weekday: 'long' }).toLowerCase())) {
                classDates.push(new Date(currentDate));
            }
            currentDate.setDate(currentDate.getDate() + 7 * interval);
        }

        // Check if the class already exists in the schedule
        const existingClass = schedule.find(cls => cls.daysOfWeek.join(',') === daysOfWeekArray.join(','));

        if (existingClass) {
            existingClass.startTimes.push(...startTime);
            existingClass.dates.push(...classDates);
        } else {
            schedule.push({
                startDate: startDate,
                endDate: endDate,
                dates: classDates,
                daysOfWeek: daysOfWeekArray,
                startTimes: [startTime]
            });
        }
    }
    // Sort dates
    schedule.forEach(item => {
        item.dates.sort((a, b) => a - b);
    });

    return schedule;
}