diff --git a/thisable/package.json b/thisable/package.json
index ec6b9f5..427cd6f 100644
--- a/thisable/package.json
+++ b/thisable/package.json
@@ -8,7 +8,9 @@
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^13.5.0",
+ "antd": "^3.26.5",
"axios": "^0.26.1",
+ "prop-types": "^15.7.2",
"bootstrap": "^5.1.3",
"dotenv": "^16.0.0",
"geolocation": "^0.2.0",
@@ -16,11 +18,16 @@
"react": "^17.0.2",
"react-bootstrap": "^2.1.2",
"react-dom": "^17.0.2",
+ "react-redux": "^7.1.3",
"react-geolocated": "^3.2.0",
"react-router-dom": "^6.2.2",
"react-scripts": "5.0.0",
"react-simple-star-rating": "^4.0.5",
"react-star-ratings": "^2.3.0",
+ "redux": "^4.0.5",
+ "redux-promise": "^0.6.0",
+ "redux-thunk": "^2.3.0",
+ "uuid": "^3.3.2",
"web-vitals": "^2.1.4"
},
"scripts": {
@@ -46,5 +53,8 @@
"last 1 firefox version",
"last 1 safari version"
]
+ },
+ "devDependencies": {
+ "http-proxy-middleware": "^0.19.1"
}
}
diff --git a/thisable/src/_actions/message_actions.js b/thisable/src/_actions/message_actions.js
new file mode 100644
index 0000000..79aa12b
--- /dev/null
+++ b/thisable/src/_actions/message_actions.js
@@ -0,0 +1,8 @@
+import { SAVE_MESSAGE } from "./types";
+
+export function saveMessage(dataToSubmit) {
+ return {
+ type: SAVE_MESSAGE,
+ payload: dataToSubmit,
+ };
+}
diff --git a/thisable/src/_actions/types.js b/thisable/src/_actions/types.js
new file mode 100644
index 0000000..96c94b5
--- /dev/null
+++ b/thisable/src/_actions/types.js
@@ -0,0 +1 @@
+export const SAVE_MESSAGE = "save_message";
diff --git a/thisable/src/_reducers/index.js b/thisable/src/_reducers/index.js
new file mode 100644
index 0000000..0ea56ac
--- /dev/null
+++ b/thisable/src/_reducers/index.js
@@ -0,0 +1,8 @@
+import { combineReducers } from "redux";
+import message from "./message_reducer";
+
+const rootReducer = combineReducers({
+ message,
+});
+
+export default rootReducer;
diff --git a/thisable/src/_reducers/message_reducer.js b/thisable/src/_reducers/message_reducer.js
new file mode 100644
index 0000000..c7421e6
--- /dev/null
+++ b/thisable/src/_reducers/message_reducer.js
@@ -0,0 +1,13 @@
+import { SAVE_MESSAGE } from "../_actions/types";
+
+export default function (state = { messages: [] }, action) {
+ switch (action.type) {
+ case SAVE_MESSAGE:
+ return {
+ ...state,
+ messages: state.messages.concat(action.payload),
+ };
+ default:
+ return state;
+ }
+}
diff --git a/thisable/src/assets/images/thumbs_down.svg b/thisable/src/assets/images/thumbs_down.svg
new file mode 100644
index 0000000..e1eed21
--- /dev/null
+++ b/thisable/src/assets/images/thumbs_down.svg
@@ -0,0 +1,3 @@
+
diff --git a/thisable/src/assets/images/thumbs_up.svg b/thisable/src/assets/images/thumbs_up.svg
new file mode 100644
index 0000000..860b520
--- /dev/null
+++ b/thisable/src/assets/images/thumbs_up.svg
@@ -0,0 +1,10 @@
+
diff --git a/thisable/src/components/Chatbot/Chatbot.css b/thisable/src/components/Chatbot/Chatbot.css
new file mode 100644
index 0000000..e9a3e5c
--- /dev/null
+++ b/thisable/src/components/Chatbot/Chatbot.css
@@ -0,0 +1,39 @@
+* {
+ word-break: keep-all;
+ overflow-wrap: break-word;
+ word-wrap: break-word;
+}
+
+.chatbotModal {
+ background-color: #e599b3;
+ width: 5rem;
+ height: 5rem;
+ border-radius: 10px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: fixed;
+ right: 50px;
+ bottom: 50px;
+ box-shadow: 0 10px 10px rgb(0 0 0 / 30%);
+}
+
+.robotBtn > svg {
+ width: 4rem;
+ height: 4rem;
+}
+
+.ant-list-item-meta-content {
+ max-width: 500px;
+ overflow-x: none;
+}
+
+.ant-list-item-meta-title {
+ font-size: 20px !important;
+}
+
+.ant-list-item-meta-description {
+ display: flex;
+ color: black !important;
+}
diff --git a/thisable/src/components/Chatbot/Chatbot.js b/thisable/src/components/Chatbot/Chatbot.js
new file mode 100644
index 0000000..e5631ab
--- /dev/null
+++ b/thisable/src/components/Chatbot/Chatbot.js
@@ -0,0 +1,180 @@
+import React, { useEffect } from "react";
+import Axios from "axios";
+import { useDispatch, useSelector } from "react-redux";
+import { saveMessage } from "../../_actions/message_actions";
+import Message from "./Sections/Message";
+import { List, Icon, Avatar } from "antd";
+import Card from "./Sections/Card";
+import "./Chatbot.css";
+
+function Chatbot() {
+ const dispatch = useDispatch();
+ const messagesFromRedux = useSelector((state) => state.message.messages);
+
+ useEffect(() => {
+ eventQuery("welcomeToMyWebsite");
+ }, []);
+
+ const textQuery = async (text) => {
+ // First Need to take care of the message I sent
+ let conversation = {
+ who: "user",
+ content: {
+ text: {
+ text: text,
+ },
+ },
+ };
+
+ dispatch(saveMessage(conversation));
+ // console.log('text I sent', conversation)
+
+ // We need to take care of the message Chatbot sent
+ const textQueryVariables = {
+ text, //== text: text
+ };
+ try {
+ //I will send request to the textQuery ROUTE
+ const response = await Axios.post(
+ "/api/dialogflow/textQuery",
+ textQueryVariables
+ );
+
+ for (let content of response.data.fulfillmentMessages) {
+ conversation = {
+ who: "bot",
+ content: content,
+ };
+
+ dispatch(saveMessage(conversation));
+ }
+ } catch (error) {
+ conversation = {
+ who: "bot",
+ content: {
+ text: {
+ text: " Error just occured, please check the problem",
+ },
+ },
+ };
+ dispatch(saveMessage(conversation));
+ }
+ };
+
+ const eventQuery = async (event) => {
+ // We need to take care of the message Chatbot sent
+ const eventQueryVariables = {
+ event,
+ };
+ try {
+ //I will send request to the textQuery ROUTE
+ const response = await Axios.post(
+ "/api/dialogflow/eventQuery",
+ eventQueryVariables
+ );
+ for (let content of response.data.fulfillmentMessages) {
+ let conversation = {
+ who: "bot",
+ content: content,
+ };
+ dispatch(saveMessage(conversation));
+ }
+ } catch (error) {
+ let conversation = {
+ who: "bot",
+ content: {
+ text: {
+ text: " Error just occured, please check the problem",
+ },
+ },
+ };
+ dispatch(saveMessage(conversation));
+ }
+ };
+
+ const keyPressHanlder = (e) => {
+ if (e.key === "Enter") {
+ if (!e.target.value) {
+ return alert("you need to type somthing first");
+ }
+
+ //we will send request to text query route
+ textQuery(e.target.value);
+ e.target.value = "";
+ }
+ };
+
+ const renderCards = (cards) => {
+ return cards.map((card, i) =>