diff --git a/src/App.jsx b/src/App.jsx
index 240c1cc6..72bb4480 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -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);
@@ -159,6 +160,7 @@ const App = () => {
} />
+
{!hideHomeHeader() && }
({
+ ...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;
diff --git a/src/components/ChatBot/ChatbotElements.jsx b/src/components/ChatBot/ChatbotElements.jsx
new file mode 100644
index 00000000..d56db28e
--- /dev/null
+++ b/src/components/ChatBot/ChatbotElements.jsx
@@ -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;
+ }
+ }
+`;
diff --git a/src/components/ChatBot/MessageParser.jsx b/src/components/ChatBot/MessageParser.jsx
new file mode 100644
index 00000000..dca2dfd1
--- /dev/null
+++ b/src/components/ChatBot/MessageParser.jsx
@@ -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;
diff --git a/src/components/ChatBot/TCHBot.jsx b/src/components/ChatBot/TCHBot.jsx
new file mode 100644
index 00000000..6f6ee121
--- /dev/null
+++ b/src/components/ChatBot/TCHBot.jsx
@@ -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 (
+ <>
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/components/ChatBot/Widgets/Assists/AssistElements.jsx b/src/components/ChatBot/Widgets/Assists/AssistElements.jsx
new file mode 100644
index 00000000..a0c9c5b3
--- /dev/null
+++ b/src/components/ChatBot/Widgets/Assists/AssistElements.jsx
@@ -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;
+ }
+ }
+ }
+`;
diff --git a/src/components/ChatBot/Widgets/Assists/AssistLinks.jsx b/src/components/ChatBot/Widgets/Assists/AssistLinks.jsx
new file mode 100644
index 00000000..47ee5c7c
--- /dev/null
+++ b/src/components/ChatBot/Widgets/Assists/AssistLinks.jsx
@@ -0,0 +1,20 @@
+import React from "react";
+import { AssistLinksData } from "./AssistLinksData";
+
+import { LinkList } from "./AssistElements";
+
+export default function AssistLinks(props) {
+ const linkMarkup = AssistLinksData.map((data) => {
+ return data.name === props.optionName
+ ? data.linkOptions.map((link) => (
+
+
+ {link.text}
+
+
+ ))
+ : "";
+ });
+
+ return {linkMarkup};
+}
diff --git a/src/components/ChatBot/Widgets/Assists/AssistLinksData.jsx b/src/components/ChatBot/Widgets/Assists/AssistLinksData.jsx
new file mode 100644
index 00000000..3f6a3268
--- /dev/null
+++ b/src/components/ChatBot/Widgets/Assists/AssistLinksData.jsx
@@ -0,0 +1,62 @@
+export const AssistLinksData = [
+ {
+ name: "Join Community",
+ linkOptions: [
+ {
+ text: "Go To Our Community Page",
+ url: "https://www.thecyberhub.org/community",
+ id: 1,
+ },
+ {
+ text: "Join Our Discord Server",
+ url: "https://discord.gg/QHBPq6xP5p",
+ id: 2,
+ },
+ ],
+ },
+ {
+ name: "Contribute To Open-Source",
+ linkOptions: [
+ {
+ text: "Go To Our Open-Source Page",
+ url: "https://www.thecyberhub.org/projects",
+ id: 1,
+ },
+ {
+ text: "View on GitHub",
+ url: "https://github.com/kabir0x23",
+ id: 2,
+ },
+ ],
+ },
+ {
+ name: "Apply For Job(s)",
+ linkOptions: [
+ {
+ text: "Go To Our Jobs/Internship Page",
+ url: "https://www.thecyberhub.org/prep/jobs",
+ id: 1,
+ },
+ ],
+ },
+ {
+ name: "See Upcoming Events",
+ linkOptions: [
+ {
+ text: "Go To Our Events Page",
+ url: "https://www.thecyberhub.org/events",
+ id: 1,
+ },
+ ],
+ },
+ {
+ name: "Enroll For Course(s)",
+ linkOptions: [
+ {
+ text: "Go To Our Courses Page",
+ url: "https://www.thecyberhub.org/resources/courses",
+ id: 1,
+ },
+ ],
+ },
+];
diff --git a/src/components/ChatBot/Widgets/Assists/AssistOptions.jsx b/src/components/ChatBot/Widgets/Assists/AssistOptions.jsx
new file mode 100644
index 00000000..f2ebc08d
--- /dev/null
+++ b/src/components/ChatBot/Widgets/Assists/AssistOptions.jsx
@@ -0,0 +1,88 @@
+import React from "react";
+import { OptionButton, OptionCont } from "./AssistElements";
+import { createClientMessage } from "react-chatbot-kit";
+
+export default function AssistOptions(props) {
+ const optionReply = {
+ "Join Community":
+ "Fantastic, It'll be a priviledge to have you in the community. I've got the following links to join our community, below:",
+ "Contribute To Open-Source":
+ "Great, contributing to Open-Source is gold. I tell you! I've got the following links to our Open-Source projects, below:",
+ "Apply For Job(s)":
+ "The journey of a thousand miles starts with a step. See below, for a link to our Jobs/Internships page. Good luck!",
+ "See Upcoming Events":
+ "Nice! Learn how successful people in your field pull off their tricks by attending events that they air in. Here is a link to our Upcoming Events page:",
+ "Enroll For Course(s)":
+ "You've come to the right place! You'll learn a lot from any of our courses you take. I've got a link to a page showing all of our available courses, below:",
+ };
+ const options = [
+ {
+ name: "Join Community",
+ handler: () => {
+ const message = createClientMessage("Join Community");
+ props.setState((prevState) => ({
+ ...prevState,
+ messages: [...prevState.messages, message],
+ }));
+ props.actionProvider.suggestLinks("Join Community", optionReply);
+ },
+ id: 1,
+ },
+ {
+ name: "Contribute To Open-Source",
+ handler: () => {
+ const message = createClientMessage("Contribute To Open-Source");
+ props.setState((prevState) => ({
+ ...prevState,
+ messages: [...prevState.messages, message],
+ }));
+ props.actionProvider.suggestLinks("Contribute To Open-Source", optionReply);
+ },
+ id: 2,
+ },
+ {
+ name: "Apply For Job(s)",
+ handler: () => {
+ const message = createClientMessage("Apply For Job(s)");
+ props.setState((prevState) => ({
+ ...prevState,
+ messages: [...prevState.messages, message],
+ }));
+ props.actionProvider.suggestLinks("Apply For Job(s)", optionReply);
+ },
+ id: 3,
+ },
+ {
+ name: "See Upcoming Events",
+ handler: () => {
+ const message = createClientMessage("See Upcoming Events");
+ props.setState((prevState) => ({
+ ...prevState,
+ messages: [...prevState.messages, message],
+ }));
+ props.actionProvider.suggestLinks("See Upcoming Events", optionReply);
+ },
+ id: 4,
+ },
+ {
+ name: "Enroll For Course(s)",
+ handler: () => {
+ const message = createClientMessage("Enroll For Course(s)");
+ props.setState((prevState) => ({
+ ...prevState,
+ messages: [...prevState.messages, message],
+ }));
+ props.actionProvider.suggestLinks("Enroll For Course(s)", optionReply);
+ },
+ id: 5,
+ },
+ ];
+
+ const optionsMarkup = options.map((option) => (
+
+ {option.name}
+
+ ));
+
+ return {optionsMarkup};
+}
diff --git a/src/components/ChatBot/Widgets/BotComponents/TCHBotAvatar.jsx b/src/components/ChatBot/Widgets/BotComponents/TCHBotAvatar.jsx
new file mode 100644
index 00000000..7cee5038
--- /dev/null
+++ b/src/components/ChatBot/Widgets/BotComponents/TCHBotAvatar.jsx
@@ -0,0 +1,14 @@
+import React from "react";
+// import BotAvatar from "../../../../assets/thecyberworld-green01.png";
+import { getCdnAssets } from "../../../../features/apiUrl";
+const logoThecyberworld = `${getCdnAssets}/images/ThecyberworldLogo/Thecyberworld_logo_outlined.png`;
+
+export default function TCHBotAvatar() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/components/ChatBot/config.jsx b/src/components/ChatBot/config.jsx
new file mode 100644
index 00000000..82a47dbc
--- /dev/null
+++ b/src/components/ChatBot/config.jsx
@@ -0,0 +1,55 @@
+import React from "react";
+
+// // Config code
+import { createChatBotMessage } from "react-chatbot-kit";
+
+import AssistOptions from "./Widgets/Assists/AssistOptions";
+import AssistLinks from "./Widgets/Assists/AssistLinks";
+
+import TCHBotAvatar from "./Widgets/BotComponents/TCHBotAvatar";
+
+const botName = "TCHBot";
+
+const config = {
+ initialMessages: [
+ createChatBotMessage(`Hi, Welcome to Thecyberhub! I'm ${botName}, and I'm here to help.`),
+ createChatBotMessage("But, before we get down to business, what's your name?"),
+ ],
+ botName,
+ state: {
+ optionName: "",
+ },
+ customComponents: { botAvatar: (props) => },
+ widgets: [
+ {
+ widgetName: "assistOptions",
+ widgetFunc: (props) => ,
+ },
+ {
+ widgetName: "assistLinks",
+ widgetFunc: (props) => ,
+ mapStateToProps: ["optionName"],
+ props: {
+ options: [
+ {
+ text: "Introduction to JS",
+ url: "https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-javascript/",
+ id: 1,
+ },
+ {
+ text: "Mozilla JS Guide",
+ url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide",
+ id: 2,
+ },
+ {
+ text: "Frontend Masters",
+ url: "https://frontendmasters.com",
+ id: 3,
+ },
+ ],
+ },
+ },
+ ],
+};
+
+export default config;