Timetable with wix code help

Good afternoon
I am struggling with this for days,maybe somebody can help me.
I followed this tutorial https://support.wix.com/en/article/corvid-creating-a-bookings-timetable
When the lightbox opens up and I press book now nothing happens
Each element has the same id and the same property as in the example
There is no error in the developer tools but google console says
Missing injection from Bookings server NULL

The same error comes up in the example page given by wix.
I have a premium plan on my website so the payment can be processed
Any idea what could cause this problem?Thank you

My page https://www.guildfl.com/event-calendar

There is also an already made Wix Editor tutorial about this here.
https://www.wix.com/corvid/example/timetable

What code have you got for the page and the lightbox so that we can check it over to make sure that there is nothing that has been missed out by mistake or in the wrong place etc.

Have you got your Schedule, Services and Staff collections all set up right and connected?

Have you actually got Wix Bookings app installed on your site?

This is the code from the Timetable tutorial that I linked to above.

  
//-------------Imports-------------//

// Import session storage to pass information between pages.
import {session} from "wix-storage";

//-------------Page Setup-------------//

$w.onReady(async function () {
// Get the booked service data from session storage.
const bookedStart = new Date(session.getItem("bookedStart"));
const bookedEnd = new Date(session.getItem("bookedEnd"));
const serviceId = session.getItem("serviceId");

// Clear session storage.
session.removeItem("bookedStart");
session.removeItem("bookedEnd");
session.removeItem("serviceId");
// Populate the page with the booking details.
$w("#bookedDate").text = getFullDate(bookedStart);
$w("#bookedTime").text = `${getTimeOfDay(bookedStart)} - ${getTimeOfDay(bookedEnd)}`;
});

//-------------Date and Time Helpers-------------//

// Get the give date formated as Day, Mon dd yyyyy (e.g. Tue, Dec 18 2018).
function getFullDate(date) {
return date.toLocaleDateString([], { weekday: "long", year: "numeric", month: "long", day: "numeric" });
}

// Get the time formated as HH:MM AM/PM.
function getTimeOfDay(date) {
return date.toLocaleTimeString([], {hour: "2-digit", minute:"2-digit"});
}

This is the code from the Timetable tutorial that I linked to above.

  
//-------------Imports-------------//

// Import the wix-data module for working with queries.
import wixData from "wix-data";
// Import the wix-bookings module for getting available service slots.
import wixBookings from "wix-bookings";
// Import the wix-window module for determining the type of device the site is being viewed on.
import wixWindow from "wix-window";
// Import session storage to pass information between pages.
import { session } from "wix-storage";

//-------------Global Variables-------------//

// The selected day.
let activeDay;
// Map of staff members.
let staffMap = {};
// Map of services.
let servicesMap = {};
// List of services.
let services = [];
// Whether the site is being viewed on a mobile device.
let isMobile;

//-------------Page Setup-------------//

$w.onReady(function () {
// Get whether the the site is being viewed on a mobile device.
isMobile = wixWindow.formFactor === "Mobile";
handleLoaders(true);
initializeSlots();
});

function handleLoaders(show = true) {
if (show === true && isMobile) {
$w("#slotsLoaderMobile").show()
$w("#slotsLoader").hide()
$w("#toolbarLoaderMobile").show()
$w("#toolbarLoader").hide()
}
else if (show === true && !isMobile) {
$w("#slotsLoaderMobile").hide()
$w("#slotsLoader").show()
$w("#toolbarLoaderMobile").hide()
$w("#toolbarLoader").show()
}
else if (show === false) {
$w("#slotsLoaderMobile").hide()
$w("#slotsLoader").hide()
$w("#toolbarLoaderMobile").hide()
$w("#toolbarLoader").hide()
}
}

async function initializeSlots() {
// Set the global services list to all the services of type class from the Services collection.
services = await getAllClasses();
// Put the services in a map for easy access by ID.
services.forEach(service => servicesMap[service._id] = service);

// Get all the staff from the Staff collection.
const staff = await getAllStaff();
// Put the staff members in a map for easy access by ID.
staff.forEach(member => staffMap[member._id] = member);

// Set the selected day to today's date.
setActiveDay(new Date());

// Set up the days toolbar functionality and show relevant dates.
setupDaysToolbar();

}

// Set up the days toolbar functionality and show relevant dates.
function setupDaysToolbar() {
// Set the first day as the global active day.
let firstDay = activeDay;
// Populate the day toolbar, starting from the global active day.
populateDaysToolbar(firstDay);
// If the site is being viewed on a mobile device:
if (isMobile) {
// Set the back button to go back one day when clicked.
$w("#backButton").onClick(() => {
setActiveDay(getPreviousMidnight(activeDay));
firstDay = activeDay;
populateDaysToolbar(firstDay);
});
// Set the forward button to go forward one day when clicked.
$w("#forwardButton").onClick(() => {
setActiveDay(getNextMidnight(activeDay));
firstDay = activeDay;
populateDaysToolbar(firstDay);
});
}
// If the site is not being viewed on a mobile device:
else {
// Set the back button to go back one week when clicked.
$w("#backButton").onClick(() => {
firstDay = getDatePreviousWeek(firstDay);
populateDaysToolbar(firstDay);
});
// Set the forward button to go forward one week when clicked.
$w("#forwardButton").onClick(() => {
firstDay = getDateNextWeek(firstDay);
populateDaysToolbar(firstDay);
});
}
handleLoaders(false);
$w("#daysToolbar").show();
}

