Motivation

I found that this is a popular task in most saas applications. The available references out there do not show any complete guide from a to z. So I thought of writing down how I did this task from beginning to end. This article will be a good asset for most developers to follow.

Table of contents

  1. Create a beginner React app
  2. Add the UI compnents
  3. Understand the core functions needed to the project
  4. Connect things up

(1) Create a beginner React app

Pre-requirements

  1. You should have installed nodejs and npm on your local computer.
  2. A code editor. In my case I prefer VS Code Editor.

If you have above, then you just have to use bellow command on your terminal to create the project. If you are from windows you can run this on the command prompt.

npx create-react-app google-Calender

(2) Add the UI components

We are going to create two more files as GoogleApiService.js and GoogleEventComponent.js in the same directory as App.js.

GoogleService.js is going to be the collection of google api functions. GoogleEventComponent.js is the one which is responsible for the UI.

Next, clean everything inside App.js and add the bellow inside it.

import './App.css';
import GoogleEventComponent from './GoogleEventComponent';
function App() {
  return (
    <div className="App">
      <GoogleEventComponent />
    </div>
  );
}
export default App;

So basicaly this will show the contents in GoogleEventComponent in our browser.

For the GoogleEventComponent.js add the bellow code.

import React, { useState, useEffect } from 'react';
export default function GoogleEventComponent() {
    const [signedin,setSignedIn] = useState(false);
    const submit = (e) =>{
        e.preventDefault();
    }
    return (
         <div className='calenderEvent-wrapper'>
            <div className='header'>
                <h1>Add an event to google Calender</h1>
            </div>
            {!signedin? <div className='google-login'>
                <p>Login in to Google</p>
                <button onClick={()=>getAuthToGoogle()}>Sign In</button>
            </div>:
            <div className='body'>
                <div className='signout'>
                    <p>Email: {googleAuthedEmail}</p>
                    <button onClick={()=>_signOutFromGoogle()}>Sign Out</button>
                </div>
                <form>
                    <div className='eventItem'>
                        <label>Description</label>
                        <input placeholder='Description...' value={description} onChange={e=>setDescription(e.target.value)}></input>
                    </div>
                    <div  className='eventItem'>
                        <label>Start Time</label>
                        <input type="datetime-local" value={startTime} onChange={e=>setStartTime(e.target.value)}></input>
                    </div>
                    <div  className='eventItem'>
                        <label>End Time</label>
                        <input type="datetime-local" value={endTime} onChange={e=>setEndTime(e.target.value)}></input>
                    </div>
                    <button type='submit' onClick={(e)=>submit(e)}>Submit</button>
                </form>
            </div>}
        </div>
    )
}

Within the state of our component we are keeping a variable to store the signed in status. So if a user successfuly sign in to google this will toggle to true. Base on that status we decide what we show on the browser. if the iser is not logged in, then we show a button to log in. Otherwise a form which collects the data to the calender event is shown.

Next we will add the styles to the application. Lets clean every thing inside App.css, and add the styles shown in bellow to it.

