반응형
윅스 (Wix) 코딩 강의 고급 (Advanced) - 예약시간표 만들기 (Timetable) - 윅스 예약시스템 (Wix Bookings)
강의 내용 요약
다음의 예제는 Wix 윅스 무료 홈페이지 만들기의 자바스크립트 (Javascript) 코딩 기능을 활용할 수 있는 Corvid by Wix (윅스 코딩 - 콜비드) 를 활용하여 만듭니다.
웹사이트 내에 예약 시간표를 만들어 방문자들이 예약할 수 있는 기능을 만듭니다.
강의 내용 만드는법
만들고자 하는 윅스 사이트에 다음과 같은 구성 요소들이 필요합니다.
- 데이터베이스 컬렉션
- 반복레이아웃 /리피터
- 버튼
- 텍스트
코드 (Code)
Schedule
//-------------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
//-------------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"});
}
API
Corvid by Wix (윅스 코딩 - 콜비드) 개발자 모드/도구 활성화하는 방법
윅스 (Wix) 코딩 - 개발자 도구를 활성화하기 (Wix Code: How to Enable Developer Tools)
연관된 토픽)
윅스 (Wix) 코딩 강의 중급 (Intermediate) - 서비스 리스트 (Service List) - 윅스 예약시스템 (Wix Bookings)
윅스 홈페이지 만들기 101
윅스 (Wix) 홈페이지 만들기 101 - E-Book - Index
출처 :
콜비드 - 윅스 코딩 (Corvid - Wix coding)
반응형
댓글