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:

  1. The Backend API Assumption: What the React component expects from your server.
  2. The React Component (ChatbotWindow.js): The core logic for state, UI, and API calls.
  3. The CSS Styling (ChatbotWindow.css): To make it look like a real chat window.
  4. Integration (App.js): How to use the component in your application.
  5. A detailed explanation of how it all works.

1. The Backend API Assumption

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.


2. The React Component (ChatbotWindow.js)

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;

3. The CSS Styling (ChatbotWindow.css)

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; }
}

4. Integration (App.js)

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;
}

5. Explanation of the Code

ChatbotWindow.js Breakdown:

  1. State Management (useState):

    • messages: An array of message objects. Each object has a unique id, the text content, and the sender ('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 is true 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.
  2. Auto-Scrolling (useRef and useEffect):

    • messageListRef is a reference attached to the div that contains the list of messages.
    • The useEffect hook runs every time the messages array changes. Inside it, we programmatically scroll the message list to its full height, ensuring the latest message is always visible.
  3. 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 to true.
    • The try...catch...finally block is key for handling the API call:
      • try: We use the fetch API to make a POST request to your backend. We await the response, parse it as JSON, and create a new message object for the bot's reply. This is then added to the messages 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 set isLoading back to false.
  4. Rendering (JSX):

    • The main container has a header, the message list, and the input form.
    • We .map() over the messages array to render each message. The className is dynamically set based on the sender, allowing for different styling for user and bot messages.
    • The loading indicator (<div className="message bot">...</div>) is conditionally rendered only when isLoading is true.
    • The input field and send button are disabled when isLoading is true to prevent the user from sending multiple messages while one is already being processed.

⚠️ **GitHub.com Fallback** ⚠️