// Populate the day toolbar, starting from the given date.
function populateDaysToolbar(startDate) {
// Reset the repeater's data to an empty array.
$w("#daysToolbar").data = [];
// If the site is being viewed on a mobile device set the repeater's data to the global active day.
// If not, set the repeater's data to a week's worth of days starting with the given start date.
$w("#daysToolbar").data = isMobile ? [{ _id: "day", date: activeDay }] : getWeekDays(startDate);
}

//-------------Update Page for Active Day-------------//

// Set the given date to the selected day in the days toolbar and populate
// the services list with the slots available on the selected day.
function setActiveDay(date) {
// Set the global active day to the given date.
activeDay = date;
// Get a date range spanning the given date.
const dayRange = getDayRange(date);
// Populate the slots repeater with the slots available for the services in the global services list during the given date range.
populateServicesSlots(dayRange.startDateTime, dayRange.endDateTime);
// For each day in the days toolbar:
$w("#daysToolbar").forEachItem(($item, itemData, index) => {
// If the site is not being viewed on a mobile device:
if (!isMobile) {
// If the newly selected day is the same as the already selected day.
if (isSameDay(itemData.date, activeDay)) {
// Disable the day selection button because it is already selected.
$item("#dayPickerButton").disable();
}
// If the newly selected day is not the same as the already selected day.
else {
// Enable the day selection button so this day can be selected.
$item("#dayPickerButton").enable();
}
}
});
}

// Populate the slots repeater with the slots available for the services in the global services list during the given date range.
async function populateServicesSlots(startDateTime, endDateTime) {
// Get the available slots from all the services for the given date range.
let availableSlots = await getMultipleServicesAvailability(services, { startDateTime, endDateTime });
// Sort the available slots in ascending order by start time.
availableSlots = availableSlots.sort((a, b) => a.startDateTime - b.startDateTime);
// Set the slot repeater's data to the sorted available slots, thereby populating the repeater.
$w("#slotRepeater").data = availableSlots;
// Hide the slots loader image.
$w("#slotsLoader").hide();

// If there is at least one available slot:
if (availableSlots.length > 0) {
// Hide the no services message.
$w("#noServices").hide();
// Show the slots repeater.
$w("#slotRepeater").show();
}
// If there are no available slots:
else {
// Show the no services message.
$w("#noServices").show();
// Hide the slots repeater.
$w("#slotRepeater").hide();
}
}

//-------------Repeaters Setup-------------//

// Set up each item in the days toolbar repeater as it is loaded.
export function daysToolbar_itemReady($item, itemData, index) {
// Populate the day field with the item's day of the week (e.g. Tue).
$item("#day").text = daysOfTheWeek[itemData.date.getDay()];
// Populate the date field with the item's date (e.g. 18/12).
$item("#date").text = getMonthDay(itemData.date);

// For mobile devices, there is no day selection button because only one day is shown at a time.
// If the site is not being viewed on a mobile device:
if (!isMobile) {
// If the current item's day is the same as the already selected day.
if (isSameDay(itemData.date, activeDay)) {
// Disable the day selection button because it is already selected.
$item("#dayPickerButton").disable();
}
// If the current item's day is not the same as the already selected day.
else {
// Enable the day selection button because it is already selected.
$item("#dayPickerButton").enable();
}

// Set the day selection button to change the selected day and the displayed services when clicked.
$item("#dayPickerButton").onClick(() => {
setActiveDay(itemData.date);
});
}
}

