> Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Tutorial | Block Off Time in the Bookings Calendar ## Article: Tutorial | Block Off Time in the Bookings Calendar ## Article Link: https://dev.wix.com/docs/develop-websites-sdk/get-started/tutorials/bookings/tutorial-block-off-time-in-the-bookings-calendar.md ## Article Content: # Tutorial | Block Off Time in the Bookings Calendar Using the [Calendar APIs](https://dev.wix.com/docs/api-reference/business-management/calendar/introduction.md), you can programmatically block time slots in your staff's Bookings calendars. This tutorial demonstrates how to build an interface that allows Wix users to efficiently manage staff availability by creating blocked time sessions for individual staff members or resources. This functionality is useful for scenarios such as staff vacation and time off, equipment maintenance, or training sessions. By the end of this tutorial, you'll have a resource availability management interface that allows Wix users to: - Select specific staff members or resources to manage. - Block single time slots on staff calendars. - Create recurring blocked time patterns.
**Caution:** The interface created in this tutorial should only be accessible in a protected area, not exposed to regular site visitors. Consider implementing this functionality in a [dashboard page](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/build-a-custom-dashboard/about-dashboard-pages.md) or on a [members-only page](https://support.wix.com/en/article/wix-editor-creating-members-only-pages) with appropriate permissions.
> The code in this article was written using the following module versions: > > - @wix/bookings (v1.0.1017) > - @wix/calendar (v1.0.148) > - @wix/web-methods (v1.0.11) > - @wix/essentials (v0.1.28) > > Learn how to install npm packages [in the editor](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-in-the-editor.md) or [using the CLI](https://dev.wix.com/docs/develop-websites-sdk/code-your-site/developer-environments/packages/npm/work-with-npm-packages-with-the-cli.md). Use the following steps to build the functionality: 1. [Set up page elements.](#step-1--set-up-page-elements) 2. [Add backend code.](#step-2--add-backend-code) 3. [Add page code.](#step-3--add-page-code) ## Before you begin It's important to note the following points before doing this tutorial: - The site must have the [Wix Bookings app](https://www.wix.com/app-market/wix-bookings) installed. - The site must have at least 1 staff member or resource set up in the Bookings dashboard. - For recurring blocked time, only intervals of 1 or more weeks are supported. ## Step 1 | Add elements to your page This step creates an interface for Wix users to set up blocked time events. At the end of this step, you'll have a functional page with all the necessary input elements for blocking time slots, including options for recurring patterns. The interface should look like this: ![Block off time interface](https://wixmp-833713b177cebf373f611808.wixmp.com/images/33eb39b5c8d5ae5e3fec707576dee046.png) To create the interface: Add the following elements to your page: - A [dropdown](https://dev.wix.com/docs/velo/velo-only-apis/$w/dropdown/introduction.md) for selecting staff members/resources. Set the dropdown ID to `resourcesDropdown`. - A [date picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/date-picker/introduction.md) for selecting the start date. Set the date picker ID to `startDate`. - A [time picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/time-picker/introduction.md) for selecting the start time. Set the time picker ID to `startTime`. - A [date picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/date-picker/introduction.md) for selecting the end date. Set the date picker ID to `endDate`. - A [time picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/time-picker/introduction.md) for selecting the end time. Set the time picker ID to `endTime`. - A [checkbox](https://dev.wix.com/docs/velo/velo-only-apis/$w/checkbox/introduction.md) to enable recurring blocked time. Set the checkbox ID to `recurrenceCheckbox`. - A [container](https://dev.wix.com/docs/velo/velo-only-apis/$w/container/introduction.md) to hold recurring-specific elements. Set the container ID to `recurrenceContainer`. - A [date picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/date-picker/introduction.md) for selecting the cutoff date of recurrence. Set the date picker ID to `untilDate`. - A [time picker](https://dev.wix.com/docs/velo/velo-only-apis/$w/time-picker/introduction.md) for selecting the cutoff time of recurrence. Set the time picker ID to `untilTime`. - A [dropdown](https://dev.wix.com/docs/velo/velo-only-apis/$w/dropdown/introduction.md) for selecting the recurrence interval, in weeks. Set the dropdown ID to `intervalDropdown`. - A [button](https://dev.wix.com/docs/velo/velo-only-apis/$w/button/introduction.md) for creating the blocked time event. Set the button ID to `button`. 2. Place `untilDate`, `untilTime`, and `intervalDropdown` inside `recurrenceContainer`. 3. In the **Properties & Events** panel, set `recurrenceContainer` to **Collapsed** by default. 4. Select `intervalDropdown`, and then select **Manage Choices** to populate the dropdown with options. Each option represents a number of weeks to set as the interval between recurring events. The panel should look like this: ![Choices panel, showing the options: 1:1, 2:2, 3:3, 4:4](https://wixmp-833713b177cebf373f611808.wixmp.com/images/00f231e02cc8bf4d1325daa363553886.png) ## Step 2 | Add backend code This step creates a backend code file with methods that call Wix APIs. At the end of this step, you'll have 2 backend methods: - A method that retrieves Bookings resources and responds with a list of options for a dropdown element. - A method that creates a blocked off event in the Bookings calendar. To add backend code: 1. Create a new backend file called `blockedOffTime.web.js` in your backend folder. 2. Add the required imports: ```javascript import { webMethod, Permissions } from "@wix/web-methods"; import { auth } from "@wix/essentials"; import { resources } from "@wix/bookings"; import { events } from "@wix/calendar"; ``` 3. Define a [web method](https://dev.wix.com/docs/sdk/core-modules/web-methods/introduction?apiView=SDK.md) that gets a list of Bookings resources by calling `queryResources()`, and returns a mapping of the resource into objects with the following fields: - `label`: The name of the resource, to display as a dropdown option. - `value`: A resource ID, to provide as a value for the dropdown option. ```javascript export const getResourceList = webMethod(Permissions.Anyone, async () => { try { const results = await resources.queryResources({}); const mappedResults = results.items.map((resource) => { return { label: resource.name, value: resource.eventsSchedule.scheduleId, }; }); return mappedResults; } catch (error) { console.error(error); return error; } }); ``` 4. Create a second web method that receives an `event` object. The method defines an [elevated](https://dev.wix.com/docs/sdk/articles/work-with-the-sdk/about-elevated-permissions?apiView=SDK.md) call to the Calendar API's `createEvent()` method, and makes the call with the specified `event` object. > **Note:** > Elevated permissions are unnecessary if the implementation is in a dashboard page. ```javascript export const createBlockedOffTimeEvent = webMethod( Permissions.Anyone, async (event) => { try { const elevatedCreateEvent = auth.elevate(events.createEvent); return elevatedCreateEvent(event); } catch (error) { return error; } } ); ``` ## Complete backend code ```javascript // blockedOffTime.web.js import { webMethod, Permissions } from "@wix/web-methods"; import { auth } from "@wix/essentials"; import { resources } from "@wix/bookings"; import { events } from "@wix/calendar"; export const getResourceList = webMethod(Permissions.Anyone, async () => { try { const results = await resources.queryResources({}); const mappedResults = results.items.map((resource) => { return { label: resource.name, value: resource.eventsSchedule.scheduleId, }; }); return mappedResults; } catch (error) { console.error(error); return error; } }); export const createBlockedOffTimeEvent = webMethod( Permissions.Anyone, async (event) => { try { const elevatedCreateEvent = auth.elevate(events.createEvent); return elevatedCreateEvent(event); } catch (error) { return error; } } ); ``` ## Step 3 | Add frontend code This step ties everything together. We use page code to define the behavior of some page elements and call our backend methods. At the end of this step, you'll have page code that: - Populates `resourcesDropdown` with options using our backend method. - Expands and collapses `recurrenceContainer` based on `recurrenceCheckbox`. - Calls the backend method `createBlockedOffTimeEvent` when the button is pressed. To add the following code to your page: 1. Import the methods you created in step 2: ```javascript import { getResourceList, createBlockedOffTimeEvent, } from "backend/blockedOffTime.web"; ``` 2. When the page loads, populate the resources dropdown, and set up an `onClick` event handler for the button and an `onChange` event handler for the checkbox. The next steps define these methods. ```javascript $w.onReady(function () { loadResourceDropdown(); $w("#recurrenceCheckbox").onChange(toggleRecurrenceContainer); $w("#button").onClick(blockOffTime); }); ``` 3. Define a method that calls `getResourceList()` from the backend and populates `resourcesDropdown` with the response. ```javascript async function loadResourceDropdown() { try { $w("#resourcesDropdown").options = await getResourceList(); } catch (error) { console.error("Failed to fetch resources.", error); } } ``` 4. Define a method that expands or collapses `recurrenceContainer` when `recurrenceCheckbox` becomes checked or unchecked. ```javascript function toggleRecurrenceContainer() { if ($w("#recurrenceCheckbox").checked) { $w("#recurrenceContainer").expand(); } else { $w("#recurrenceContainer").collapse(); } } ``` 5. Define a helper method that formats a date and a time into a single string. The Events API requires the following format for `localDate` fields: `YYYY-MM-DDTHH:mm:ss`. The `'en-CA'` locale provides the correct format for the date. ```javascript function formatLocalDate(date, time) { return date.toLocaleDateString("en-CA") + "T" + time; } ``` 6. Define a method to block off time in the calendar. First, get data from page elements, format it to match the requirements of the [Calendar API](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/create-event?apiView=SDK.md), and then create an `event` object. ```javascript async function blockOffTime() { const startTime = formatLocalDate( $w("#startDate").value, $w("#startTime").value ); const endTime = formatLocalDate($w("#endDate").value, $w("#endTime").value); try { let event = { scheduleId: $w("#resourcesDropdown").value, start: { localDate: startTime }, end: { localDate: endTime }, }; // Put the code from the next steps here. } catch (error) { console.error("Event creation failed.", error); } } ``` 7. If `recurrenceCheckbox` is checked, add recurrence data to the `event` object. The `'en-CA'` locale and `{ weekday: 'long' }` provide the chosen date's weekday in English. ```javascript if ($w("#recurrenceCheckbox").checked) { const recurrenceDay = $w("#startDate") .value.toLocaleDateString("en-CA", { weekday: "long" }) .toUpperCase(); const untilTime = formatLocalDate( $w("#untilDate").value, $w("#untilTime").value ); event = { ...event, recurrenceRule: { frequency: "WEEKLY", interval: $w("#intervalDropdown").value, days: [recurrenceDay], until: { localDate: untilTime }, }, }; } // Put the code from the next step here. ``` 8. Finish the method by calling the `createBlockedOffTimeEvent()` method from the backend, specifying the constructed `event` object. ```javascript createBlockedOffTimeEvent(event); ``` You've successfully created an interface that allows Wix users to block off time slots in staff calendars, including support for both single time blocks and recurring patterns. ## Complete page code ```javascript import { getResourceList, createBlockedOffTimeEvent, } from "backend/blockedOffTime.web"; $w.onReady(function () { loadResourceDropdown(); $w("#recurrenceCheckbox").onChange(toggleRecurrenceContainer); $w("#button").onClick(blockOffTime); }); async function loadResourceDropdown() { try { $w("#resourcesDropdown").options = await getResourceList(); } catch (error) { console.error("Failed to fetch resources.", error); } } function toggleRecurrenceContainer() { if ($w("#recurrenceCheckbox").checked) { $w("#recurrenceContainer").expand(); } else { $w("#recurrenceContainer").collapse(); } } function formatLocalDate(date, time) { return date.toLocaleDateString("en-CA") + "T" + time; } async function blockOffTime() { const startTime = formatLocalDate( $w("#startDate").value, $w("#startTime").value ); const endTime = formatLocalDate($w("#endDate").value, $w("#endTime").value); try { let event = { scheduleId: $w("#resourcesDropdown").value, start: { localDate: startTime }, end: { localDate: endTime }, }; if ($w("#recurrenceCheckbox").checked) { const recurrenceDay = $w("#startDate") .value.toLocaleDateString("en-CA", { weekday: "long" }) .toUpperCase(); const untilTime = formatLocalDate( $w("#untilDate").value, $w("#untilTime").value ); event = { ...event, recurrenceRule: { frequency: "WEEKLY", interval: $w("#intervalDropdown").value, days: [recurrenceDay], until: { localDate: untilTime }, }, }; } createBlockedOffTimeEvent(event); } catch (error) { console.error("Session creation failed.", error); } } ``` ## See also - [Recurring Events](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/recurring-events.md) - [Calendar Events API](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/introduction.md) - [Bookings API](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/introduction.md)