diff --git a/backend/email/email.js b/backend/email/email.js index 5b70a030c..4eff7b09f 100644 --- a/backend/email/email.js +++ b/backend/email/email.js @@ -1,4 +1,16 @@ +const sentEmails = []; + +function clearEmails() { + sentEmails.length = 0; +} + +function getSentEmails() { + return sentEmails; +} + function sendEmail(email, subject, message) { + // add to the list of sent emails + sentEmails.push({ address: email, subject: subject, content: message }); response = "Sending email to " + email + @@ -10,4 +22,4 @@ function sendEmail(email, subject, message) { return response; } -module.exports = { sendEmail }; +module.exports = { clearEmails, getSentEmails, sendEmail }; diff --git a/backend/router.js b/backend/router.js index 8da6edcfe..6dd33656e 100644 --- a/backend/router.js +++ b/backend/router.js @@ -1,5 +1,6 @@ // Importing express module const express = require("express"); +const { clearEmails, getSentEmails } = require("./email/email"); const { chatGptSendMessage, clearMessages } = require("./openai/openai"); const router = express.Router(); @@ -8,6 +9,17 @@ router.get("/", (req, res, next) => { res.send("Hello world"); }); +// Clear sent emails +router.post("/email/clear", (req, res, next) => { + clearEmails(); + res.send("Sent emails cleared"); +}); + +// Get sent emails +router.get("/email/get", (req, res, next) => { + res.send(getSentEmails()); +}); + // Chat to ChatGPT router.post("/openai/chat", async (req, res, next) => { const message = req.body?.message; diff --git a/frontend/src/App.css b/frontend/src/App.css index 2e7dd7bfb..797a90135 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,4 +1,10 @@ #main-area { + display: flex; + flex-direction: row; + height: 100%; +} + +#centre-area { display: flex; flex-direction: column; border-width: 0px 1px; @@ -6,7 +12,7 @@ border-color: #ccc; position: relative; height: 100%; - width: 500px; + width: 40%; margin: 0 auto; text-align: center; } @@ -20,3 +26,19 @@ h1 { height: 100%; } +.side-bar { + display: flex; + flex-direction: column; + height: 100%; + width: 30%; +} + +.side-bar-header { + border-color: #ccc; + border-width: 0 0 1px 0; + border-style: solid; + margin: 40px; + margin-bottom: 10px; + padding-bottom: 20px; + text-align: center; +} diff --git a/frontend/src/App.js b/frontend/src/App.js index 87b04006b..9332b90fa 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,15 +1,27 @@ import "./App.css"; -import ChatBox from "./components/ChatBox"; +import ChatBox from "./components/ChatBox/ChatBox"; +import EmailBox from "./components/EmailBox/EmailBox"; import Header from "./components/Header"; +import { useState } from "react"; function App() { + const [emails, setEmails] = useState([]); + return ( -
-
- -
+ + +
+
+ +
+ +
); } export default App; - diff --git a/frontend/src/components/ChatBox.css b/frontend/src/components/ChatBox.css deleted file mode 100644 index 47d589ff5..000000000 --- a/frontend/src/components/ChatBox.css +++ /dev/null @@ -1,23 +0,0 @@ -#chat-box { - display: flex; - flex-direction: column; - flex-grow: 1; -} - -#chat-box-input { - align-self: flex-end; - display: flex; - justify-content: center; - width: 100%; - padding: 40px 0px; -} - -#chat-box-input input { - width: 85%; - height: 40px; - border: 1px solid #ccc; - border-radius: 5px; - padding: 0 20px; - font-size: 16px; - box-sizing: border-box; -} diff --git a/frontend/src/components/ChatBox.jsx b/frontend/src/components/ChatBox.jsx deleted file mode 100644 index 44ed0d759..000000000 --- a/frontend/src/components/ChatBox.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useState } from "react"; - -import "./ChatBox.css"; -import ChatBoxFeed from "./ChatBoxFeed"; - -function ChatBox() { - const [messages, setMessages] = useState([]); - - const onKeyUpValue = (event) => { - if (event.key === "Enter") { - // get the message - const message = event.target.value; - // add it to the list of messages - setMessages((messages) => [...messages, { message, isUser: true }]); - // clear the input - event.target.value = ""; - - // send the message to the backend - fetch("http://localhost:3001/openai/chat", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ message }), - }).then((response) => { - // get the response message - response.text().then((data) => { - // add it to the list of messages - setMessages((messages) => [ - ...messages, - { message: data, isUser: false }, - ]); - }); - }); - } - }; - - return ( -
- -
- -
-
- ); -} - -export default ChatBox; diff --git a/frontend/src/components/ChatBox/ChatBox.css b/frontend/src/components/ChatBox/ChatBox.css new file mode 100644 index 000000000..1b0256443 --- /dev/null +++ b/frontend/src/components/ChatBox/ChatBox.css @@ -0,0 +1,49 @@ +#chat-box { + display: flex; + flex-direction: column; + flex-grow: 1; +} + +#chat-box-footer { + align-items: center; + display: flex; + padding: 0 32px; +} + +#chat-box-input { + align-self: flex-end; + display: flex; + justify-content: center; + width: 100%; + padding: 40px 0px; +} + +#chat-box-input input { + width: 100%; + height: 40px; + border: 1px solid #ccc; + border-radius: 5px; + padding: 0 12px; + font-size: 16px; + box-sizing: border-box; +} + +#chat-box-button { + padding-left: 20px; +} + +#chat-box-button button { + width: 100%; + height: 40px; + border: 1px solid #ccc; + border-radius: 5px; + padding: 0 12px; + font-size: 16px; + box-sizing: border-box; + color: #666; +} + +#chat-box-button button:hover { + background-color: #ccc; + color: #fff; +} diff --git a/frontend/src/components/ChatBox/ChatBox.jsx b/frontend/src/components/ChatBox/ChatBox.jsx new file mode 100644 index 000000000..256395336 --- /dev/null +++ b/frontend/src/components/ChatBox/ChatBox.jsx @@ -0,0 +1,77 @@ +import React, { useEffect, useState } from "react"; + +import "./ChatBox.css"; +import ChatBoxFeed from "./ChatBoxFeed"; +import { + clearOpenAiChat, + openAiSendMessage, +} from "../../service/openaiService"; +import { getSentEmails } from "../../service/emailService"; + +function ChatBox(props) { + const [messages, setMessages] = useState([]); + + // called on mount + useEffect(() => { + // clear remote messages + clearOpenAiChat(); + // get sent emails + getSentEmails().then((sentEmails) => { + props.setEmails(sentEmails); + }); + }, []); + + const clearClicked = () => { + // clear local messages + setMessages([]); + // clear remote messages + clearOpenAiChat(); + }; + + const sendChatMessage = async (event) => { + if (event.key === "Enter") { + // get the message + const message = event.target.value; + // add it to the list of messages + setMessages((messages) => [ + ...messages, + { message: message, isUser: true }, + ]); + // clear the input + event.target.value = ""; + + const reply = await openAiSendMessage(message); + // add it to the list of messages + setMessages((messages) => [ + ...messages, + { message: reply, isUser: false }, + ]); + + // get sent emails + const sentEmails = await getSentEmails(); + // update emails + props.setEmails(sentEmails); + } + }; + + return ( +
+ + +
+ ); +} + +export default ChatBox; diff --git a/frontend/src/components/ChatBoxFeed.css b/frontend/src/components/ChatBox/ChatBoxFeed.css similarity index 75% rename from frontend/src/components/ChatBoxFeed.css rename to frontend/src/components/ChatBox/ChatBoxFeed.css index eeeebe429..d2ef15848 100644 --- a/frontend/src/components/ChatBoxFeed.css +++ b/frontend/src/components/ChatBox/ChatBoxFeed.css @@ -1,6 +1,6 @@ #chat-box-feed { display: flex; - flex-direction: column; + flex-direction: column-reverse; flex-grow: 1; height: 1px; overflow: auto; diff --git a/frontend/src/components/ChatBoxFeed.jsx b/frontend/src/components/ChatBox/ChatBoxFeed.jsx similarity index 85% rename from frontend/src/components/ChatBoxFeed.jsx rename to frontend/src/components/ChatBox/ChatBoxFeed.jsx index cedb99434..334a92520 100644 --- a/frontend/src/components/ChatBoxFeed.jsx +++ b/frontend/src/components/ChatBox/ChatBoxFeed.jsx @@ -4,7 +4,7 @@ import ChatBoxMessage from "./ChatBoxMessage"; function ChatBoxFeed(props) { return (
- {props.messages.map((message, index) => { + {props.messages.toReversed().map((message, index) => { return ( + {props.emails.toReversed().map((email, index) => { + return ( + + ); + })} +
+ ); +} + +export default EmailBox; diff --git a/frontend/src/components/EmailBox/SentEmail.css b/frontend/src/components/EmailBox/SentEmail.css new file mode 100644 index 000000000..d94a0aaa5 --- /dev/null +++ b/frontend/src/components/EmailBox/SentEmail.css @@ -0,0 +1,15 @@ +.sent-email { + border-color: #ccc; + border-radius: 5px; + border-style: solid; + border-width: 1px; + font-size: 14px; + margin-top: 10px; + padding: 5px; +} + +.sent-email-divider { + border-bottom: 1px solid #ccc; + margin: 5px 0; + width: 100%; +} diff --git a/frontend/src/components/EmailBox/SentEmail.jsx b/frontend/src/components/EmailBox/SentEmail.jsx new file mode 100644 index 000000000..2d36746cf --- /dev/null +++ b/frontend/src/components/EmailBox/SentEmail.jsx @@ -0,0 +1,14 @@ +import "./SentEmail.css"; + +function SentEmail(props) { + return ( +
+
to: {props.address}
+
subject: {props.subject}
+
+
{props.content}
+
+ ); +} + +export default SentEmail; diff --git a/frontend/src/service/emailService.js b/frontend/src/service/emailService.js new file mode 100644 index 000000000..80a4d10b9 --- /dev/null +++ b/frontend/src/service/emailService.js @@ -0,0 +1,18 @@ +const URL = "http://localhost:3001/email/"; + +async function clearEmails() { + const response = await fetch(URL + "clear", { + method: "POST", + }); + return response.status === 200; +} + +async function getSentEmails() { + const response = await fetch(URL + "get", { + method: "GET", + }); + const data = await response.json(); + return data; +} + +export { clearEmails, getSentEmails }; diff --git a/frontend/src/service/openaiService.js b/frontend/src/service/openaiService.js new file mode 100644 index 000000000..66056b197 --- /dev/null +++ b/frontend/src/service/openaiService.js @@ -0,0 +1,22 @@ +const URL = "http://localhost:3001/openai/"; + +async function clearOpenAiChat() { + const response = await fetch(URL + "clear", { + method: "POST", + }); + return response.status === 200; +} + +async function openAiSendMessage(message) { + const response = await fetch(URL + "chat", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ message }), + }); + const data = await response.text(); + return data; +} + +export { clearOpenAiChat, openAiSendMessage };