@quickstart-ai/chatbot
Version:
Chatbot React
728 lines (669 loc) • 24.7 kB
JavaScript
// src/App.tsx
import { useEffect as useEffect2, useState as useState2 } from "react";
import { TbMessageChatbot } from "react-icons/tb";
import { AiOutlineClose } from "react-icons/ai";
import styled2, { keyframes as keyframes2 } from "styled-components";
// src/components/chatbot.tsx
import { useState, useEffect, useRef } from "react";
import { GoogleGenerativeAI as GoogleGenerativeAI2 } from "@google/generative-ai";
// src/components/prompt.tsx
var systemPrompt = `
You are a highly intelligent, professional, and friendly chatbot that assists users with inquiries about a business. Your goal is to provide concise, accurate responses, and when specific information is unavailable, offer helpful alternatives or guide the user to other resources. Follow these instructions:
1. **Thoughtful Responses:** If you don't have the exact answer, offer related suggestions or information. Always respond humbly and politely.
2. **Contextual Replies:** Tailor your responses to the user's question. If asked about an event or date and you're unsure, pivot to relevant business details or upcoming services.
3. **Suggest Alternatives:** When specific information is missing, recommend ways the user can find the answer (e.g., contacting support, checking business hours) and show eagerness to assist further.
4. **Friendly, Professional Tone:** Always maintain empathy and professionalism. Even when you lack information, keep the conversation positive and flowing.
5. **Interaction with Politeness:** Greet users appropriately. For example:
- If the user says \u201CHi,\u201D reply with \u201CHello.\u201D
- If they say \u201CThank you,\u201D respond with \u201CYou\u2019re welcome.\u201D
- If they apologize, say \u201CNo problem.\u201D
- If they say \u201CBye,\u201D reply with \u201CGoodbye.\u201D
6. **Handle Irrelevant Questions:** If the user asks something unrelated to the business, respond with: "I'm sorry, I can't help with that. Please ask something related to the business."
7. **Keep Responses Short and Focused:** Avoid long explanations. Only provide what is necessary based on available data.
8. **Avoid Past Messages:** Do not reference or display previous messages in your responses.
9. **Never Answer in list forms. You need to behave like chatbot write message in lines**
---
**Examples:**
**Business Details:**
Q: What is the hardware cost?
A: It costs around $200 for soft materials and $400 for hard materials. These prices are based on 1kg.
**User's Message:**
Q: What is the price for 2kg of soft material?
*The price for 2kg of soft material is approximately $400, based on our standard $200 per kg pricing. Let me know if you'd like more details!* \u{1F60A}
---
**User's Message:**
Q: What\u2019s happening on 1st September?
*I'm not sure about any specific events on 1st September, but I'd be happy to help with any information about our services or hours. Let me know what you\u2019re looking for!* \u{1F31F}
---
**User's Message:**
Q: What gluten-free options do you have?
*I'm sorry, I don't have details on gluten-free options right now, but we do offer vegan and vegetarian products. Let me know if you'd like to explore those options!* \u{1F331}
---
**Important Notes:**
- Keep responses human-like, not robotic.
- Share only the data available\u2014avoid adding unnecessary information.
- Don't make it lengthy; keep it short and sweet of max 200 char.
`;
// src/utils/generateSession.tsx
import axios from "axios";
// src/baseUrl.tsx
var baseUrl = "https://chatbot-npm-backend.vercel.app/api/v1";
var baseUrl_default = baseUrl;
// src/utils/generateSession.tsx
var generateSession = async ({ email, username, chatbotId }) => {
try {
const response = await axios.post(`${baseUrl_default}/session/create`, {
email,
username,
chatbotId
});
return response.data;
} catch (error) {
console.log(error);
throw new Error("Something went wrong while generating session");
}
};
var generateSession_default = generateSession;
// src/components/chatbot.tsx
import axios2 from "axios";
import styled from "styled-components";
import { keyframes } from "styled-components";
import { IoSend } from "react-icons/io5";
// src/utils/generateSuggestions.tsx
import { GoogleGenerativeAI } from "@google/generative-ai";
var genAI = new GoogleGenerativeAI("AIzaSyAAUKB6qgTIonRWHEyFQ2hNgGz7syXDunQ");
var model = genAI.getGenerativeModel({
model: "gemini-1.5-pro",
generationConfig: { responseMimeType: "application/json" }
});
var generateSugesstions = async (bussinessDetails) => {
const prompt = `
You will be given business details, and you need to generate 4 single-word topics from it.
Example:
This Perfect solutions providing the service of Website Development. Here are the pricing: $20 per hour. We provide onsite and remote services. Our website is at github.com/AQIB-NAWAB. We offer 24/7 website support.
Your response should be an array containing the words for suggestions like:
["services", "pricing", "website", "timing", ...]
Business Details: ${bussinessDetails}
Now, generate at least 4 suggestions.
`;
try {
const result = await model.generateContent(prompt);
const candidates = result.response?.candidates;
if (!candidates || !candidates[0]?.content?.parts[0]?.text) {
throw new Error("Unexpected response format from API");
}
const suggestions = JSON.parse(candidates[0].content.parts[0].text);
if (Array.isArray(suggestions)) {
return suggestions;
} else {
throw new Error("Unexpected response format from API");
}
} catch (error) {
console.error("Error generating suggestions:", error);
return [];
}
};
var generateSuggestions_default = generateSugesstions;
// src/components/chatbot.tsx
import { jsx, jsxs } from "react/jsx-runtime";
var genAI2 = new GoogleGenerativeAI2("AIzaSyAAUKB6qgTIonRWHEyFQ2hNgGz7syXDunQ");
var Chatbot = ({
chatbotDetails,
theme,
position,
wantToShowSuggestions = false
}) => {
const [session, setSession] = useState({
started: false,
email: "",
username: "",
sessionId: ""
});
const [messages, setMessages] = useState([
{ role: "bot", message: "Hello! How can I assist you today?" }
]);
const [suggestions, setSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const [suggestionClicked, setSuggestionClicked] = useState(false);
const [input, setInput] = useState("");
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [loading, setLoading] = useState(false);
const messagesEndRef = useRef(null);
const messagesContainerRef = useRef(null);
const scrollToBottom = () => {
if (messagesContainerRef.current) {
messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
}
};
let bussinessDetails = "Bussiness Details :";
bussinessDetails += "Bussiness Name : " + chatbotDetails.bussinessName + ",";
bussinessDetails += "Bussiness Category : " + chatbotDetails.bussinessCategory + ",";
bussinessDetails += "Bussiness Description : " + chatbotDetails.bussinessDescription + ",";
bussinessDetails += "Questionaries :";
chatbotDetails.bussinessDetails.forEach((item) => {
bussinessDetails += item.question + " : " + item.answer + ",";
});
useEffect(() => {
scrollToBottom();
}, [messages]);
const handleSendMessage = async () => {
setShowSuggestions(false);
if (input.trim() !== "" && !loading) {
const updatedMessages = [
...messages,
{ role: "user", message: input }
];
setMessages(updatedMessages);
const temp = input;
setInput("");
setLoading(true);
try {
const model2 = genAI2.getGenerativeModel({
model: "gemini-pro"
});
const result = await model2.generateContentStream({
contents: [
{
role: "user",
parts: [
{ text: systemPrompt },
{ text: bussinessDetails },
{
text: "Past message :" + updatedMessages[updatedMessages.length - 2].message
},
{ text: "User message :" + input }
]
}
]
});
const botMessageIndex = updatedMessages.length;
setMessages((prevMessages) => [
...prevMessages,
{ role: "bot", message: "Typing..." }
// Placeholder while bot is typing
]);
let content = "";
const typewriterEffect = async (text) => {
for (let i = 0; i < text.length; i++) {
await new Promise((resolve) => setTimeout(resolve, 10));
content += text.charAt(i);
setMessages((prevMessages) => [
...prevMessages.slice(0, botMessageIndex),
{ role: "bot", message: content }
]);
scrollToBottom();
}
};
setLoading(false);
for await (const chunk of result.stream) {
await typewriterEffect(chunk.text());
}
if (wantToShowSuggestions && (content.includes("sorry") || content.includes("apologize") || content.includes("error") || content.includes("Sorry"))) {
setShowSuggestions(true);
} else {
setShowSuggestions(false);
}
await axios2.post(`${baseUrl_default}/session/addMessages`, {
sessionId: session.sessionId,
botMessage: content,
userMessage: temp,
chatbotId: chatbotDetails.id
});
} catch (error) {
console.error("Error fetching response:", error);
setMessages((prevMessages) => [
...prevMessages,
{ role: "bot", message: "Sorry, something went wrong." }
]);
} finally {
setLoading(false);
scrollToBottom();
}
} else {
alert("Please enter a valid message");
}
};
const handleStartSession = async () => {
setLoading(true);
if (name.trim() !== "" && email.trim() !== "") {
try {
const response = await generateSession_default({
email,
username: name,
chatbotId: chatbotDetails.id
});
setSession({
started: true,
email,
username: name,
sessionId: response.session._id
});
} catch (error) {
console.error("Error starting session", error);
} finally {
setLoading(false);
}
} else {
setLoading(false);
}
};
const getSugesstionsList = async () => {
if (wantToShowSuggestions && showSuggestions) {
const suggestionsList = await generateSuggestions_default(bussinessDetails);
console.log(suggestionsList);
setSuggestions(suggestionsList);
console.log(suggestionsList);
}
};
useEffect(() => {
getSugesstionsList();
}, []);
const handleSugesstionClick = async (sugg) => {
if (loading) return;
setInput(sugg);
setShowSuggestions(false);
};
if (!session.started) {
return /* @__PURE__ */ jsxs(ChatbotContainer, { position, children: [
/* @__PURE__ */ jsx(Header, { theme, children: /* @__PURE__ */ jsx("a", { href: "https://quickstart-ai.me", target: "_blank", rel: "noopener noreferrer", children: "QuickStart Chatbot" }) }),
/* @__PURE__ */ jsxs(FormContainer, { children: [
/* @__PURE__ */ jsx(
HelpImage,
{
src: "https://res.cloudinary.com/dvxvf2vxu/image/upload/v1725171366/help_wa1buy.png",
alt: "Chatbot"
}
),
/* @__PURE__ */ jsx(
FormInput,
{
theme,
type: "text",
value: name,
onChange: (e) => setName(e.target.value),
placeholder: "Your Name",
required: true
}
),
/* @__PURE__ */ jsx(
FormInput,
{
theme,
type: "email",
value: email,
onChange: (e) => setEmail(e.target.value),
placeholder: "Your Email",
required: true
}
),
/* @__PURE__ */ jsx(FormButton, { theme, disabled: loading, onClick: handleStartSession, children: loading ? "Starting..." : "Start Chat" })
] })
] });
}
return /* @__PURE__ */ jsxs(ChatbotContainer, { position, children: [
/* @__PURE__ */ jsx(Header, { theme, children: /* @__PURE__ */ jsx("a", { href: "https://quickstart-ai.me", target: "_blank", rel: "noopener noreferrer", children: "QuickStart Chatbot" }) }),
/* @__PURE__ */ jsxs(MessagesContainer, { ref: messagesContainerRef, children: [
messages.map((message, index) => /* @__PURE__ */ jsx(MessageBubble, { theme, role: message.role, children: message.message }, index)),
showSuggestions && /* @__PURE__ */ jsx(SuggestionsContainer, { children: suggestions.map((sugg) => /* @__PURE__ */ jsx(Suggestion, { theme, onClick: () => handleSugesstionClick(sugg), children: sugg })) }),
loading && /* @__PURE__ */ jsx(MessageBubble, { theme, role: "bot", children: "Typing..." }),
/* @__PURE__ */ jsx("div", { ref: messagesEndRef })
] }),
/* @__PURE__ */ jsxs(ChatInputContainer, { children: [
/* @__PURE__ */ jsx(
ChatInput,
{
theme,
type: "text",
value: input,
onChange: (e) => setInput(e.target.value),
onKeyPress: (e) => {
if (e.key === "Enter") handleSendMessage();
},
placeholder: "Type a message..."
}
),
/* @__PURE__ */ jsx(SendButton, { theme, onClick: handleSendMessage, children: /* @__PURE__ */ jsx(IoSend, { color: "white", width: 200, height: 100, size: 20 }) })
] })
] });
};
var chatbot_default = Chatbot;
var slideUp = keyframes`
from {
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
`;
var ChatbotContainer = styled.div`
width: 100%;
max-width: 380px;
height: 100%;
max-height: 500px;
background-color: #ffffff;
border-radius: 15px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
position: absolute;
animation: ${slideUp} 0.3s forwards;
font-family: "Outfit", sans-serif;
z-index: 9999;
transition: transform 0.12s, opacity 0.12s;
transform-origin: bottom;
@media (max-width: 768px) {
max-height: 100%;
}
`;
var Header = styled.div`
background-color: ${(props) => props.theme === "primary" ? "#007bff" : props.theme === "secondary" ? "#343a40" : props.theme === "professional" ? "#004085" : props.theme === "tech" ? "#6c757d" : "blue"};
color: white;
padding: 16px;
text-align: center;
font-size: 1.2em;
border-radius: 15px 15px 0 0;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
a {
color: white;
text-decoration: none;
}
`;
var FormContainer = styled.form`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
padding: 0 10px;
box-sizing: border-box;
@media (max-width: 768px) {
padding: 0 30px;
}
`;
var HelpImage = styled.img`
width: 100%;
max-width: 220px;
border-radius: 15px;
aspect-ratio: 1 / 1;
object-fit: cover;
margin-bottom: 20px;
`;
var FormInput = styled.input`
width: 100%;
max-width: 300px;
font-family: "Outfit", sans-serif;
padding: 12px;
margin: 10px 0;
border-radius: 5px;
outline:none;
border: 1px solid
${(props) => props.theme === "primary" ? "#007bff" : props.theme === "secondary" ? "#343a40" : props.theme === "professional" ? "#004085" : props.theme === "tech" ? "#6c757d" : "blue"};
font-size: 1em;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
background-color: white !important;
color: black !important;
:focus {
outline: none;
border:1px solid red;
}
:placeholder {
color: #b3b3b3;
}
::placeholder {
color: #b3b3b3;
}
`;
var FormButton = styled.button`
background-color: ${(props) => props.theme === "primary" ? "#007bff" : props.theme === "secondary" ? "#343a40" : props.theme === "professional" ? "#004085" : props.theme === "tech" ? "#6c757d" : "blue"};
color: white;
border: none;
border-radius: 5px;
padding: 12px 20px;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
:hover {
background-color: ${(props) => props.theme === "primary" ? "#0056b3" : props.theme === "secondary" ? "#23272a" : props.theme === "professional" ? "#00305a" : props.theme === "tech" ? "#5a6268" : "blue"};
}
:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
:active {
transform: scale(0.95);
}
`;
var MessagesContainer = styled.div`
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 16px;
background-color: #f9f9f9;
/* Custom scrollbar styling */
&::-webkit-scrollbar {
width: 8px; /* Width of the scrollbar */
}
&::-webkit-scrollbar-track {
background: #f9f9f9; /* Track color */
border-radius: 10px; /* Rounded edges */
}
&::-webkit-scrollbar-thumb {
background-color: #b0b0b0; /* Thumb color (scroll handle) */
border-radius: 10px; /* Rounded thumb */
border: 2px solid #f9f9f9; /* Adds space around thumb */
}
&::-webkit-scrollbar-thumb:hover {
background-color: #909090; /* Change color on hover */
}
@media (max-width: 768px) {
padding: 10px; /* Make padding smaller on mobile */
&::-webkit-scrollbar {
width: 6px; /* Make scrollbar thinner on smaller screens */
}
}
`;
var MessageBubble = styled.div`
align-self: ${(props) => props.role === "user" ? "flex-end" : "flex-start"};
background-color: ${(props) => props.role === "user" ? props.theme === "primary" ? "#007bff" : props.theme === "secondary" ? "#343a40" : props.theme === "professional" ? "#004085" : props.theme === "tech" ? "#6c757d" : "#e0e0e0" : "#e0e0e0"};
color: ${(props) => props.role === "user" ? "white" : "black"};
padding: 12px 18px;
border-radius: 18px;
max-width: 80%;
animation: ${slideUp} 0.3s ease-out;
`;
var ChatInputContainer = styled.div`
display: flex;
padding: 8px 16px;
background-color: #f9f9f9;
border-top: 1px solid #e0e0e0;
`;
var ChatInput = styled.input`
flex: 1;
border: 1px solid
${(props) => props.theme === "primary" ? "#007bff" : props.theme === "secondary" ? "#343a40" : props.theme === "professional" ? "#004085" : props.theme === "tech" ? "#6c757d" : "blue"};
padding: 10px;
border-radius: 4px;
margin-right: 10px;
outline:none;
font-family: "Outfit", sans-serif;
font-weight:500;
font-size:0.9em;
:focus {
outline: none;
border-color: ${(props) => props.theme === "primary" ? "#007bff" : props.theme === "secondary" ? "#343a40" : props.theme === "professional" ? "#004085" : props.theme === "tech" ? "#6c757d" : "blue"};
}
`;
var SendButton = styled.button`
background-color: ${(props) => props.theme === "primary" ? "#007bff" : props.theme === "secondary" ? "#343a40" : props.theme === "professional" ? "#004085" : props.theme === "tech" ? "#6c757d" : "blue"};
color: white;
border: none;
border-radius: 4px;
padding: 8px 15px;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
:hover {
background-color: ${(props) => props.theme === "primary" ? "#0056b3" : props.theme === "secondary" ? "#23272a" : props.theme === "professional" ? "#00305a" : props.theme === "tech" ? "#5a6268" : "blue"};
}
:active {
transform: scale(0.95);
}
`;
var SuggestionsContainer = styled.div`
display:flex;
flex-direction:row;
flex-wrap:wrap;
gap:10px;
padding:0px;
background-color:#f9f9f9;
border-radius:10px;
max-width:90%;
animation: ${slideUp} 0.3s ease-out;
height:fit-content;
max-height:300px;
`;
var Suggestion = styled.div`
background-color: ${(props) => props.theme === "primary" ? "#007bff" : props.theme === "secondary" ? "#343a40" : props.theme === "professional" ? "#004085" : props.theme === "tech" ? "#6c757d" : "#e0e0e0"};
color: white;
padding: 10px 15px;
border-radius: 18px;
max-width: 80%;
font-size:0.9em;
animation: ${slideUp} 0.3s ease-out;
cursor:pointer;
&:hover {
transform: translateY(-3px); /* Subtle lift on hover */
background-color: ${(props) => props.theme === "primary" ? "#0056b3" : props.theme === "secondary" ? "#23272b" : props.theme === "professional" ? "#003366" : props.theme === "tech" ? "#5a6268" : "#bdbdbd"};
}
`;
// src/utils/getChatbotDetails.tsx
import axios3 from "axios";
var getChatbotDetails = async (token) => {
try {
const response = await axios3.get(`${baseUrl_default}/user/token/verify`, {
params: {
token
}
});
return response.data.data;
} catch (error) {
throw new Error("Something went wrong while fetching chatbot details");
}
};
var getChatbotDetails_default = getChatbotDetails;
// src/App.tsx
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
var bounce = keyframes2`
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-10px);
}
60% {
transform: translateY(-5px);
}
`;
var spin = keyframes2`
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
`;
var AppContainer = styled2.div`
font-family: 'Outfit', sans-serif;
`;
var ChatbotToggleButton = styled2.div`
position: fixed;
bottom: 20px;
${({ position }) => position === "left" ? "left: 20px;" : "right: 20px;"}
font-size: 2em;
cursor: pointer;
border-radius: 50%;
padding: 10px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, background-color 0.3s, color 0.3s;
${({ theme }) => theme === "primary" ? "background-color: #007bff; color: white;" : theme === "secondary" ? "background-color: #343a40; color: white;" : theme === "professional" ? "background-color: #004085; color: white;" : theme === "tech" ? "background-color: #6c757d; color: white;" : "background-color: #007bff; color: white;"}
animation: ${(props) => props.animate ? bounce : "none"} 1s infinite;
z-index: 999;
&:hover {
transform: scale(1.1);
}
`;
var ChatbotWrapper = styled2.div`
position: fixed;
${({ position }) => position === "left" ? "left: 60px;" : "right: 20px;"}
bottom: 80px;
width: 350px;
height: 500px;
background-color: #ffffff;
border-radius: 15px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: opacity 0.4s ease-out, transform 0.4s ease-out;
${({ show }) => show ? "opacity: 1; transform: translateY(-10px);" : "opacity: 0; display: none; transform: translateY(20px);"}
z-index: 999;
@media (max-width: 768px) {
width: 330px;
height: 80%;
}
`;
var Loader = styled2.div`
border: 6px solid #f3f3f3;
border-top: 6px solid ${({ theme }) => theme === "primary" ? "#007bff" : "#343a40"};
border-radius: 50%;
width: 50px;
height: 50px;
animation: ${spin} 1s linear infinite;
margin: 150px auto;
margin-top: 200px;
`;
var App = ({
icon,
toggleBtnBgColor = "",
toggleBtncolor = "",
animate = true,
token,
theme = "primary",
position = "right",
wantToShowSuggestions = false
}) => {
const [show, setShow] = useState2(false);
const [chatbotDetails, setChatbotDetails] = useState2(null);
useEffect2(() => {
const fetchChatbotDetails = async () => {
try {
const details = await getChatbotDetails_default(token);
setChatbotDetails(details);
} catch (error) {
console.error(error);
}
};
fetchChatbotDetails();
}, [token]);
if (!token) {
console.error("Valid Token is required");
throw new Error("Valid Token is required");
}
return /* @__PURE__ */ jsxs2(AppContainer, { children: [
/* @__PURE__ */ jsx2(
ChatbotToggleButton,
{
position,
theme,
animate: !show && animate,
style: { backgroundColor: toggleBtnBgColor, color: toggleBtncolor },
onClick: () => setShow(!show),
children: show ? /* @__PURE__ */ jsx2(AiOutlineClose, {}) : icon || /* @__PURE__ */ jsx2(TbMessageChatbot, { size: 45 })
}
),
/* @__PURE__ */ jsx2(ChatbotWrapper, { show, position, children: chatbotDetails ? /* @__PURE__ */ jsx2(chatbot_default, { chatbotDetails, theme, position, wantToShowSuggestions }) : /* @__PURE__ */ jsx2(Loader, { theme }) })
] });
};
var App_default = App;
export {
App_default as ChatBot
};