DEV Community

Cover image for How to Build a Calendly Clone in 30 Minutes
Vihar Kurama for Appsmith

Posted on • Edited on

How to Build a Calendly Clone in 30 Minutes

By Nikhil Nandagopal and Vihar Kurama

We've been a long-time Calendly user, an app that lets others block our time and schedules meetings. And we love how much time it saves me. But we've needed a few additional features that were only available on their premium plans. Instead of upgrading it, we've decided to build our own that can be easy to use and fully customisable!

Calendly Clone on Appsmith

Unfortunately, there's one problem here. We'll have to pick technologies, write lots of code, build UI, deploy them from scratch. Which is again time-consuming. To skip all of these, we used Appsmith for the UI, APIs by Google Calendar and Zoom for video-call services.

✨ Appsmith is a cloud or self-hosted open-source platform to build admin panels, CRUD apps and workflows. With Appsmith, you can build everything you need 10x faster. ✨

You can build this, too, in just under 30 minutes! Click here to try. Block the time for a live demo. Here's a quick sketch of what we'll be going through in this guide:

  • Connecting Your Google Calendar
  • Listing our Events from Google Calendar
  • Displaying Free Slots
  • Creating Calendar Events
  • Setting up Zoom for Video Conferencing
  • Final step

Let’s get started!

Connecting Your Google Calendar

Our first task is to connect our Google Calendar and list down all our calendar events. For this, we’ll have to use Google APIs and OAuth authorization to authenticate the user from Appsmith.

If you're an existing user, you can sign in to Appsmith or sign up for a new one (it's free!). We’ll walk through different steps to list our events!

  1. First, you’ll have to create a new application on Appsmith.
  2. A new application opens up an application titled Untitled Application 1; you can rename it by double-clicking on the existing one.
  3. Next, you’ll have to create a new data-source to interact with Google Calendar: To do this create a new API by clicking on the + icon on the left navigation.
  4. Add a new API and save it as a data source with the following URL: https://www.googleapis.com/calendar
  5. You can also set the name of the data source; in this case, we’ll call it GCalender.
  6. Now, navigate to the GCalender data source and set the following configuration: Authentication Type: Oauth 2.0 Grant Type: Authorization Code
  7. Add Access Token URL: https://oauth2.googleapis.com/token this token allows users to verify their identity, and in return, receive a unique access token in return.
  8. Add the Client ID and Client Secret from Google Cloud Platform
  9. Lastly, set the following config:
  10. Scope: https://www.googleapis.com/auth/calendar
  11. Authorisation URL https://accounts.google.com/o/oauth2/v2/auth Add Auth params prompt: consent, access_type: offline
  12. Save and Authorize the first time around!

Awesome! We've now had the authorisation to access our google calendar.

Listing our Events from Google Calendar

Our next step is to create an Appsmith App and fetch all the calendar events.

Let’s create an API to fetch events from our calendar and call it fetch_calendar_events. Use the following CURL command:

curl --location --request GET 'https://www.googleapis.com/calendar/v3/calendars/primary/events?timeMin=%3CMIN_TIME%3E&timeMax=%3CMAX_TIME%3E&singleEvents=true&orderBy=startTime'
Enter fullscreen mode Exit fullscreen mode

Let's make these parameters dynamic by adding some javascript. To do this, we'll use Javascript and moment.js, both of which can be used in Appsmith by writing {{ }} in any field.

// Replace MIN_TIME 
{{moment().format("YYYY-MM-DDT00:mm:ss+05:30")}} 

// Replace MAX_TIME
{{moment().add(1,"days").format("YYYY-MM-DDT00:mm:ss+05:30")}} 
Enter fullscreen mode Exit fullscreen mode

Now replace the placeholders with the above js snippets and hit run to fetch your calendar events! Below is a screenshot explaining the same.

Updating Params

Awesome! This will now display all your calendar events.

Displaying Free Slots

