24
February
2021
Tutorial

How to Build a Meeting Scheduler (Calendly Clone) in 30 Minutes

How to Build a Meeting Scheduler (Calendly Clone) in 30 Minutes

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. Authorization 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 authorization 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'

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")}}

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

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.

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:


{{nameInput.text}}

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

_USERNAME:

{{nameInput.text}}

_STARTTIME:

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

_ENDTIME:

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

DESCRIPTION:

{{purposeInput.text}}

TITLE:

{{nameInput.text + " Meeting with Nikhil"}}

EMAIL:

{{emailInput.text + " Meeting with Nikhil"}}

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.

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": "",
  "type": 2,
  "start_time": "",
  "duration": ,
"timezone": "Asia/Kolkata",
  "settings": {
    "registrants_email_notification": true
  }
}'

Replace the following variables as shown:

JWT with your JWT token

TOPIC:

{{nameInput.text + "<> Name"}}

STARTTIME:

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

-DURATION:_

{{durationDropdown.selectedOptionValue}}

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 }}

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")
      )
    )
  )
}}


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, we’re an open-source company.

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

What’s a Rich Text element?

asdsadasdsa

The rich text element allows you to create and format headings, paragraphs, blockquotes, images, and video all in one place instead of having to add and format them individually. Just double-click and easily create content.

sfdfsdfds

dsfdsfdsf

adfkaldf

  • dfghhghff
  • dfgdfgfd
The rich text element allows you to create and format

sadadasdasdas dsada sadas asd ad

Static and dynamic content editing

  • dcdsdfd
  • dfdfvddf
  1. vdfgdgd
  2. gjgjg

A rich text element can be used with static or dynamic content. For static content, just drop it into any page and begin editing. For dynamic content, add a rich text field to any collection and then connect a rich text element to that field in the settings panel. Voila!

How to customize formatting for each rich text

Headings, paragraphs, blockquotes, figures, images, and figure captions can all be styled after a class is added to the rich text element using the "When inside of" nested selector system.

swzdswxzdsw

Square
Try Appsmith
The Modern Stack to Build Internal Tools: Supabase, Appsmith, n8n
8
November
2021
Resources

The Modern Stack to Build Internal Tools: Supabase, Appsmith, n8n

The Modern Stack to Build Internal Tools: Supabase, Appsmith, n8n
Vihar Kurama
8
 minutes ↗
#
applications
#
community
#
crud
#
automation
#
beginners
Resources
October Roundup: Widget Improvements, New Appsmith functions, and ARM Architecture Support
2
November
2021
Monthly Round-up

October Roundup: Widget Improvements, New Appsmith functions, and ARM Architecture Support

October Roundup: Widget Improvements, New Appsmith functions, and ARM Architecture Support
Vihar Kurama
5
 minutes ↗
#
announcement
#
applications
#
app-development
#
community
#
crud
Monthly Round-up
Learn How to Push Content from Multiple Sources to Discord in One Go
20
October
2021
Tutorial

Learn How to Push Content from Multiple Sources to Discord in One Go

Learn How to Push Content from Multiple Sources to Discord in One Go
Vihar Kurama
8
 minutes ↗
#
community
#
applications
#
dashboard
Tutorial