Chatbot - realestateexplorer/relatedToCh GitHub Wiki
https://github.com/hellotinah/financial_agent
Build an AI Chatbot with Python | Start-to-finish beginner friendly project walkthrough https://github.com/acstrahl/chatbot_demo/blob/main/README.md
create chatbot window that can communicate with backend API in React
Of course! Here is a comprehensive guide to creating a functional and good-looking chatbot window in React that communicates with a backend API.
This guide will cover:
- The Backend API Assumption: What the React component expects from your server.
-
The React Component (
ChatbotWindow.js
): The core logic for state, UI, and API calls. -
The CSS Styling (
ChatbotWindow.css
): To make it look like a real chat window. -
Integration (
App.js
): How to use the component in your application. - A detailed explanation of how it all works.
Before we build the frontend, let's define what our component expects from the backend. This is crucial. Our React component will send a request and expect a specific response format.
Your backend should have an endpoint, for example: /api/chat
-
Request (React -> Backend): It will be a
POST
request with a JSON body.{ "message": "Hello, what are your hours?" }
-
Response (Backend -> React): It should respond with a JSON object containing the bot's reply.
{ "reply": "We are open from 9 AM to 5 PM, Monday to Friday." }
Our React code will be built around this simple contract.
Create a new file in your src/components
folder (or wherever you prefer) named ChatbotWindow.js
.
// src/components/ChatbotWindow.js
import React, { useState, useEffect, useRef } from 'react';
import './ChatbotWindow.css';
const ChatbotWindow = () => {
// State to hold all messages
const [messages, setMessages] = useState([
{
id: 1,
text: "Hello! I'm your friendly support bot. How can I help you today?",
sender: 'bot',
},
]);
// State for the user's input
const [inputValue, setInputValue] = useState('');
// State to indicate if the bot is "typing"
const [isLoading, setIsLoading] = useState(false);
// Ref for the message list container to enable auto-scrolling
const messageListRef = useRef(null);
// Effect to scroll to the bottom of the message list whenever a new message is added
useEffect(() => {
if (messageListRef.current) {
messageListRef.current.scrollTop = messageListRef.current.scrollHeight;
}
}, [messages]);
// Function to handle sending a message
const handleSendMessage = async (e) => {
e.preventDefault();
const trimmedInput = inputValue.trim();
if (!trimmedInput) return; // Don't send empty messages
// Create the user's message object and add it to the messages state
const userMessage = {
id: Date.now(),
text: trimmedInput,
sender: 'user',
};
setMessages((prevMessages) => [...prevMessages, userMessage]);
// Clear the input field and set loading state
setInputValue('');
setIsLoading(true);
try {
// --- API Call ---
const response = await fetch('/api/chat', { // Replace with your actual API endpoint
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: trimmedInput }),
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
// Create the bot's response message object
const botMessage = {
id: Date.now() + 1, // Ensure unique ID
text: data.reply,
sender: 'bot',
};
// Add the bot's message to the state
setMessages((prevMessages) => [...prevMessages, botMessage]);
} catch (error) {
console.error("Error fetching bot response:", error);
// Optionally, add an error message to the chat
const errorMessage = {
id: Date.now() + 1,
text: "Sorry, I'm having trouble connecting. Please try again later.",
sender: 'bot',
isError: true,
};
setMessages((prevMessages) => [...prevMessages, errorMessage]);
} finally {
// Stop the loading indicator
setIsLoading(false);
}
};
return (
<div className="chatbot-window">
<div className="chatbot-header">
<h2>Support Bot</h2>
</div>
<div className="message-list" ref={messageListRef}>
{messages.map((message) => (
<div
key={message.id}
className={`message ${message.sender} ${message.isError ? 'error' : ''}`}
>
{message.text}
</div>
))}
{isLoading && (
<div className="message bot">
<span className="loading-dots">
<span>.</span><span>.</span><span>.</span>
</span>
</div>
)}
</div>
<form className="chat-input-form" onSubmit={handleSendMessage}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Type your message..."
disabled={isLoading}
autoFocus
/>
<button type="submit" disabled={isLoading}>
Send
</button>
</form>
</div>
);
};
export default ChatbotWindow;
Create a corresponding CSS file in the same folder: ChatbotWindow.css
.
/* src/components/ChatbotWindow.css */
.chatbot-window {
width: 370px;
height: 550px;
background-color: #f9f9f9;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
overflow: hidden;
font-family: Arial, sans-serif;
}
.chatbot-header {
background-color: #4a90e2;
color: white;
padding: 15px;
text-align: center;
font-size: 1.2rem;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.chatbot-header h2 {
margin: 0;
font-size: 16px;
font-weight: 600;
}
.message-list {
flex-grow: 1;
padding: 20px 10px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 12px;
}
/* Custom scrollbar for a better look */
.message-list::-webkit-scrollbar {
width: 6px;
}
.message-list::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 10px;
}
.message-list::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
.message {
padding: 10px 15px;
border-radius: 18px;
max-width: 75%;
line-height: 1.4;
word-wrap: break-word;
}
.message.user {
background-color: #4a90e2;
color: white;
align-self: flex-end;
border-bottom-right-radius: 4px;
}
.message.bot {
background-color: #e5e5ea;
color: #333;
align-self: flex-start;
border-bottom-left-radius: 4px;
}
.message.error {
background-color: #fdd;
color: #a00;
border: 1px solid #fbb;
}
.chat-input-form {
display: flex;
padding: 10px;
border-top: 1px solid #ddd;
background-color: #fff;
}
.chat-input-form input {
flex-grow: 1;
border: 1px solid #ccc;
border-radius: 20px;
padding: 10px 15px;
font-size: 1rem;
outline: none;
transition: border-color 0.2s;
}
.chat-input-form input:focus {
border-color: #4a90e2;
}
.chat-input-form input:disabled {
background-color: #f0f0f0;
}
.chat-input-form button {
background-color: #4a90e2;
color: white;
border: none;
border-radius: 20px;
padding: 10px 20px;
margin-left: 10px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.2s;
}
.chat-input-form button:hover {
background-color: #357abd;
}
.chat-input-form button:disabled {
background-color: #a0c3e8;
cursor: not-allowed;
}
/* Loading dots animation */
.loading-dots span {
animation-name: blink;
animation-duration: 1.4s;
animation-iteration-count: infinite;
animation-fill-mode: both;
font-size: 1.5rem;
line-height: 1;
}
.loading-dots span:nth-child(2) {
animation-delay: 0.2s;
}
.loading-dots span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes blink {
0% { opacity: 0.2; }
20% { opacity: 1; }
100% { opacity: 0.2; }
}
Now, let's use our new ChatbotWindow
component in the main App.js
file.
// src/App.js
import React from 'react';
import ChatbotWindow from './components/ChatbotWindow';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<h1>Welcome to My React App</h1>
<p>Here's a chatbot to help you out!</p>
</header>
<main>
{/* We place the chatbot window component here */}
<ChatbotWindow />
</main>
</div>
);
}
export default App;
And some basic CSS for App.css
to center everything nicely.
/* src/App.css */
.App {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
background-color: #eef2f5;
text-align: center;
}
.App-header {
margin-bottom: 30px;
}
-
State Management (
useState
):-
messages
: An array of message objects. Each object has a uniqueid
, thetext
content, and thesender
('user' or 'bot'). We initialize it with a welcome message from the bot. -
inputValue
: A standard controlled component state for the text input field. -
isLoading
: A boolean flag that istrue
while we are waiting for the API response. This is crucial for good UX, as it allows us to show a "typing" indicator and disable the input field.
-
-
Auto-Scrolling (
useRef
anduseEffect
):-
messageListRef
is a reference attached to thediv
that contains the list of messages. - The
useEffect
hook runs every time themessages
array changes. Inside it, we programmatically scroll the message list to its full height, ensuring the latest message is always visible.
-
-
Sending a Message (
handleSendMessage
):- This
async
function is triggered when the form is submitted (either by clicking "Send" or pressing Enter). - It prevents the default form submission behavior (page reload).
- It adds the user's message to the
messages
array immediately for a snappy UI response. - It clears the input and sets
isLoading
totrue
. -
The
try...catch...finally
block is key for handling the API call:-
try
: We use thefetch
API to make aPOST
request to your backend. Weawait
the response, parse it as JSON, and create a new message object for the bot's reply. This is then added to themessages
state. -
catch
: If the network request fails or the server returns an error, we catch it, log it, and add a user-friendly error message to the chat. -
finally
: No matter if the request succeeded or failed, we setisLoading
back tofalse
.
-
- This
-
Rendering (JSX):
- The main container has a header, the message list, and the input form.
- We
.map()
over themessages
array to render each message. TheclassName
is dynamically set based on thesender
, allowing for different styling for user and bot messages. - The loading indicator (
<div className="message bot">...</div>
) is conditionally rendered only whenisLoading
istrue
. - The input field and send button are
disabled
whenisLoading
istrue
to prevent the user from sending multiple messages while one is already being processed.