// Set up each item in the slot repeater as it is loaded.
export function slotRepeater_itemReady($item, itemData, index) {
// Get the duration of the slot from the item's start and end dates (e.g. 1 hr 30 min).
const duration = getDuration(itemData.startDateTime, itemData.endDateTime);
// Get the slot's service name from the global service map using the item's service ID.
const serviceName = servicesMap[itemData.serviceId].serviceName;
// Get the slot's staff member name from the global staff map using the item's staff member ID.
const staffMember = staffMap[itemData.staffMemberId].name;
// Get the service's form fields from the global services map using the item's service ID.
const form = servicesMap[itemData.serviceId].form;

// Populate the item's display fields.
$item("#time").text = getTimeOfDay(itemData.startDateTime);
$item("#duration").text = duration;
$item("#spots").text = itemData.remainingSpots + " spots left";
$item("#serviceName").text = serviceName;
$item("#staffMember").text = staffMember;

// If the slot's start time has already passed:
if (itemData.startDateTime < Date.now()) {
// Disable the book button.
$item("#bookButton").disable();
// Change the book button's label to say "Passed".
$item("#bookButton").label = "Passed";
}
// If the slot's start time has not passed yet:
else {
// If there are no spots remaining in the slot:
if (itemData.remainingSpots === 0) {
// Disable the book button. 
$item("#bookButton").disable();
// Change the book button's label to say "Full".
$item("#bookButton").label = "Full";
}
// If there are remaining spots in the slot: 
else {
// Enable the book button. 
$item("#bookButton").enable();
// Change the book button's label to say "Book Now".
$item("#bookButton").label = "Book Now";
// Set the book button to open the booking form, passing relevant data to the form, when clicked.
$item("#bookButton").onClick(event => {
wixWindow.openLightbox("Booking Form", {
slot: itemData,
form: servicesMap[itemData.serviceId].form
});
});
}
}
}

//-------------Bookings Data Retrieval-------------//

// Get all the staff members from the Staff collection.
async function getAllStaff() {
const data = await wixData.query("Bookings/Staff").find();
return data.items;
}

// Get all services of type class from the Services collection.
async function getAllClasses() {
const data = await wixData.query("Bookings/Services").eq("serviceType", "CLASS").find();
return data.items;
}

// Get the availability of multiple given services for the given date range. Optimize the service availability 
// requests by batching all the service availability requests into one promise.
async function getMultipleServicesAvailability(requestedServices, availabilityOptions) {
// Create an empty list of Promises.
let slotsPromises = [];
// Create an empty list of slots.
let slots = [];
// For each of the given requested services:
requestedServices.forEach(requestedservice => {
// Make a call to get the service's available slots and store the function's returned Promise.
const slotsPromise = wixBookings.getServiceAvailability(requestedservice._id, availabilityOptions).then(result => {
// When the Promise is resolved, add the available slots to the global slot list.
result.slots.forEach(slot => slots.push(slot));
});
// Add the service availability Promise to the list of Promises.
slotsPromises.push(slotsPromise);
});
// Wait for all the availability calls for all the services to finish. 
await Promise.all(slotsPromises);
// Return the available slots found from all the services.
return slots;
}

//-------------Date and Time Helpers-------------//

// Map of the days of the week for display purposes.
const daysOfTheWeek = {
0: "SUN",
1: "MON",
2: "TUE",
3: "WED",
4: "THU",
5: "FRI",
6: "SAT"
};

// Get a list of the next 7 days of the week, starting from a given date.
function getWeekDays(startDate) {
// Create an empty list of days.
let weekDays = [];
// Get the midnight that started today's day.
let current = getMidnight(startDate);

// For 7 days:
for (let i = 0; i < 7; i++) {
// Add to the list of days an object with a running day ID and the day's date.
weekDays.push({
_id: "day" + i,
date: current
});
// Get the midnight of the next day.
current = getNextMidnight(current);
}
// Return the list of days.
return weekDays;
}

// Gets a date range object where the start is the midnight at the beginning of the given date
// and the end is the midnight at the end of the given date.
function getDayRange(date) {
// Get the date's starting midnight.
const startDateTime = getMidnight(date);
// Get the date's ending midnight.
const endDateTime = getNextMidnight(date);
// Return the range.
return {
startDateTime,
endDateTime
}
}

// Get the midnight that started the given date's day.
function getMidnight(date) {
// Create a new date which is a copy of the given date.
let midnight = new Date(date);
// Set the new date's time to the previous midnight.
midnight.setHours(0, 0, 0, 0);
// Return the new date.
return midnight;
}

// Get the midnight that starts the day after the given date.
function getNextMidnight(date) {
// Create a new date which is a copy of the given date.
let midnight = new Date(date);
// Set the new date's time to the next midnight.
midnight.setHours(24, 0, 0, 0);
// Return the new date.
return midnight;
}

// Get the midnight that starts the day before the given date.
function getPreviousMidnight(date) {
// Create a new date which is a copy of the given date.
let midnight = new Date(date);
// Set the new date's time to the midnight that starts the previous day.
midnight.setHours(-24, 0, 0, 0);
// Return the new date.
return midnight;
}

// Get a duration text from a given date range (e.g. 1 hr 30 min).
function getDuration(start, end) {
// Calculate the duration in milliseconds.
let diff = Math.abs(start - end);
// Calculate the number of milliseconds in a minute.
const minute = 1000 * 60;
// Calculate the duration in minutes.
const total_minutes = Math.floor(diff / minute);
// Calculate how many hours are in the duration.
const hours = Math.floor(total_minutes / 60);
// Calculate how many minutes are left over. 
const minutes = total_minutes % 60;
// Create the hours text.
const hoursOutput = hours === 0 ? `` : hours === 1 ? `1 hr` : `${hours} hrs`;
// Create the minutes text.
const minutesOutput = minutes === 0 ? `` : `${minutes} min`;
// Return the hours and minutes texts.
return `${hoursOutput} ${minutesOutput}`;
}

