🧑‍💻 How I Built the World's Best NextJS, AI Scheduling App 🤖✨
Arindam Majumder

Arindam Majumder @arindam_1729

About: Developer Advocate | Technical Writer | 600k+ Reads | Mail for Collabs

Location:
Kolkata, India
Joined:
Nov 5, 2022

🧑‍💻 How I Built the World's Best NextJS, AI Scheduling App 🤖✨

Publish Date: Oct 24 '24
374 54

TL;DR

By the end of this article, you'll learn how to:

  • Integrate an AI copilot into your Next.js application by building a scheduling app with Cal.com.

  • Develop and manage a custom scheduler that enhances user experience across your application.

Plus, you'll have a cool project to showcase in your portfolio!

Cool


What is an AI Copilot?

An AI Copilot, is an in-app AI assistant that helps users answer questions and take actions inside an application. It brings LLM intelligence right into your application.

The following are some of the potential use cases for AI copilots:

  • ChatBot: A contextual in-app messaging facility for the application that can help the users with queries and perform some specific actions in the application.
  • AI Autocomplete: Smart input controls that offer suggestions related to the context as the user is typing in a message.
  • CoAgents: Artificially intelligent helpers who can work hands-on with your app and your users, capable of performing complex tasks on their own.

CopilotKit is the leading, most robust, and easiest to use open-source framework for building in-app AI copilots. You can have a fully custom AI copilot running in your app within minutes.

Copilot

Checkout CopilotKit ⭐️


Prerequisites

To follow along with this tutorial, you need to have the following.

Here is a demo of what we’ll be building throughout this tutorial:

Demo1

Creating a New Next.js Project

Next.js is one of the most widely used frameworks for creating scalable and high-performance web applications. It builds upon React's core features and offers server-side rendering, static site generation, and simpler routing interfaces which help to create fast and production-ready websites with good SEO.

To allow us to focus on learning how to integrate Cal.com and Copilot into your room booking Next.js app, I have created the components and UI interface for this app. Run the command below to clone and run the app:

git clone https://github.com/icode247/copilot-booking-app 
&& cd copilot-booking-app && npm install && npm run dev
Enter fullscreen mode Exit fullscreen mode

ScreenShot-1

Integrating Cal.com API for Booking

Now define those routes. In those routes, we’ll make API requests to the Cal.com APU to create new bookings or cancel bookings. In your API folder, create a new bookings/route.ts file and add the code below:

import { NextRequest, NextResponse } from 'next/server';
import axios from 'axios';

