From 560f860c3f0e76f3352ba5e0045a7af22f273825 Mon Sep 17 00:00:00 2001 From: nitingupta95 Date: Sun, 2 Nov 2025 23:46:24 +0530 Subject: [PATCH 1/2] feat: integrate meeting scheduling with Google Calendar --- frontend/src/App.jsx | 22 +++--- frontend/src/components/ChatbotWidget.jsx | 75 +++++++++++++++++---- frontend/src/pages/Chatbot.jsx | 82 +++++++++++++++++++---- 3 files changed, 144 insertions(+), 35 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 0c37cc4..2314bbf 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,6 +1,6 @@ import React from "react"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; -import { Toaster } from "react-hot-toast"; +import { Toaster } from "react-hot-toast"; // Your Components import Navbar from "./components/Navbar"; @@ -14,7 +14,7 @@ import NotFound from "./pages/NotFound"; import TemplateBuilder from "./pages/Campaign"; import { AuthProvider } from "./context/AuthContext"; import ForgotPassword from "./pages/Forgotpassword"; -import Contacts from "./pages/Contact"; +import Contacts from "./pages/Contact"; export default function App() { return ( @@ -23,24 +23,24 @@ export default function App() {
- + } /> } /> } /> } /> } /> - } /> - }/> + } /> + }/> }/> } /> - - -
+ - - + + + { + const t = text.toLowerCase(); + // Match phrases like "schedule a meeting", "book meeting", "set meeting", etc. + return /(schedule|book|set|create).*(meeting|call|appointment)/i.test(t); + }; + + useEffect(() => { scrollToBottom(); }, [messages]); @@ -28,6 +35,36 @@ export default function ChatbotWidget() { } }, [isOpen]); + + const scheduleMeeting = async (meetingData) => { + try { + const res = await fetch("http://localhost:5001/api/google-calendar/schedule", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(meetingData), + }); + + const data = await res.json(); + if (data.success) { + return `✅ Meeting scheduled successfully! [View on Google Calendar](${data.eventLink})`; + } else { + throw new Error(data.message || "Failed to schedule meeting"); + } + } catch (err) { + console.error("Google Calendar not connected, using mock scheduler."); + // fallback mock scheduler + const res = await fetch("http://localhost:5001/api/meetings", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(meetingData), + }); + const data = await res.json(); + return data.success + ? `📅 Meeting added to internal scheduler for ${meetingData.date} at ${meetingData.time}.` + : "❌ Failed to schedule meeting."; + } + }; + const sendMessage = async () => { if (!inputMessage.trim()) return; @@ -43,7 +80,24 @@ export default function ChatbotWidget() { setIsLoading(true); try { - const response = await fetch('http://localhost:5000/api/chatbot/message', { + if (detectMeetingIntent(inputMessage)) { + const meetingData = { + title: "Chatbot Meeting", + date: new Date().toISOString().split("T")[0], + time: inputMessage.match(/(\d{1,2})(:\d{2})?\s?(am|pm)?/)?.[0] || "10:00 AM", + duration: 30, + userId: "widget-user", + }; + + const confirmation = await scheduleMeeting(meetingData); + const botMessage = { id: Date.now() + 1, message: confirmation, timestamp: new Date(), isBot: true }; + setMessages((prev) => [...prev, botMessage]); + setIsLoading(false); + return; + } + + // fallback: normal chatbot message + const response = await fetch('http://localhost:5001/api/chatbot/message', { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -59,9 +113,7 @@ export default function ChatbotWidget() { if (data.success) { const botMessage = { id: Date.now() + 1, - message: data.data.response, - timestamp: new Date(data.data.timestamp), - isBot: true + message: data.data.response, timestamp: new Date(data.data.timestamp), isBot: true }; setMessages(prev => [...prev, botMessage]); @@ -126,24 +178,23 @@ export default function ChatbotWidget() {
{/* Messages */} -
+
{messages.map((msg) => (

{msg.message}

))} - + {isLoading && (
@@ -151,12 +202,12 @@ export default function ChatbotWidget() {
)} - +
{/* Input */} -
+
{ + const t = text.toLowerCase(); + return /(schedule|book|set|create).*(meeting|call|appointment)/i.test(t); + }; + + // ✅ Schedule meeting (Google Calendar or fallback) + const scheduleMeeting = async (meetingData) => { + try { + const res = await fetch('http://localhost:5001/api/google-calendar/schedule', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(meetingData), + }); + + const data = await res.json(); + if (data.success) { + return `✅ Meeting scheduled successfully! [View on Google Calendar](${data.eventLink})`; + } else { + throw new Error(data.message || 'Failed to schedule meeting'); + } + } catch (err) { + console.error('Google Calendar not connected, using mock scheduler.'); + const res = await fetch('http://localhost:5001/api/meetings', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(meetingData), + }); + const data = await res.json(); + return data.success + ? `📅 Meeting added to internal scheduler for ${meetingData.date} at ${meetingData.time}.` + : '❌ Failed to schedule meeting.'; + } + }; + + // ✅ Send message logic const sendMessage = async () => { if (!inputMessage.trim()) return; @@ -32,22 +68,45 @@ export default function Chatbot() { id: Date.now(), message: inputMessage, timestamp: new Date(), - isBot: false + isBot: false }; setMessages(prev => [...prev, userMessage]); setInputMessage(''); setIsLoading(true); + try { - const response = await fetch('http://localhost:5000/api/chatbot/message', { + // 1️⃣ Meeting intent + if (detectMeetingIntent(inputMessage)) { + const meetingData = { + title: 'Chatbot Meeting', + date: new Date().toISOString().split('T')[0], + time: + inputMessage.match(/(\d{1,2})(:\d{2})?\s?(am|pm)?/)?.[0] || '10:00 AM', + duration: 30, + userId: 'demo-user', + }; + + const confirmation = await scheduleMeeting(meetingData); + const botMessage = { + id: Date.now() + 1, + message: confirmation, + timestamp: new Date(), + isBot: true, + }; + setMessages((prev) => [...prev, botMessage]); + setIsLoading(false); + return; + } + + // 2️⃣ Normal chatbot message + const response = await fetch('http://localhost:5001/api/chatbot/message', { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: inputMessage, - userId: 'demo-user' + userId: 'demo-user', }), }); @@ -60,7 +119,6 @@ export default function Chatbot() { timestamp: new Date(data.data.timestamp), isBot: true }; - setMessages(prev => [...prev, botMessage]); } else { throw new Error(data.error || 'Failed to get response'); @@ -90,7 +148,7 @@ export default function Chatbot() {

AI Assistant

- +
{/* Messages */}
@@ -102,7 +160,7 @@ export default function Chatbot() {
@@ -110,7 +168,7 @@ export default function Chatbot() {
))} - + {isLoading && (
@@ -118,7 +176,7 @@ export default function Chatbot() {
)} - +
@@ -131,7 +189,7 @@ export default function Chatbot() { onChange={(e) => setInputMessage(e.target.value)} onKeyPress={handleKeyPress} placeholder="Type your message here..." - className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" + className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-black" disabled={isLoading} />