// Check if two dates are the same day.
function isSameDay(date1, date2) {
return getMidnight(date1).getTime() === getMidnight(date2).getTime();
}

// Get the time of the given date formatted as HH:MM AM/PM.
function getTimeOfDay(date) {
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }).toLowerCase();
}
// Get the month and day of the given date formatted as MM/DD.
function getMonthDay(date) {
return date.toLocaleDateString("en-GB", { day: "numeric", month: "numeric" });
}

const hoursInAWeek = 24 * 7;

// Get the date one week from the given date.
function getDateNextWeek(date) {
let nextWeek = new Date(date);
nextWeek.setHours(hoursInAWeek);
return nextWeek;
}

// Get the date one week before the given date.
function getDatePreviousWeek(date) {
let prevWeek = new Date(date);
prevWeek.setHours(-hoursInAWeek);
return prevWeek;
}

Thank you so much for getting back to me. Yes I have wix bookings on the page,for a while now and currently taking bookings so that part works good.

I have built this up following the tutorial so my code is like the one in the tutorial. I have opened the template website in the editor and made sure that each element has the same id and the properties,i am spinning this for two weeks now,I must be missing something and I don’t understand what. As there is no error in the code neither in the developer part.

The calendar part works up to the part where should redirect to the payment part/booking finishing,when I click on a date it shows the relevant booking,I click on book now opens up the lightbox I fill up the details but nothing happens. Maybe somehow the data is not being imported from the calendar page to the lightbox? Can you help me with some ideas?
If i try the template page,it happens the same thing,the whole thing works up until i fill up the lightbox

On my page the calendar is not yet published but can be accessed on the link https://www.guildfl.com/event-calendar

Anyone,I have added catch error code still no error shows,really confusing not sure where to turn

Hi,

Looking at the code from the tutorial page that I linked to - https://www.wix.com/corvid/example/timetable

You would have assumed that it would be the same as stated in the tutorial that you have already been using - https://support.wix.com/en/article/corvid-creating-a-bookings-timetable

However, that does not seem to be the case here and we have separate but similar codes for the two tutorials.

So, can you please paste up the code that you have used on your website, so that I can check it against the tutorial that you were using and not from the one I have linked to which is different.

At least then we can both be on the same page and using the same code from the same tutorial.

In the meanwhile and I know that this will involve more work for you, however have you tried duplicating this page and using the code that is from the tutorial that I have linked?

If both sets of tutorial code does not work, then we can surely guess that there is a fault somewhere, although please test on a published site and not just through the preview/

Okay, hold fire on that above post about showing your code as I think it might just be a simple case of forgotten to do something.

On your page code the ‘bookButton’, the backButton’ and the forwardButton’ onClick event is added within the code, so that you don’t have to do anything with the element itself.