export async function POST(request: NextRequest) {
  const { room, start, end, time, email } = await request.json();

  try {
    const [startDate] = start.split("T");
    const startDateTime = new Date(`${startDate}T${time}:00`);
    const formattedStart = startDateTime.toISOString();

    const endDateTime = new Date(startDateTime);
    endDateTime.setHours(endDateTime.getHours() + 1);
    const formattedEnd = endDateTime.toISOString();

    // Step 1: Create event-type
    const eventTypePayload = {
      length: 60,
      slug: `booking-${Date.now()}-${room.toLowerCase().replace(/\s+/g, "-")}`,
      title: `Booking for ${room}`,
      description: `Booking for ${room} from ${formattedStart} to ${formattedEnd}`,
      locations: [{ type: "inPerson", address: room }],
      disableGuests: false,
      slotInterval: 0,
      minimumBookingNotice: 0,
      beforeEventBuffer: 0,
      afterEventBuffer: 0,
    };

    const eventTypeResponse = await axios.post(
      `${CALCOM_API_BASE_URL}/event-types`,
      eventTypePayload,
      {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${process.env.NEXT_PUBLIC_CALCOM_API_KEY}`,
        },
      }
    );

    const eventTypeId = eventTypeResponse.data.data.id;

    // Step 2: Create booking
    const bookingPayload = {
      end: formattedEnd,
      start: formattedStart,
      eventTypeId,
      eventTypeSlug: eventTypePayload.slug,
      timeZone: "Africa/Lagos",
      user: [email],
      language: "en",
      bookingUid: `booking-${Date.now()}`,
      metadata: {},
      responses: {
        name: email.split("@")[0],
        email,
        guests: [],
        notes: `Booking for ${room} from ${formattedStart} to ${formattedEnd}`,
      },
    };

    const bookingResponse = await axios.post(
      `${process.env.CALCOM_API_BASE_URL}/bookings`,
      bookingPayload,
      {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${process.env.NEXT_PUBLIC_CALCOM_API_KEY}`,
        },
      }
    );

    return NextResponse.json({ booking: bookingResponse.data }, { status: 201 });
  } catch (error) {
    console.error("Error response status:", error.response?.status);
    return NextResponse.json(
      {
        error: "Failed to create booking",
        details: error.response?.data || error.message,
      },
      { status: 500 }
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

The above code will create an event type in Cal.com with the booking details, including the room and time information. Then, using the ID of the newly created event type, it proceeds to create an actual booking on Cal.com. The code handles date formatting constructs the necessary payloads for both API calls, and uses axios to send requests to the Cal.com API.

Create a new cancel/route.tsfile and add the code below:

import { NextRequest, NextResponse } from "next/server";
import axios from "axios";

export async function POST(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  try {
    const body = await request.json();
    const bookingId = params.id;

    const cancelledBookingResponse = await axios.post(
      `${process.env.CALCOM_API_BASE_URL}/bookings/${bookingId}/cancel`,
      body,
      {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${process.env.NEXT_PUBLIC_CALCOM_API_KEY}`,
        },
      }
    );

    return NextResponse.json(
      { booking: cancelledBookingResponse.data },
      { status: 201 }
    );
  } catch (error) {
    console.error("Error cancelling booking:", error);
    return NextResponse.json(
      { error: "Failed to cancel booking" },
      { status: 500 }
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

The above code implements an API route handler for canceling bookings using the Cal.com API.

Now update your app/page.tsx file to use the RoomBookingProvider we created:

// app/page.tsx
'use client';

import Layout from "@/components/Layout";
import RoomBookingCard from "@/components/RoomBookingCard";
import { RoomBookingProvider } from "@/lib/hooks/use-room-booking";

export default function Home() {
  return (
    <RoomBookingProvider>
      <Layout>
        <RoomBookingCard />
      </Layout>
    </RoomBookingProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Then, update your RoomBookingCard.tsx file to use the useRoomBooking hook to allow users to book rooms.

// components/RoomBookingCard.tsx
"use client";

import { FC, useEffect, useState } from "react";
import RoomList from "./RoomList";
import Notification from "./Notification";
import BookingForm from "./BookingForm";
import { useRoomBooking } from "@/lib/hooks/use-room-booking";

const RoomBookingCard: FC = () => {
  const [selectedRoom, setSelectedRoom] = useState<string | null>(null);
  const [notification, setNotification] = useState<string | null>(null);
  const [rooms, setRooms] = useState([]);
  const { addBooking } = useRoomBooking();

  useEffect(() => {
    async function fetchRooms() {
      const response = await fetch("/api/rooms");
      const data = await response.json();
      setRooms(data);
    }
    fetchRooms();
  }, []);

  const handleRoomSelect = (room: string) => {
    setSelectedRoom(room);
  };

  const handleBookingConfirm = async (
    sdate: string,
    time: string,
    edate: string,
    email: string
  ) => {
    try {
      if (selectedRoom) {
        await addBooking(selectedRoom, sdate, time, edate, email);
        setNotification("Booking confirmed!");
        setSelectedRoom(null);
      }
    } catch (error) {
      setNotification(error.message);
    }
  };

  return (
    <div>
      {notification && (
        <Notification
          message={notification}
          onClose={() => setNotification(null)}
        />
      )}
      {selectedRoom ? (
        <BookingForm room={selectedRoom} onConfirm={handleBookingConfirm} />
      ) : (
        <RoomList rooms={rooms} onSelectRoom={handleRoomSelect} />
      )}
    </div>
  );
};

export default RoomBookingCard;
Enter fullscreen mode Exit fullscreen mode

Now you can select any room, enter your details, and book it.

ScreenShot-2

Now let’s make the application more interesting by adding AI copilot using CopilotKit. First, visit the OpenAI Developers' Platform and create a new secret key.

Integrating Copilotkit for AI-Driven Interaction

OpenAI dashboard

Setting Up Copilotkit

Copilotkit offers two options for integration:

  • Copilot Cloud: Which easiest way to get started with CopilotKit and
  • Self Hosting: This sets up an instance of Copilot Runtime on your own infrastructure.

For this tutorial, we’ll use the Copilot Cloud. Click here to get your Copilot Cloud API key for free. Then, replace <your-public-api-key> with your actual Open AI key to generate a Copilot public key.

Add your API key securely to your .env.local file:

PUBLIC_API_KEY=<your-public-api-key>
Enter fullscreen mode Exit fullscreen mode

Update your app/page.tsx file to wrap the <CopilotKit> provider in your app:


// app/page.tsx
"use client";

import Layout from "@/components/Layout";
import RoomBookingCard from "@/components/RoomBookingCard";
import { RoomBookingProvider } from "@/lib/hooks/use-room-booking";
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotPopup } from "@copilotkit/react-ui";
import "@copilotkit/react-ui/styles.css";

export default function Home() {
  return (
    <CopilotKit publicApiKey={process.env.PUBLIC_API_KEY}>
      <RoomBookingProvider>
        <Layout>
          <RoomBookingCard />
        </Layout>
      </RoomBookingProvider>
      <CopilotPopup />
    </CopilotKit>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the above code, we imported the <CopilotPopup /> component and wrapped the page with the <CopilotKit> provider, both from @copilotkit/react-ui. We also imported optional built-in styles from the same package to enhance the UI.

Now you’ll see the chat popup in the bottom right corner of the page.

Popup Screenshot

For Copilot to provide us with the right answers and perform tasks, we need to make it aware of our app state using the useCopilotReadable hook. Update the code in your lib/hooks/use-room-booking.tsx file to provide copilot with the state of our bookings and rooms:

// ...

import { useCopilotReadable } from "@copilotkit/react-core";
// ...

export function RoomBookingProvider({ children }: { children: ReactNode }) {
    // ... 
    const [rooms, setRooms] = useState([]);

    // ... 

    useCopilotReadable({
       description: "The state of the booking list",
       value: JSON.stringify(bookings),
    });

    useCopilotReadable({
       description: "The state of the rooms list",
       value: JSON.stringify(rooms),
    });
    //...
}
Enter fullscreen mode Exit fullscreen mode

Now you can test the Copilot by asking how many bookings you have.

CopilotKit Chat screen

Handling Booking and Cancellations

Let’s enable the Copilot to perform more tasks like creating bookings, canceling bookings, and having information about available rooms. To achieve that, we’ll use the Copilot useCopilotAction hook. This hook allows you to make actions available to the copilot.

Update your lib/hooks/use-room-booking.tsx file and add the following actions:

// ...

export function RoomBookingProvider({ children }: { children: ReactNode }) {
// ...

  useCopilotAction({
    name: "addBooking",
    description: "Adds a booking to the list",
    parameters: [
      {
        name: "room",
        type: "string",
        description: "The room to be booked",
        required: true,
      },
      {
        name: "date",
        type: "string",
        description: "The date of the booking",
        required: true,
      },
      {
        name: "time",
        type: "string",
        description: "The time of the booking",
        required: true,
      },
      {
        name: "end",
        type: "string",
        description: "The checkout time for booking",
        required: true,
      },
      {
        name: "email",
        type: "string",
        description: "Email address of the user booking the room",
        required: true,
      },
    ],
    handler: async ({ room, date, time, end, email }) => {
      await addBooking(room, date, time, end, email);
    },
  });

  useCopilotAction({
    name: "cancelBooking",
    description: "Cancels a booking from the list",
    parameters: [
      { name: "room", type: "string", description: "The room of the booking to be cancelled", required: true },
      { name: "date", type: "string", description: "The date of the booking to be cancelled", required: true },
      { name: "reason", type: "string", description: "The reason for cancellation", required: true },
    ],
    handler: async ({ room, date, reason }) => {
      await cancelBooking(room, date, reason);
    },
  });

  useCopilotAction({
    name: "fetchAvailableRooms",
    description: "Fetches available rooms for a given date",
    parameters: [
      {
        name: "date",
        type: "string",
        description: "The date to check room availability",
        required: true,
      },
    ],
    handler: async ({ date }) => {
      const availableRooms = await fetchAvailableRooms(date);
      setRooms(availableRooms);
    },
  });

  useCopilotAction({
    name: "setBookingStatus",
    description: "Sets the status of a booking",
    parameters: [
      {
        name: "id",
        type: "number",
        description: "The ID of the booking",
        required: true,
      },
      {
        name: "status",
        type: "string",
        description: "The status of the booking",
        enum: Object.values(BookingStatus),
        required: true,
      },
    ],
    handler: ({ id, status }) => {
      setBookingStatus(id, status);
    },
  });
  //...
 }
Enter fullscreen mode Exit fullscreen mode

In the above code snippet, we used the useCopilotAction hook to create actions to create bookings, addBooking, cancelBooking, fetchAvailableRooms, and setBookingStatus. Each of these actions takes an object with the following properties.

name stands for the name of the action.

description refers to characterizing the action. This is very important because it would allow our copilot to pick the right action.

parameters is the set of parameters that the action takes which is an array. It conforms to the JSON Schema format.

handler is simply a function that will be executed when the action is executed.

Now you can use the copilot to perform advanced actions like seeing a list of available rooms, booking, and canceling bookings. Feel free to chat with the copilot to perform other actions.

Advance Actions with CopilotKit

Excellent, you now have an AI-powered scheduling app to add to your portfolio.


Wrapping It Up

CopilotKit is an innovative and convenient tool for adding AI capabilities to your products. Whether it is creating a voice interactive system or deploying automation for challenging procedures, its adaptability will satisfy the needs of any software developer who wants to incorporate AI into his work.

If you are developing products with a strong emphasis on AI or are looking for ways to add it to the applications you have made, then you should definitely consider using CopilotKit. The interface is simple and can be configured quickly which translates into reduced time as well as improved focus globally.

Your curiosity about tools such as CopilotKit helps you keep up with the rapidly changing landscape of the software industry.

Join CopilotKit's amazing community of developers and come build with us!

Don't forget to check out our GitHub and show your love by giving us a star ⭐️

Comments 54 total

  • Nevo David
    Nevo DavidOct 24, 2024

    Awesome article!

  • Nathan Tarbert
    Nathan TarbertOct 24, 2024

    I use Cal.com for my scheduling and this is a great use case to boost it with AI.
    Nicely done @arindam_1729!

    • Arindam Majumder
      Arindam Majumder Oct 24, 2024

      Glad you liked it, Nathan!

      With CopilotKit, We can make so many cool projects that will help us save our time!

  • Astrodevil
    AstrodevilOct 24, 2024

    Very impressive article!

  • David Asaolu
    David AsaoluOct 24, 2024

    Nicely written! 🔥
    Great tutorial

  • David
    DavidOct 24, 2024

    This is sick!

  • Sajay
    SajayOct 24, 2024

    Going to build this one, thanks!

    • Arindam Majumder
      Arindam Majumder Oct 24, 2024

      Amazing, Let us know how it goes!

      You can share your progress in our community.

      We would love to support you.

  • 𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒
    𝚂𝚊𝚞𝚛𝚊𝚋𝚑 𝚁𝚊𝚒Oct 24, 2024

    Dad-bod Pikachu is 🔥

  • Ferguson
    FergusonOct 24, 2024

    Nice!
    I didn't know you could hook into Cal.com's API. This is really cool!

  • SREETAMA
    SREETAMAOct 24, 2024

    Amazing article 👏

  • Debajyati Dey
    Debajyati DeyOct 24, 2024

    Damn! Dope content!

  • Mathew
    MathewOct 24, 2024

    I only use open source so when I see a tutorial like this, it sparks my interest to build it.
    I mean why not, I already use CAL :)

    • Arindam Majumder
      Arindam Majumder Oct 24, 2024

      Absolute, Mathew!

      Cal.com is my go-to platform for scheduling.

      And with the AI powers of CopilotKit, It becomes even more interesting to build projects like this.

  • Gina Acosta
    Gina AcostaOct 24, 2024

    Great article!

  • John Cook
    John CookOct 24, 2024

    I need to look further into Cal.com, but as far as building an AI scheduling app, are time zones automatically handled?

    I'm curious because I'm building a SaaS, and I heard about CopilotKit a few days ago and was intrigued. Just now looking into them.

    • Nathan Tarbert
      Nathan TarbertOct 24, 2024

      Hey @johncook1122, thanks for checking out CopilotKit. Getting started is extremely easy and depending on which kind of AI interface you want to add, we have a suite of options.
      docs.copilotkit.ai/

    • Arindam Majumder
      Arindam Majumder Oct 24, 2024

      Hey John,

      Yes, They do handle that.

      They have a prop autoUpdateTimezone in the Cal Provider that automatically updates the user’s timezone.

      You can check this for reference: Cal.com Docs

  • Sammy Scolling
    Sammy ScollingOct 24, 2024

    Great article, keep rockin' it!

  • Akshay bondre
    Akshay bondreOct 24, 2024

    Recently hearing a lot about CopilotKit, With this Tutorial, this looks promising. I'll definitely explore it and build some projects

  • Hemath
    HemathOct 24, 2024

    Great Tutorial Arindam. Love the way you explained and i'll try to do this project and add some new features to it.

    • Arindam Majumder
      Arindam Majumder Oct 24, 2024

      Thanks a lot.

      Let us know how it goes. Would love to see your project!

  • Tanmoy Sinha
    Tanmoy SinhaOct 24, 2024

    Dope Content!

  • Akshay SIng
    Akshay SIngOct 24, 2024

    Nice Tutorial Arindam!

  • Ekemini Samuel
    Ekemini SamuelOct 25, 2024

    Great article @arindam_1729

  • Mahmoud Alaskalany
    Mahmoud AlaskalanyOct 26, 2024

    We have a booking app to book events and halls ,and that is a good way to start using knowledge in this article and apply it to our mobile app ,good one keep it up 👍

  • Debarun
    DebarunOct 26, 2024

    It's actually nice.. good work

  • Dada Abiola
    Dada AbiolaOct 27, 2024

    Thanks dude for such great article! Simple and easy to follow through. My team and I are currently working on a scheduling app for institutions in the academic world. We are integrating GOOGLE Calendar API for scheduling. How can we leverage your approach to that?

  • Marko Rajević
    Marko RajevićNov 9, 2024

    Hey, great text!

    Is it possible to render your own component in the chat instead of text?
    For example instead of text Room A, Room B with details to render a table with that data?

  • Pratham Dey
    Pratham DeyDec 25, 2024

    Really insightful Article Arindam!

Add comment