.App{
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: rgb(224, 228, 228);
  min-height: 100vh;
}
.calenderEvent-wrapper{
  width: 500px;
  height: 370px;
  background-color: white;
  border: 1px solid rgb(202, 199, 199);
  border-radius: 7px;
  box-shadow: 4px 4px 4px grey;
  display: flex;
  flex-direction: column;
}
.header{
  height: 50px;
  width: 100%;
  color: white;
  background-color: #0388D1;
  border-radius: 7px;
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.google-login{
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
  height: 100%;
}
.signout{
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.signout > button{
  height: 30px;
  background-color: rgb(38, 214, 155);
  border: none;
  border-radius: 7px;
  cursor: pointer;
}
.google-login > button{
  height: 40px;
  width: 100px;
  border-radius: 7px;
  border: 1px solid grey;
  outline: 0;
  cursor: pointer;
}
.body{
  padding: 10px;
}
.eventItem{
  margin-top: 20px;
  display: flex;
  align-items: center;
}
.eventItem > label{
  display: block;
  width: 150px;
}
.eventItem > input{
  flex: 1;
  outline: 0;
  height: 30px;
  border-radius: 7px;
  border: 1px solid grey;
  padding-left: 5px;
}
.body > form > button {
  width: 100%;
  margin-top: 40px;
  height: 40px;
  border-radius: 7px;
  outline: 0;
  background-color:#0388D1;
  color: white;
  border: none;
  cursor: pointer;
  font-size: 20px;
}

Now you should be able to see some thing very similar to this

(3) Understand the core functions needed to the project

Next we are going to talk about what will be inside GoogleApiService.js. We will be building 6 functions inside this. They will as follows.

  1. Function that initialize gapi so that the application can access to google and handle the rest.
  2. Function that checks the sign in status.
  3. Function that sign in an user.
  4. Function that log out an user.
  5. Function that gets the signed in user’s email.
  6. Function that puts the calendar event to google calendar.

Before building these we need some important variables from the google cloud platform. There you have to create a new project and get some parameters more like passcode to get access to google api. In this tutorial we are not gong to discuss how to create a project in GCP (Google Cloud Platform). If you dont know how to do that you can refer to bellow link.

https://developers.google.com/workspace/guides/create-project

From there you should get the client id and the api key for the created project.

At the very beginning of GoogleService.js we declare some constants as bellow.

let gapi = window.gapi;
const API_KEY = 'YOUR_API_KEY'; 
const DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
const CLIENT_ID = 'YOUR_CLIENT_ID';
const SCOPES = "https://www.googleapis.com/auth/calendar.events";

The first line creates the initial object for gapi. For this to work without error, we should define what is this gapi to our application. In your react project you can find a public folder. In there, there is a file called index.html. Inside that palce the bellow code just before the head tag ends.

<script
  src="https://apis.google.com/js/api.js"
  type="text/javascript"
></script>

Okay. now we can continue on GoogleService.js. Lets define the first function which initialize the gapi with the constants we already defined.

export function initClient(callback) {
    gapi.load('client:auth2',()=>{
        try {
            gapi.client.init({
                apiKey: API_KEY,
                clientId: CLIENT_ID,
                discoveryDocs: DISCOVERY_DOCS,
                scope: SCOPES
            }).then(function () {
                if (typeof(callback)==='function'){
                    callback(true)
                }
            }, function(error) {
                console.log(error);
            });
        } catch (error) {
            console.log(error);
        }
    });
};

Here the gapi.load is given a attribute called ‘client:auth2’. This loads the gapi with definition of auth2. The next callback is used to initialize with defined constants. Since this returns a promise we can use the defined callback function to know it has successfully initialized.

Next function is to check signed in status. gapi gives us an endpoint to check the initialozed client is signed in or not. We can do that with bellow function.

export const checkSignInStatus =async () =>{
    try {
        let status = await gapi.auth2.getAuthInstance().isSignedIn.get();
        return status;
    } catch (error) {
        console.log(error);
    }
}

If an user is signed in then this returns true. Else it returns false.

The other function is to sign in a user. We are going to use the bellow code for that.

export const signInToGoogle = async ()=>{
    try {
        let googleuser = await gapi.auth2.getAuthInstance().signIn({prompt:'consent'});
        if (googleuser){
            return true;
        }
    } catch (error) {
        console.log(error)
    }
};

This will open a prompt for entering google account credentials. The object {prompt:‘consent’} defines how this prompt should work. you can see for more options through the bellow link.

https://developers.google.com/identity/sign-in/web/reference#gapiauth2getauthinstance

The fourth function is to log out an user. we are going to use the bellow function for that.

export const signOutFromGoogle = () => {
    try {
        var auth2 = gapi.auth2.getAuthInstance();
        auth2.signOut().then(function () {
            auth2.disconnect();
        });
        return true;
    } catch (error) {
        console.log(error)
    }
}

The fifth functions is for getting the logged in user’s email. The gapi gives an endpoint for that as follows.

export const getSignedInUserEmail = async () => {
    try {
        let status = await checkSignInStatus();
        if (status){
            var auth2 = gapi.auth2.getAuthInstance();
            var profile = auth2.currentUser.get().getBasicProfile();
            return profile.getEmail()
        } else {
            return null;
        }
    } catch (error) {
        console.log(error)
    }
}

We check the sign in status first. If a user is not signed in , then there will be no email address to retrieve.

The last function defines how we put a calendar event to the google calendar.

export const publishTheCalenderEvent = (event) => {
    try {
        gapi.client.load('calendar', 'v3', () => {
            var request = gapi.client.calendar.events.insert({
                'calendarId': 'primary',
                'resource': event
            });
        
            request.execute(function(event) {
                console.log('Event created: ' + event.htmlLink);
            });
        })
          
    } catch (error) {
        console.log(error)
    }
}

The event variable is an object with event details. A general event can have a structure as bellow.

var event = {
  'summary': 'Google I/O 2015',
  'location': '800 Howard St., San Francisco, CA 94103',
  'description': 'A chance to hear more about Google\'s developer products.',
  'start': {
    'dateTime': '2021-07-10T09:00:00-07:00',
    'timeZone': 'America/Los_Angeles'
  },
  'end': {
    'dateTime': '2021-07-10T17:00:00-07:30',
    'timeZone': 'America/Los_Angeles'
  },
  'recurrence': [
    'RRULE:FREQ=DAILY;COUNT=2'
  ],
  'attendees': [
    {'email': 'lpage@example.com'},
    {'email': 'sbrin@example.com'}
  ],
  'reminders': {
    'useDefault': false,
    'overrides': [
      {'method': 'email', 'minutes': 24 * 60},
      {'method': 'popup', 'minutes': 10}
    ]
  }
};

Okay now we are done with GoogleApiService.js. Next we are going to import these functions to GoogleEventComponent.js to build the application functionality.

(4) Connecting Things Together

The first thing is to import the GoogleApiService.js functions to this component.

import { signInToGoogle, initClient,getSignedInUserEmail, signOutFromGoogle , publishTheCalenderEvent } from './GoogleApiService';

We use a useEffect to initialize the gapi at the beginning of component mount.

useEffect(()=>{
    initClient((success)=>{
        if (success){
            getGoogleAuthorizedEmail();
        }
    });
},[]);

At the success of initialization, we request to get the signed in user email. This will returns null if no user is signed in.

Next we connect the other functions to bring the flow.

const getGoogleAuthorizedEmail =async ()=>{
    let email = await getSignedInUserEmail();
    if (email){
        setSignedIn(true)
        setgoogleAuthedEmail(email)
    }
};
const getAuthToGoogle =async ()=>{
    let successfull =await signInToGoogle();
    if (successfull){
        getGoogleAuthorizedEmail();
    }
  };
const _signOutFromGoogle = () => {
    let status = signOutFromGoogle();
    if (status){
        setSignedIn(false);
        setgoogleAuthedEmail(null);
    }
};
const submit = (e) =>{
    e.preventDefault();
    var event = {
        description,
        'start': {
            'dateTime':moment(startTime),
            'timeZone': 'America/Los_Angeles'
          },
          'end': {
            'dateTime': moment(endTime),
            'timeZone': 'America/Los_Angeles'
          },
    }
    publishTheCalenderEvent(event);
}

We use useState to store the states

const [signedin,setSignedIn] = useState(false);
const [googleAuthedEmail,setgoogleAuthedEmail] = useState(null);
const [description,setDescription] =useState('');
const [startTime,setStartTime] = useState('');
const [endTime,setEndTime] = useState('');

Now every thing is up and ready. If you followed the steps correctly you can see the event scheduler screen as bellow.