$item(“#bookButton”).onClick( event => {
$w(“#backButton”).onClick(() => {
$w(“#forwardButton”).onClick(() => {

Whereas the code for the bookButton on the actual lightbox itself is not.

Step 19: Create the bookButton_click Function

The bookButton function is an event handler that is wired to the bookButton’s onClick event. It gathers all the booking information and performs the booking.

export async function bookButton_click(event) {

Therefore, you need to go to the lightbox and click on the ‘bookButton’ on there and in the properties panel for that element, you need to add the onClick event for it.

It will probably add another line of code at the bottom of your page again with the export function bookButton.etc. However, as you have already got that line already changed to export async function in your code then you don’t need it again.

I have also double checked on the tutorial that I had linked to as well and have attached the pic below so you can see what events have been added through the properties panel yourself.

Finally, I can agree with you if you are saying that it should have been mentioned better in the tutorial, however, if you are doing a tutorial as complex as this and not just on a beginners tutorial for example, then Wix could assume that when it talks about ‘The bookButton function is an event handler that is wired to the bookButton’s onClick event’, that the user would know to add the onClick event through the properties panel for that element.

Anyways, I do hope this is the cause of your issue and that it is now solved and working fine.

If not then came back with your code that you have used and we can check that as well.

Thank you for your time and for your support.I have added the code from the tutorial so we should have the same code now

There is something that I would like to mention,when i open the template page(offered by wix)and try the whole calendar feature I also get an error the same as on my own page.

Now I have added the properties to the button,and added a console.log to be able to understand maybe what is wrong,when I press the book button i get the error
code: -10010
message: “Over Capacity” not sure if it makes sense,i tried booking different services but I am getting the same error

The code used for the schedule page is,I have opened the template website and clicked each element on the page to make sure that each one has the same properties.I have notice that the template page and the code example given in the tutorial is slightly different in parts.

//-------------Imports-------------//

// Import the wix-data module for working with queries.
import wixData from “wix-data”;
// Import the wix-bookings module for getting available service slots.
import wixBookings from “wix-bookings”;
// Import the wix-window module for determining the type of device the site is being viewed on.
import wixWindow from “wix-window”;
// Import session storage to pass information between pages.
import { session } from “wix-storage”;

//-------------Global Variables-------------//

// The selected day.
let activeDay;
// Map of staff members.
let staffMap = {};
// Map of services.
let servicesMap = {};
// List of services.
let services = ;
// Whether the site is being viewed on a mobile device.
let isMobile;

//-------------Page Setup-------------//

$w.onReady( function () {
// Get whether the the site is being viewed on a mobile device.
isMobile = wixWindow.formFactor === “Mobile”;
handleLoaders( true );
initializeSlots();
});

function handleLoaders(show = true ) {
if (show === true && isMobile) {
$w(“#slotsLoaderMobile”).show()
$w(“#slotsLoader”).hide()
$w(“#toolbarLoaderMobile”).show()
$w(“#toolbarLoader”).hide()
}
else if (show === true && !isMobile) {
$w(“#slotsLoaderMobile”).hide()
$w(“#slotsLoader”).show()
$w(“#toolbarLoaderMobile”).hide()
$w(“#toolbarLoader”).show()
}
else if (show === false ) {
$w(“#slotsLoaderMobile”).hide()
$w(“#slotsLoader”).hide()
$w(“#toolbarLoaderMobile”).hide()
$w(“#toolbarLoader”).hide()
}
}

async function initializeSlots() {
// Set the global services list to all the services of type class from the Services collection.
services = await getAllClasses();
// Put the services in a map for easy access by ID.
services.forEach(service => servicesMap[service._id] = service);

// Get all the staff from the Staff collection.
const staff = await getAllStaff();
// Put the staff members in a map for easy access by ID.
staff.forEach(member => staffMap[member._id] = member);

// Set the selected day to today’s date.
setActiveDay( new Date());

// Set up the days toolbar functionality and show relevant dates.
setupDaysToolbar();

}

// Set up the days toolbar functionality and show relevant dates.
function setupDaysToolbar() {
// Set the first day as the global active day.
let firstDay = activeDay;
// Populate the day toolbar, starting from the global active day.
populateDaysToolbar(firstDay);
// If the site is being viewed on a mobile device:
if (isMobile) {
// Set the back button to go back one day when clicked.
$w(“#backButton”).onClick(() => {
setActiveDay(getPreviousMidnight(activeDay));
firstDay = activeDay;
populateDaysToolbar(firstDay);
});
// Set the forward button to go forward one day when clicked.
$w(“#forwardButton”).onClick(() => {
setActiveDay(getNextMidnight(activeDay));
firstDay = activeDay;
populateDaysToolbar(firstDay);
});
}
// If the site is not being viewed on a mobile device:
else {
// Set the back button to go back one week when clicked.
$w(“#backButton”).onClick(() => {
firstDay = getDatePreviousWeek(firstDay);
populateDaysToolbar(firstDay);
});
// Set the forward button to go forward one week when clicked.
$w(“#forwardButton”).onClick(() => {
firstDay = getDateNextWeek(firstDay);
populateDaysToolbar(firstDay);
});
}
handleLoaders( false );
$w(“#daysToolbar”).show();
}

// Populate the day toolbar, starting from the given date.
function populateDaysToolbar(startDate) {
// Reset the repeater’s data to an empty array.
$w(“#daysToolbar”).data = ;
// If the site is being viewed on a mobile device set the repeater’s data to the global active day.
// If not, set the repeater’s data to a week’s worth of days starting with the given start date.
$w(“#daysToolbar”).data = isMobile ? [{ _id: “day”, date: activeDay }] : getWeekDays(startDate);
}

//-------------Update Page for Active Day-------------//

// Set the given date to the selected day in the days toolbar and populate
// the services list with the slots available on the selected day.
function setActiveDay(date) {
// Set the global active day to the given date.
activeDay = date;
// Get a date range spanning the given date.
const dayRange = getDayRange(date);
// Populate the slots repeater with the slots available for the services in the global services list during the given date range.
populateServicesSlots(dayRange.startDateTime, dayRange.endDateTime);
// For each day in the days toolbar:
$w(“#daysToolbar”).forEachItem(($item, itemData, index) => {
// If the site is not being viewed on a mobile device:
if (!isMobile) {
// If the newly selected day is the same as the already selected day.
if (isSameDay(itemData.date, activeDay)) {
// Disable the day selection button because it is already selected.
$item(“#dayPickerButton”).disable();
}
// If the newly selected day is not the same as the already selected day.
else {
// Enable the day selection button so this day can be selected.
$item(“#dayPickerButton”).enable();
}
}
});
}

// Populate the slots repeater with the slots available for the services in the global services list during the given date range.
async function populateServicesSlots(startDateTime, endDateTime) {
// Get the available slots from all the services for the given date range.
let availableSlots = await getMultipleServicesAvailability(services, { startDateTime, endDateTime });
// Sort the available slots in ascending order by start time.
availableSlots = availableSlots.sort((a, b) => a.startDateTime - b.startDateTime);
// Set the slot repeater’s data to the sorted available slots, thereby populating the repeater.
$w(“#slotRepeater”).data = availableSlots;
// Hide the slots loader image.
$w(“#slotsLoader”).hide();

// If there is at least one available slot:
if (availableSlots.length > 0) {
// Hide the no services message.
$w(“#noServices”).hide();
// Show the slots repeater.
$w(“#slotRepeater”).show();
}
// If there are no available slots:
else {
// Show the no services message.
$w(“#noServices”).show();
// Hide the slots repeater.
$w(“#slotRepeater”).hide();
}
}

//-------------Repeaters Setup-------------//

// Set up each item in the days toolbar repeater as it is loaded.
export function daysToolbar_itemReady($item, itemData, index) {
// Populate the day field with the item’s day of the week (e.g. Tue).
$item(“#day”).text = daysOfTheWeek[itemData.date.getDay()];
// Populate the date field with the item’s date (e.g. 18/12).
$item(“#date”).text = getMonthDay(itemData.date);

// For mobile devices, there is no day selection button because only one day is shown at a time.
// If the site is not being viewed on a mobile device:
if (!isMobile) {
// If the current item’s day is the same as the already selected day.
if (isSameDay(itemData.date, activeDay)) {
// Disable the day selection button because it is already selected.
$item(“#dayPickerButton”).disable();
}
// If the current item’s day is not the same as the already selected day.
else {
// Enable the day selection button because it is already selected.
$item(“#dayPickerButton”).enable();
}

// Set the day selection button to change the selected day and the displayed services when clicked.
$item(“#dayPickerButton”).onClick(() => {
setActiveDay(itemData.date);
});
}
}

// Set up each item in the slot repeater as it is loaded.
export function slotRepeater_itemReady($item, itemData, index) {
// Get the duration of the slot from the item’s start and end dates (e.g. 1 hr 30 min).
const duration = getDuration(itemData.startDateTime, itemData.endDateTime);
// Get the slot’s service name from the global service map using the item’s service ID.
const serviceName = servicesMap[itemData.serviceId].serviceName;
// Get the slot’s staff member name from the global staff map using the item’s staff member ID.
const staffMember = staffMap[itemData.staffMemberId].name;
// Get the service’s form fields from the global services map using the item’s service ID.
const form = servicesMap[itemData.serviceId].form;

// Populate the item’s display fields.
$item(“#time”).text = getTimeOfDay(itemData.startDateTime);
$item(“#duration”).text = duration;
$item(“#spots”).text = itemData.remainingSpots + " spots left";
$item(“#serviceName”).text = serviceName;
$item(“#staffMember”).text = staffMember;

// If the slot’s start time has already passed:
if (itemData.startDateTime < Date.now()) {
// Disable the book button.
$item(“#bookButton”).disable();
// Change the book button’s label to say “Passed”.
$item(“#bookButton”).label = “Passed”;
}
// If the slot’s start time has not passed yet:
else {
// If there are no spots remaining in the slot:
if (itemData.remainingSpots === 0) {
// Disable the book button.
$item(“#bookButton”).disable();
// Change the book button’s label to say “Full”.
$item(“#bookButton”).label = “Full”;
}
// If there are remaining spots in the slot:
else {
// Enable the book button.
$item(“#bookButton”).enable();
// Change the book button’s label to say “Book Now”.
$item(“#bookButton”).label = “Book Now”;
// Set the book button to open the booking form, passing relevant data to the form, when clicked.
$item(“#bookButton”).onClick(event => {
wixWindow.openLightbox(“Booking Form”, {
slot: itemData,
form: servicesMap[itemData.serviceId].form
});
});
}
}
}

//-------------Bookings Data Retrieval-------------//

// Get all the staff members from the Staff collection.
async function getAllStaff() {
const data = await wixData.query(“Bookings/Staff”).find();
return data.items;
}

// Get all services of type class from the Services collection.
async function getAllClasses() {
const data = await wixData.query(“Bookings/Services”).eq(“serviceType”, “CLASS”).find();
return data.items;
}

// Get the availability of multiple given services for the given date range. Optimize the service availability
// requests by batching all the service availability requests into one promise.
async function getMultipleServicesAvailability(requestedServices, availabilityOptions) {
// Create an empty list of Promises.
let slotsPromises = ;
// Create an empty list of slots.
let slots = ;
// For each of the given requested services:
requestedServices.forEach(requestedservice => {
// Make a call to get the service’s available slots and store the function’s returned Promise.
const slotsPromise = wixBookings.getServiceAvailability(requestedservice._id, availabilityOptions).then(result => {
// When the Promise is resolved, add the available slots to the global slot list.
result.slots.forEach(slot => slots.push(slot));
});
// Add the service availability Promise to the list of Promises.
slotsPromises.push(slotsPromise);
});
// Wait for all the availability calls for all the services to finish.
await Promise.all(slotsPromises);
// Return the available slots found from all the services.
return slots;
}

//-------------Date and Time Helpers-------------//

// Map of the days of the week for display purposes.
const daysOfTheWeek = {
0: “SUN”,
1: “MON”,
2: “TUE”,
3: “WED”,
4: “THU”,
5: “FRI”,
6: “SAT”
};

// Get a list of the next 7 days of the week, starting from a given date.
function getWeekDays(startDate) {
// Create an empty list of days.
let weekDays = ;
// Get the midnight that started today’s day.
let current = getMidnight(startDate);

// For 7 days:
for ( let i = 0; i < 7; i++) {
// Add to the list of days an object with a running day ID and the day’s date.
weekDays.push({
_id: “day” + i,
date: current
});
// Get the midnight of the next day.
current = getNextMidnight(current);
}
// Return the list of days.
return weekDays;
}

// Gets a date range object where the start is the midnight at the beginning of the given date
// and the end is the midnight at the end of the given date.
function getDayRange(date) {
// Get the date’s starting midnight.
const startDateTime = getMidnight(date);
// Get the date’s ending midnight.
const endDateTime = getNextMidnight(date);
// Return the range.
return {
startDateTime,
endDateTime
}
}

// Get the midnight that started the given date’s day.
function getMidnight(date) {
// Create a new date which is a copy of the given date.
let midnight = new Date(date);
// Set the new date’s time to the previous midnight.
midnight.setHours(0, 0, 0, 0);
// Return the new date.
return midnight;
}

// Get the midnight that starts the day after the given date.
function getNextMidnight(date) {
// Create a new date which is a copy of the given date.
let midnight = new Date(date);
// Set the new date’s time to the next midnight.
midnight.setHours(24, 0, 0, 0);
// Return the new date.
return midnight;
}

// Get the midnight that starts the day before the given date.
function getPreviousMidnight(date) {
// Create a new date which is a copy of the given date.
let midnight = new Date(date);
// Set the new date’s time to the midnight that starts the previous day.
midnight.setHours(-24, 0, 0, 0);
// Return the new date.
return midnight;
}

// Get a duration text from a given date range (e.g. 1 hr 30 min).
function getDuration(start, end) {
// Calculate the duration in milliseconds.
let diff = Math.abs(start - end);
// Calculate the number of milliseconds in a minute.
const minute = 1000 * 60;
// Calculate the duration in minutes.
const total_minutes = Math.floor(diff / minute);
// Calculate how many hours are in the duration.
const hours = Math.floor(total_minutes / 60);
// Calculate how many minutes are left over.
const minutes = total_minutes % 60;
// Create the hours text.
const hoursOutput = hours === 0 ? : hours === 1 ? `1 hr` : `${hours} hrs`; // Create the minutes text. **const** minutesOutput = minutes === 0 ? : ${minutes} min;
// Return the hours and minutes texts.
return ${hoursOutput} ${minutesOutput};
}

// Check if two dates are the same day.
function isSameDay(date1, date2) {
return getMidnight(date1).getTime() === getMidnight(date2).getTime();
}

// Get the time of the given date formatted as HH:MM AM/PM.
function getTimeOfDay(date) {
return date.toLocaleTimeString(, { hour: “2-digit”, minute: “2-digit” }).toLowerCase();
}

// Get the month and day of the given date formatted as MM/DD.
function getMonthDay(date) {
return date.toLocaleDateString(“en-GB”, { day: “numeric”, month: “numeric” });
}

const hoursInAWeek = 24 * 7;

// Get the date one week from the given date.
function getDateNextWeek(date) {
let nextWeek = new Date(date);
nextWeek.setHours(hoursInAWeek);
return nextWeek;
}

// Get the date one week before the given date.
function getDatePreviousWeek(date) {
let prevWeek = new Date(date);
prevWeek.setHours(-hoursInAWeek);
return prevWeek;
}

The code for the lightbox Booking Form,I have added a console.log to be able maybe to detect the issue

//-------------Imports-------------//

// Import the wix-bookings module for booking classes.
import wixBookings from “wix-bookings”;
// Import the wix-window module for getting data passed to the lightbox.
import wixWindow from “wix-window”;
// Import the wix-location module for navigating to the thank you page.
import wixLocation from “wix-location”;
// Import session storage to pass information between pages.
import { session } from “wix-storage”;

//-------------Global Variables-------------//

// Slot to book.
let slot;

//-------------Lightbox Setup-------------//

$w.onReady( function () {
// Get the data sent from the Schedule page.
const context = wixWindow.lightbox.getContext();

// Set the global slot to the slot that was sent from the Schedule page.
slot = context.slot;
// Get the form fields that were sent from the Schedule page.
const fields = context.form.fields;
// Set the form repeater’s data to the form fields.
$w(“#formRepeater”).data = fields;
});

//-------------Repeater Setup-------------//

// Set up each item in the form repeater as it is loaded.
export function formRepeater_itemReady($item, itemData, index) {
// Set up the item’s input field.
$item(“#input”).placeholder = itemData.label;
$item(“#input”).inputType = itemData.type;
$item(“#input”).required = itemData.constraints.required;
$item(“#input”).resetValidityIndication();
}

//-------------Form Submission-------------//

// Set what happens when the book button is clicked.
export async function bookButton_click(event) {
// Disable the book button.
$w(“#bookButton”).disable();
// Hide the error text.
$w(“#errorText”).hide();
// Set the validity indicator to true.
let isValid = true ;
// Create an empty form fields list.
let formFields = ;

// For each item in the form:
$w(“#formRepeater”).forEachItem(($item, itemData, index) => {
// If its value is not valid.
if (!$item(“#input”).valid) {
// Set the validity indicator to false.
isValid = false ;
}
// Get the item’s value.
const value = $item(“#input”).value;
// Get the item’s ID.
const _id = itemData._id;
// Add the item’s value and ID to the list of form fields.
formFields.push({ _id, value });
});

// Create a booking info object using the slot passed from the Schedule page
// and the form field data collected in this lightbox.
const bookingInfo = {
slot,
formFields
};

// Set the payment options to an online payment.
const paymentOptions = {
paymentType: “wixPay_Online”
};

// If all the fields have valid values:
if (isValid) {
try {
// Perform the booking for the requested slot using the booking info and payment options from above.
const response = await wixBookings.checkoutBooking(bookingInfo, paymentOptions);
// If the booking is confirmed:
if (response.status === “Confirmed”) {
// Place slot data in session storage to be used on the thank you page.
session.setItem(“bookedStart”, new Date(slot.startDateTime));
session.setItem(“bookedEnd”, new Date(slot.endDateTime));
session.setItem(“serviceId”, slot.serviceId);

// Navigate to the thank you page.
wixLocation.to(“/thank-you”);
}
// If the booking is not confirmed:
else {
// Show the error text.
$w(“#errorText”).show();

        } 
    }  

// If an error occurred in the booking process:
catch (error) {
// Show the error text.
$w(“#errorText”).show();
// Enable the book button.
$w(“#bookButton”).enable();
console.log(error);
}
}
// If some of the fields have invalid values:
else {
// Show the error text.
$w(“#errorText”).show();
// Enable the book button.
$w(“#bookButton”).enable();

} 

}

This is the example page that I have opened from the wix tutorial,without touching the code or doing anything on it,the whole idea is not working,gets stucked at the same part as on my own website,might be some other problem and not the code itself?Shouldn’t be working the example website given by wix?

Okay, so that is not good news, I was hoping that it was just going to be that simple fix, not with Wix! I will have a look through the code later when I have time and hopefully we can get something else sorted.

Plus, I see that you have also commented on another forum post about the over capacity issue, so hopefully you might get an answer there which solves something too.

As it comes up with the Wix bookings API error about checkoutBooking().

Then check your code and payment setups as it needs to be able to go and pay for the booking.

See here for info about the Wix Bookings API and see the checkoutBooking() function info. https://www.wix.com/corvid/forum/corvid-tips-and-updates/new-bookings-api

checkoutBooking( ) - Use this function once users have selected one of the available slots you presented to them. This function will book the selected service and process payments for the service if necessary.

Also have a good read of the Wix Bookings API reference for checkoutBooking() here.
https://www.wix.com/corvid/reference/wix-bookings.html#checkoutBooking

If the service being checked out is not a free service, you also need to pass a PaymentOptions object containing information about the method of payment and any coupon codes. If an online method of payment is specified, a payment popup is presented for the user to input payment information, such as credit card information. The function’s returned Promise resolves after the user finishes entering the payment information and the service has been successfully booked. If no payment or an offline method of payment is specified, the payment popup is not presented and the Promise resolves when the service has been successfully booked.

If a service is configured as a paid service in the Dashboard , attempting to perform a checkout as if it is a free service results in an error.

For payment options see the reference here.
https://www.wix.com/corvid/reference/wix-bookings.html#PaymentOptions

Thank you once again for taking the time to reply. I have setup the wix payment following the tutorial and the example shown by wix(the link you sent) but it won’t redirect customers to the payment part.I have tried something different,added a free service from the app modified the code to be offline payment-free service-the error is the same it won’t redirect customers to any page.I have seen that you also commented on the other thread.What do you suggest doing now?It must be a solution somewhere

Still couldn’t figure out the error,can anybody help?