Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Chatbot #489

Merged
merged 1 commit into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import SecurityRoutes from "./components/Other/Security/SecurityRoutes";
import ExploreRoutes from "./components/Explore/ExploreRoutes";
import Leaderboard from "./components/Other/CyberGames/Leaderboard/Leaderboard";
import SettingsRoute from "./components/Dashboard/Settings";
import TCHBot from "./components/Chatbot/TCHBot";

const App = () => {
const [isLoading, setIsLoading] = useState(true);
Expand Down Expand Up @@ -159,6 +160,7 @@ const App = () => {
<Route path={"*"} element={<NotFound />} />
</Routes>
</ScrollToTop>
<TCHBot />
{!hideHomeHeader() && <Footer />}
</Container>
<ToastContainer
Expand Down
44 changes: 44 additions & 0 deletions src/components/ChatBot/ActionProvider.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// ActionProvider code
class ActionProvider {
constructor(createChatBotMessage, setStateFunc, createClientMessage, stateRef, createCustomMessage, ...rest) {
this.createChatBotMessage = createChatBotMessage;
this.setState = setStateFunc;
this.createClientMessage = createClientMessage;
this.stateRef = stateRef;
this.createCustomMessage = createCustomMessage;
}

updateChatbotState(message) {
this.setState((prevState) => ({
...prevState,
messages: [...prevState.messages, message],
}));
}

welcome(name) {
const userName = name.charAt(0).toUpperCase() + name.slice(1);
const welcomeMessage = this.createChatBotMessage(`Hi, ${userName}! How can I best assist you today?`, {
widget: "assistOptions",
});
this.updateChatbotState(welcomeMessage);
}

warn() {
const warnMessage = this.createChatBotMessage("I didn't get that. Your First Name would do please.");
this.updateChatbotState(warnMessage);
}

suggestLinks = (name, reply) => {
const message = this.createChatBotMessage(`${reply[name]}`, {
widget: "assistLinks",
});

this.setState((prevState) => ({
...prevState,
optionName: name,
messages: [...prevState.messages, message],
}));
};
}

export default ActionProvider;
195 changes: 195 additions & 0 deletions src/components/ChatBot/ChatbotElements.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import styled from "styled-components";

export const ThecyberhubBot = styled.div`
background-color: #0b0b0b;
color: #cecac3;
outline: 1px solid rgb(42, 42, 42);
display: ${({ callBot }) => (callBot ? "block" : "none")};
height: 100vh;
width: 40vw;
transition: 260ms all;
padding: 2rem 2rem 0;
position: fixed;
right: 0;
top: 0;
z-index: 11;

@media screen and (max-width: 1024px) {
width: 50vw;
}
@media screen and (max-width: 768px) {
width: 70vw;
}
@media screen and (max-width: 640px) {
width: 100vw;
}

.react-chatbot-kit-chat-container {
border-radius: 0.5rem;
outline: 1px solid #2a2a2a;
transition: 260ms all;
width: 100%;

:hover {
outline-color: #545454;
outline-offset: 2px;
}

.react-chatbot-kit-chat-inner-container {
.react-chatbot-kit-chat-header {
background: rgba(32, 194, 14, 0.04);
background: linear-gradient(to right, #b1faa9, #f6dbaa);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
border-bottom: 1px solid #363636;
border-radius: 0.5rem 0.5rem 0 0;
font-weight: 500;
padding: 1rem;
}

.react-chatbot-kit-chat-message-container {
display: flex;
flex-direction: column;
justify-content: start;
row-gap: 1.3rem;
height: 60vh;
overflow: auto;
padding: 1.5rem;

.react-chatbot-kit-chat-bot-message-container {
display: flex;
column-gap: 0.5rem;
.react-chatbot-kit-chat-bot-avatar {
div {
width: 2.5rem;
}
img {
object-fit: fill;
width: 100%;
}
}
.react-chatbot-kit-chat-bot-message {
background: #1a1c1d;
border-radius: 0 0.5rem 0.5rem;
outline: 2px solid #2a2a2a;
overflow-wrap: break-word;
padding: 0.5rem;
/* width: fit-content; */

span {
font-weight: 400;
letter-spacing: 0.08rem;
}
}
}

.react-chatbot-kit-user-chat-message-container {
display: flex;
justify-content: end;
column-gap: 0.5rem;
.react-chatbot-kit-user-chat-message {
background: rgba(32, 194, 14, 0.2);
border-radius: 0.5rem 0 0.5rem 0.5rem;
font-weight: 300;
letter-spacing: 0.08rem;
outline: 2px solid #2a2a2a;
overflow-wrap: break-word;
padding: 0.5rem;
}
.react-chatbot-kit-user-avatar {
div {
height: 2rem;
width: 2rem;
background: linear-gradient(to right, #b1faa9, #f6dbaa);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
svg {
fill: rgb(32, 194, 14);
width: 50%;
}
}
}
}

.react-chatbot-kit-chat-input-container {
border-top: 1px solid #363636;
height: 3rem;
width: 100%;

form {
display: flex;
align-items: center;
justify-content: start;
height: 100%;
width: 100%;
}
input {
background: rgba(32, 194, 14, 0.04);
border: none;
border-radius: 0 0 0 0.5rem;
caret-color: white;
color: #cecac3;
height: 100%;
width: 100%;
text-indent: 1rem;

:focus {
outline: none;
}
::placeholder {
color: #cecac3;
font-weight: 100;
letter-spacing: 0.1rem;
}
}
button {
background: rgba(32, 194, 14, 0.04);
border: none;
border-radius: 0 0 0.5rem 0;
cursor: pointer;
height: 100%;
width: 3rem;
transition: 260ms all;

:hover {
background: rgba(32, 194, 14, 0.2);
}
}
svg {
fill: #cecac3;
width: 1rem;
}
}
}
}
`;

export const BotButtonCont = styled.div`
position: fixed;
right: 2rem;
bottom: 1rem;
z-index: 11;

button {
background: #0f0a06;
border-radius: 50%;
cursor: pointer;
outline: 2px solid #363636;
display: flex;
justify-content: center;
align-items: center;
width: 3.5rem;
height: 3.5rem;
transition: 260ms all;

:hover {
background: #23150e;
outline-color: #545454;
outline-offset: 1px;
}
}
`;
39 changes: 39 additions & 0 deletions src/components/ChatBot/MessageParser.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// MessageParser code
class MessageParser {
constructor(actionProvider, state) {
this.actionProvider = actionProvider;
this.state = state;
}

parse(message) {
const lowerCaseMessage = message.toLowerCase();
const lowerCaseMessageArr = lowerCaseMessage.split(" ");
const actions = this.actionProvider;

// Detect the User's name
if (lowerCaseMessage === "") {
actions.warn();
} else if (lowerCaseMessage.includes("is") || lowerCaseMessage.includes("am")) {
const userName = lowerCaseMessage.includes("is")
? lowerCaseMessageArr[lowerCaseMessageArr.indexOf("is") + 1]
: lowerCaseMessageArr[lowerCaseMessageArr.indexOf("am") + 1];
actions.welcome(userName);
} else if (lowerCaseMessageArr.length === 1) {
actions.welcome(lowerCaseMessage);
} else if (lowerCaseMessageArr.length === 2 && lowerCaseMessage.includes("i'm")) {
actions.welcome(lowerCaseMessageArr[1]);
} else if (lowerCaseMessageArr.length === 2) {
actions.welcome(lowerCaseMessageArr[0]);
} else if (
!lowerCaseMessage.includes("is") &&
lowerCaseMessageArr.length !== 1 &&
lowerCaseMessageArr.length !== 2
) {
actions.warn();
} else {
actions.warn();
}
}
}

export default MessageParser;
45 changes: 45 additions & 0 deletions src/components/ChatBot/TCHBot.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useState, useCallback } from "react";

import Chatbot from "react-chatbot-kit";

import config from "./config";
import ActionProvider from "./ActionProvider";
import MessageParser from "./MessageParser";

import { ThecyberhubBot, BotButtonCont } from "./ChatbotElements";

// import { FaRobot } from "react-icons/fa";
import { CgClose } from "react-icons/cg";
import { getCdnAssets } from "../../features/apiUrl";
const logoThecyberworld = `${getCdnAssets}/images/ThecyberworldLogo/Thecyberworld_logo_outlined.png`;

export default function TCHBot() {
const [callBot, setCallBot] = useState(false);

const talkToBot = useCallback(() => {
setCallBot((prevCallBot) => !prevCallBot);
}, []);

return (
<>
<ThecyberhubBot callBot={callBot}>
<Chatbot config={config} messageParser={MessageParser} actionProvider={ActionProvider} />
</ThecyberhubBot>
<BotButtonCont>
<button onClick={talkToBot}>
{!callBot ? (
<img
style={{
padding: "5px",
}}
src={logoThecyberworld}
alt={""}
/>
) : (
<CgClose size={35} style={{ color: "white" }} />
)}
</button>
</BotButtonCont>
</>
);
}
56 changes: 56 additions & 0 deletions src/components/ChatBot/Widgets/Assists/AssistElements.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import styled from "styled-components";

export const OptionButton = styled.button`
background: transparent;
border-radius: 0.3rem;
color: #cecac3;
cursor: pointer;
font-weight: 700;
letter-spacing: 0.08rem;
outline: 1px solid rgba(32, 194, 14, 0.4);
padding: 0.5rem;
transition: 260ms all;

:hover {
background: rgba(32, 194, 14, 0.2);
color: white;
outline-color: #545454;
outline-offset: 1px;
}
`;

export const OptionCont = styled.div`
display: flex;
gap: 0.5rem;
align-items: flex-start;
flex-wrap: wrap;
`;

export const LinkList = styled.ul`
list-style: none;
display: flex;
flex-direction: column;
row-gap: 1.5rem;

li {
a {
text-decoration: none;
background: rgba(32, 194, 14, 0.2);
border-radius: 0.3rem;
color: white;
font-weight: 500;
font-style: italic;
letter-spacing: 0.08rem;
outline: 1px solid #545454;
padding: 0.5rem;
transition: 260ms all;

:hover {
background: transparent;
color: #cecac3;
outline-color: rgba(32, 194, 14, 0.4);
outline-offset: 1px;
}
}
}
`;
Loading