Now that we have all the events from our calendars, we need to build a UI to display these events. Appsmith has a simply DnD interface to build UI. We can use the Table to display our events, a date picker to select which day we should fetch events for, and a dropdown to select the meeting duration we'd like our users to schedule. My UI looks like this, but you can get creative and built something that feels intuitive to you. Be sure to name your widgets well. The naming I'm following in this post is

  • durationDropdown for the dropdown widget
  • slotDatePicker for the date picker widget
  • eventsTable for the table widget

With an idea of the layout and inputs we need, we can configure our Table to display the events our API is returning using JS. Now, set the table data to : {{fetch_calendar_events.data.items}}

Now, this is how our app should look like:

Alt Text

No surprise our table doesn't look very readable because APIs are rarely built to work with views out of the box! Now with a little Javascript, we can transform the ugly API response into something human-readable and add some logic to show us the free slots instead of the calendar event. The following code goes into the table data property of the eventsTable.

{{
function() {
let bookings = fetch_calendar_events.data.items;
let bookingIndex = 0;
const startHour = 10;
const endHour = 20;
let startingTime = moment(slotDatePicker.selectedDate, "DD/MM/YYYY");
startingTime.hour(startHour);
startingTime.minutes(0);
const hr = startingTime.hour();
let slots = [];
const slotDuration = Number(durationDropdown.selectedOptionValue);
while (startingTime.hour() < endHour) {
  if (startingTime.isBefore(moment())) {
    startingTime = moment();
    if (startingTime.minutes() > 30) {
      startingTime.minutes(0);
      startingTime.hour(startingTime.hour() + 1);
    } else startingTime.minutes(30);
  }
  const booking = bookings[bookingIndex];
  let bookingStart = undefined;
  if (booking) {
    bookingStart = moment(booking.start.dateTime);
  } else {
    bookingStart = moment(DatePicker1.selectedDate, "DD/MM/YYYY");
    bookingStart.hour(endHour);
    bookingStart.minutes(0);
  }
  const slotNum = Math.floor(Math.round(moment.duration(bookingStart.diff(startingTime)).asMinutes()) / slotDuration);
  for (let i = 0; i < slotNum; i++) {
    const slotStartTime = startingTime.format("HH:mm");
    startingTime.add(slotDuration, "minutes");
    const slotEndTime = startingTime.format("HH:mm");
    slots.push({
      startTime: slotStartTime,
      endTime: slotEndTime
    });
  }
  startingTime = booking ? moment(booking.end.dateTime) : startingTime.hour(endHour);
  bookingIndex += 1;
}
return slots.map((slot) => {
  return {
    slot: slot.startTime + " - " + slot.endTime
  }
})
}()
}}
Enter fullscreen mode Exit fullscreen mode

The above logic was really the hardest part of building this application and took the longest to get right (but also the most fun part). The code declares a self-invoking function that iterates over the response of the fetch_calendar_events API and adds free time slots to array slots that are returned by the function. It begins iterating from 10 am to 8 pm (again hardcoding my workday for convenience) and uses the value in the durationDropdown to determine the number of free slots of selected duration that can fit between the current available time and the next calendar event. It then skips to the end of the next busy calendar event and continues this till it reaches the end of my workday!

Creating Calendar Events

To schedule a meeting, we can create a button on the table and configure it to open a modal once it is clicked. The modal UI can also be custom-built using DnD to capture a user's name, email, and purpose of the meeting.

creating-cal-envents

Name the inputs as nameInput, emailInput and purposeInput so we're able to use them in our API. Let's import the following CURL and name the API create_event:

curl --location --request POST 'https://www.googleapis.com/calendar/v3/calendars/primary/events?text=%3CUSER_NAME%3E%20Meeting&sendUpdates=all' \
--header 'Content-Type: text/plain' \
--data-raw '{
    "start": {
        "dateTime": "<START_TIME>"
    },
    "end": {
        "dateTime": "<END_TIME>"
    },
    "description": "<DESCRIPTION>",
    "summary": "<TITLE>",
    "attendees": [{ "email": "<EMAIL>" }]
}'
Enter fullscreen mode Exit fullscreen mode

In the above API, we need to accept dynamic values from our app, so we need to replace some of the params and headers.

USER_NAME:

