🧑‍💻 我是如何打造世界上最好的 NextJS AI 调度应用程序的 🤖✨

2025-05-24

🧑‍💻 我是如何打造世界上最好的 NextJS AI 调度应用程序的 🤖✨

TL;DR

阅读完本文后,您将了解如何:

  • 通过使用 Cal.com 构建调度应用程序,将 AI 副驾驶集成到您的 Next.js 应用程序中。

  • 开发和管理自定义调度程序,以增强整个应用程序的用户体验。

此外,您还将拥有一个很酷的项目来展示您的作品集!

凉爽的


什么是 AI Copilot?

AI Copilot 是一款应用内 AI 助手,可帮助用户在应用程序内回答问题并采取行动。它将 LLM 智能直接引入您的应用程序。

以下是人工智能副驾驶的一些潜在用例:

  • ChatBot:应用程序的上下文应用内消息传递功能,可以帮助用户进行查询并在应用程序中执行一些特定操作。
  • AI 自动完成:智能输入控件,在用户输入消息时提供与上下文相关的建议。
  • CoAgents:人工智能助手,可以与您的应用程序和用户进行实际操作,能够自行执行复杂的任务。

CopilotKit是领先、最强大且最易于使用的开源框架,用于构建应用内 AI 副驾驶。只需几分钟,即可在您的应用中运行完全自定义的 AI 副驾驶。

副驾驶

查看 CopilotKit ⭐️


先决条件

要遵循本教程,您需要具备以下内容。

以下是我们将在本教程中构建的内容的演示:

演示1

创建新的 Next.js 项目

Next.js是用于创建可扩展、高性能 Web 应用程序的最广泛使用的框架之一。它基于 React 的核心功能构建,并提供服务器端渲染、静态站点生成和更简单的路由接口,有助于创建快速、可立即投入生产且具有良好 SEO 效果的网站。

为了让我们专注于学习如何将Cal.com和 Copilot 集成到您的 Next.js 客房预订应用中,我为该应用创建了组件和 UI 界面。运行以下命令克隆并运行该应用:

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

屏幕截图-1

集成Cal.com API 进行预订

现在定义这些路由。在这些路由中,我们将向Cal.com APU发出 API 请求,以创建新预订或取消预订。在您的 API 文件夹中,创建一个新bookings/route.ts文件并添加以下代码:

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

上述代码将在Cal.com中创建一个包含预订详情(包括房间和时间信息)的事件类型。然后,使用新创建的事件类型的 ID,在Cal.com上创建实际预订。该代码处理日期格式,为两个 API 调用构建必要的负载,并使用 axios 向Cal.com API发送请求

创建一个新cancel/route.ts文件并添加以下代码:

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

上述代码实现了使用Cal.com API 取消预订的 API 路由处理程序。

现在更新您的app/page.tsx文件以使用RoomBookingProvider我们创建的:

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

然后,更新您的 RoomBookingCard.tsx 文件以使用 useRoomBooking 钩子来允许用户预订房间。

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

现在您可以选择任何房间,输入您的详细信息并进行预订。

屏幕截图-2

现在,让我们通过使用 CopilotKit 添加 AI 副驾驶,让应用程序变得更有趣。首先,访问 OpenAI 开发者平台 并创建一个新的密钥。

集成 Copilotkit 实现 AI 驱动交互

OpenAI 仪表板

设置 Copilotkit

Copilotkit 提供两种集成选项:

  • Copilot Cloud:哪种方式最容易开始使用 CopilotKit 和
  • 自托管:这会在您自己的基础架构上设置 Copilot Runtime 的实例。

在本教程中,我们将使用Copilot Cloud。点击此处免费获取您的 Copilot Cloud API 密钥。然后,将其替换 <your-public-api-key> 为您的实际 Open AI 密钥,以生成 Copilot 公钥。

将您的 API 密钥安全地添加到您的.env.local文件中:

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

更新您的app/page.tsx文件以将 <CopilotKit> 提供程序包装在您的应用中:


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

在上面的代码中,我们导入了<CopilotPopup />组件,并用提供程序包装了页面<CopilotKit>,两者均来自@copilotkit/react-ui。我们还从同一个包中导入了可选的内置样式,以增强 UI。

现在您将在页面右下角看到聊天弹出窗口。

弹出截图

为了让 Copilot 提供正确的答案并执行任务,我们需要使用 useCopilotReadable 钩子让它感知我们的应用状态。更新文件中的代码lib/hooks/use-room-booking.tsx,以便向 Copilot 提供我们的预订和房间状态:

// ...

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

现在您可以通过询问有多少个预订来测试 Copilot。

CopilotKit 聊天屏幕

处理预订和取消

让我们让 Copilot 能够执行更多任务,例如创建预订、取消预订以及获取可用房间信息。为此,我们将使用CopilotuseCopilotAction 钩子。此钩子允许您将操作提供给 Copilot。

更新您的lib/hooks/use-room-booking.tsx文件并添加以下操作:

// ...

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

在上面的代码片段中,我们使用useCopilotAction 钩子创建了创建预订的操作,addBookingcancelBookingfetchAvailableRoomssetBookingStatus。每个操作都采用具有以下属性的对象。

name代表动作的名称。

description指的是对动作进行描述。这非常重要,因为它能让我们的副驾驶选择正确的行动。

parameters是操作所采用的参数集合,是一个数组。它符合 JSON Schema 格式。

handler只是一个在执行动作时执行的函数。

现在,您可以使用副驾驶执行高级操作,例如查看可用房间列表、预订和取消预订。您也可以随时与副驾驶聊天以执行其他操作。

使用 CopilotKit 进行高级操作

太好了,现在您有一个由人工智能驱动的调度应用程序可以添加到您的投资组合中。


总结

CopilotKit是一款创新便捷的工具,可用于为您的产品添加 AI 功能。无论是创建语音交互系统,还是为高难度流程部署自动化,它的灵活适应性都能满足任何希望将 AI 融入工作的软件开发人员的需求。

如果您正在开发以人工智能为重点的产品,或者正在寻找将其添加到现有应用程序中的方法,那么您绝对应该考虑使用 CopilotKit。它的界面简洁,配置快速,从而缩短了开发时间,并提高了全局的专注度。

您对 CopilotKit 等工具的好奇心可以帮助您跟上软件行业快速变化的格局。

加入 CopilotKit 令人惊叹的开发者社区,与我们一起构建!

别忘了查看我们的GitHub并给我们一颗星来表达你的喜爱⭐️

文章来源:https://dev.to/copilotkit/how-i-built-the-worlds-best-nextjs-ai-scheduling-app-442c
PREV
我如何利用生成式 UI 提升我的前端游戏水平🧑‍💻🌠
NEXT
几分钟内轻松为您的 AI 代理构建 UI (LangGraph + CopilotKit)⚡️