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();
}
}