{{nameInput.text}}
Enter fullscreen mode Exit fullscreen mode

START_TIME:

{{moment(slotDatePicker.selectedDate+" "+eventsTavble.selectedRow.slot.split(" - ")[0], "DD/MM/YYYY HH:mm").toISOString()}}
Enter fullscreen mode Exit fullscreen mode

END_TIME:

{{moment(slotDatePicker.selectedDate+" "+eventsTavble.selectedRow.slot.split(" - ")[1], "DD/MM/YYYY HH:mm").toISOString()}}
Enter fullscreen mode Exit fullscreen mode

DESCRIPTION:

{{purposeInput.text}}
Enter fullscreen mode Exit fullscreen mode

TITLE:

{{nameInput.text + " Meeting with Nikhil"}}
Enter fullscreen mode Exit fullscreen mode

EMAIL:

{{emailInput.text + " Meeting with Nikhil"}}
Enter fullscreen mode Exit fullscreen mode

With this, our API is reading values from the eventsTable and the slotDatePicker to create the right event for the selected slot (eventsTable.selectedRow). We can now bind the onClick of the confirm button to call the create_event API and close the modal onSuccess.

creating-cal-success

Setting up Zoom for Video Conferencing

Finally, there's one more thing. We need to send out a Zoom link for every event to integrate Zoom's APIs and create a zoom link for the calendar events. (This is one of the things I love about Calendly) To integrate with zoom, we have to fetch a JWT to authenticate our application requests with zoom. We'll follow this guide. With a JWT in hand, it became as easy as importing another CURL and naming it create_zoom.

curl --location --request POST 'https://api.zoom.us/v2/users/5IrMmK-8Rv-dFkX1kbk22w/meetings' \
--header 'Content-Type: text/plain' \
--data-raw '{
  "topic": "<TOPIC>",
  "type": 2,
  "start_time": "<START_TIME>",
  "duration": <DURATION>,
"timezone": "Asia/Kolkata",
  "settings": {
    "registrants_email_notification": true
  }
}'
Enter fullscreen mode Exit fullscreen mode

Replace the following variables as shown:

JWT with your JWT token

TOPIC:

{{nameInput.text + "<> Name"}}
Enter fullscreen mode Exit fullscreen mode

STARTTIME:

{moment(slotDatePicker.selectedDate+" "+eventsTable.selectedRow.slot.split(" - ")[0], "DD/MM/YYYY HH:mm").format("yyyy-MM-DDTHH:mm:ss")}} 
Enter fullscreen mode Exit fullscreen mode

-DURATION:_

{{durationDropdown.selectedOptionValue}}
Enter fullscreen mode Exit fullscreen mode

We also have to update our create_event API to save the zoom link returned by this API in the calendar event. Update the description field to

{{purposeInput.text + "\n Zoom Link: " + create_zoom.data.join_url }} 
Enter fullscreen mode Exit fullscreen mode

Now let's call our create_zoom API before we call create_event and close the modal. To do this, we can easily convert the onClick configuration to javascript and write a neat little workflow using callbacks.

{{ create_zoom.run(() => 
    create_event_event.run(() => 
      fetch_calendar_events.run(() => 
        closeModal("Modal1")
      )
    )
  )
}}
Enter fullscreen mode Exit fullscreen mode

Alt Text

Now hit deploy, make your application URL public by clicking the share button, and share it with anyone you need to meet. You can try my meeting scheduler here. Soon we’ll be having a fork app feature on Appsmith so that we can directly fork this one and customise accordingly within no time. All we’ll have to do is update the forked app with the new API keys.

**If you like what we've built, star our ⭐️ Github Repo.

Also, do let us know your thoughts on this article in the comments section.

Top comments (3)

Collapse
 
danilohrf profile image
thisisfin3

Would it be too much trouble to make this into a video? I'm having difficulties with some of the parts as what I'm seeing in my screen does not match the prints in your guide.

Collapse
 
asitprakash profile image
Asit Prakash

Can you give any info about microsoft flow ?

Collapse
 
bodasheera profile image
Vignesh Saikrishna Mallya

where should I replace the JWT token ?