From c6b9f5039d0ffe9549ae1310451b64e2f121677e Mon Sep 17 00:00:00 2001 From: Bavly Hamdy <100946403+Bavly-Hamdy@users.noreply.github.com> Date: Tue, 10 Dec 2024 03:56:28 +0200 Subject: [PATCH] Summary: This project is a web-based chatbot application integrated with OpenAI's GPT-4 for user interaction. The app allows users to send and receive messages, with the chat history being saved locally in the browser using localStorage. Users can view their past conversations in the History page, which stores and displays previous chats along with timestamps. The app is designed with responsive styling and supports dark/light modes for a customizable experience. Description: This project includes: A chatbot interface where users can chat with an AI-powered bot. The ability to save chat history locally in the browser. A History page where users can view past conversations. Dark mode toggle for better user experience. Integration with Firebase for authentication (login/logout). --- package-lock.json | 10 ++ package.json | 5 +- src/App.js | 5 + src/components/Chatbot/Favourite.css | 27 ---- src/components/Chatbot/Favourite.jsx | 90 ++++++++++--- src/components/Chatbot/Favourite.module.css | 135 ++++++++++++++++++++ src/components/Chatbot/History.css | 29 ----- src/components/Chatbot/History.jsx | 66 +++++++--- src/components/Chatbot/History.module.css | 60 +++++++++ src/components/Chatbot/Navbar.module.css | 31 ++++- src/components/Chatbot/index.jsx | 34 +++-- src/components/Chatbot/styles.module.css | 41 +++--- 12 files changed, 409 insertions(+), 124 deletions(-) delete mode 100644 src/components/Chatbot/Favourite.css create mode 100644 src/components/Chatbot/Favourite.module.css delete mode 100644 src/components/Chatbot/History.css create mode 100644 src/components/Chatbot/History.module.css diff --git a/package-lock.json b/package-lock.json index 518a2b6..0215785 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "react": "^18.3.1", "react-chartjs-2": "^5.2.0", "react-dom": "^18.3.1", + "react-icons": "^5.4.0", "react-minimal-pie-chart": "^8.4.1", "react-router-dom": "^7.0.1", "react-scripts": "5.0.1", @@ -15362,6 +15363,15 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", "license": "MIT" }, + "node_modules/react-icons": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.4.0.tgz", + "integrity": "sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index 692ef40..9c9dd81 100644 --- a/package.json +++ b/package.json @@ -19,14 +19,15 @@ "react": "^18.3.1", "react-chartjs-2": "^5.2.0", "react-dom": "^18.3.1", + "react-icons": "^5.4.0", "react-minimal-pie-chart": "^8.4.1", "react-router-dom": "^7.0.1", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, "scripts": { - "predeploy":"npm run build", - "deploy":"gh-pages -d build", + "predeploy": "npm run build", + "deploy": "gh-pages -d build", "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", diff --git a/src/App.js b/src/App.js index eded44d..909c636 100644 --- a/src/App.js +++ b/src/App.js @@ -4,6 +4,9 @@ import Signup from "./components/Singup"; import Login from "./components/Login"; import Chatbot from "./components/Chatbot"; import SavedMessages from "./components/Chatbot/SavedMessages"; +import FavouriteMessages from "./components/Chatbot/Favourite"; +import History from "./components/Chatbot/History"; + function App() { const user = localStorage.getItem("token"); @@ -19,6 +22,8 @@ function App() { } /> )} } /> + } /> + } /> } /> } /> diff --git a/src/components/Chatbot/Favourite.css b/src/components/Chatbot/Favourite.css deleted file mode 100644 index 632fc76..0000000 --- a/src/components/Chatbot/Favourite.css +++ /dev/null @@ -1,27 +0,0 @@ -.favourite-container { - padding: 20px; - margin-left: 220px; /* Same offset for alignment */ - } - - .favourite-container h2 { - font-size: 24px; - margin-bottom: 20px; - } - - .favourite-list { - display: flex; - gap: 20px; - } - - .favourite-item { - background-color: #f1f0f0; - padding: 20px; - border-radius: 8px; - cursor: pointer; - transition: transform 0.3s ease; - } - - .favourite-item:hover { - transform: scale(1.05); - } - \ No newline at end of file diff --git a/src/components/Chatbot/Favourite.jsx b/src/components/Chatbot/Favourite.jsx index fc0eaab..355ac39 100644 --- a/src/components/Chatbot/Favourite.jsx +++ b/src/components/Chatbot/Favourite.jsx @@ -1,25 +1,85 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; -import styles from "./styles.module.css"; +import Navbar from "./Navbar"; // استيراد الـ Navbar +import styles from "./Favourite.module.css"; // استخدام CSS خاص بـ Favourite +import { FaTrash, FaSave } from "react-icons/fa"; // استخدام أيقونات Font Awesome -const Favourite = () => { - const favouriteMessages = JSON.parse(localStorage.getItem("favouriteMessages")) || []; +const FavouriteMessages = () => { const navigate = useNavigate(); + const [favouriteMessages, setFavouriteMessages] = useState([]); + + // تحميل الرسائل المفضلة من localStorage عند تحميل الصفحة + useEffect(() => { + const storedMessages = JSON.parse(localStorage.getItem("favouriteMessages")) || []; + setFavouriteMessages(storedMessages); + }, []); + + // حذف رسالة مفضلة + const handleDeleteMessage = (index) => { + const updatedMessages = favouriteMessages.filter((_, i) => i !== index); + setFavouriteMessages(updatedMessages); + localStorage.setItem("favouriteMessages", JSON.stringify(updatedMessages)); + }; + + // حفظ الرسالة (هنا قد تحتاج لإضافة وظيفة إضافية مثل نقل الرسالة لقسم آخر أو تنفيذ إجراء معين) + const handleSaveMessage = (index) => { + const messageToSave = favouriteMessages[index]; + console.log("Message saved:", messageToSave); + // إضافة منطق لحفظ الرسالة إذا لزم الأمر + }; + + // مسح كل الرسائل المفضلة + const handleClearAllMessages = () => { + setFavouriteMessages([]); + localStorage.removeItem("favouriteMessages"); + }; return ( -
-

Favourite Messages

- {favouriteMessages.length > 0 ? ( - favouriteMessages.map((msg, index) => ( -
-

{msg.text}

+
+ + +
+

Favourite Messages

+ {favouriteMessages.length > 0 ? ( +
+
+
Favourite Messages
+
Actions
+
+ {favouriteMessages.map((msg, index) => ( +
+
{msg.text}
+
+ + +
+
+ ))}
- )) - ) : ( -

No favourite messages yet.

- )} + ) : ( +

No favourite messages yet.

+ )} +
+ + +
+
); }; -export default Favourite; +export default FavouriteMessages; diff --git a/src/components/Chatbot/Favourite.module.css b/src/components/Chatbot/Favourite.module.css new file mode 100644 index 0000000..2342c7f --- /dev/null +++ b/src/components/Chatbot/Favourite.module.css @@ -0,0 +1,135 @@ +/* Page Container */ +.pageContainer { + padding: 20px; + font-family: Arial, sans-serif; +} + +/* Title */ +.title { + text-align: center; + font-size: 28px; + margin-bottom: 20px; + color: #333; +} + +/* Message Table */ +.messageTable { + width: 100%; + margin-top: 20px; +} + +/* Table Header */ +.tableHeader { + display: flex; + padding: 20px; + background-color: #f1f1f1; + border-radius: 8px; + justify-content: space-between; + margin-bottom: 10px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.columnHeader { + font-weight: bold; + width: 48%; + text-align: center; + color: #555; +} + +/* Message Row */ +.messageRow { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + padding: 15px; + background-color: #fff; + border-radius: 8px; + border: 1px solid #ddd; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + transition: transform 0.3s, box-shadow 0.3s; +} + +.messageRow:hover { + transform: translateY(-5px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +/* Message Text */ +.messageText { + width: 48%; + padding: 10px; + background-color: #f9f9f9; + border: 1px solid #ddd; + border-radius: 4px; + transition: background-color 0.3s; +} + +.messageText:hover { + background-color: #eaeaea; +} + +/* Action Icons */ +.actionIcons { + width: 48%; + display: flex; + justify-content: center; + gap: 15px; +} + +.deleteButton, .saveButton { + background: none; + border: none; + font-size: 22px; + cursor: pointer; + transition: color 0.3s, transform 0.3s; +} + +.deleteButton:hover, .saveButton:hover { + color: #ff4d4d; + transform: scale(1.2); +} + +.saveButton:hover { + color: #45a049; +} + + +/* Buttons Container */ +.buttonsContainer { + display: flex; + justify-content: space-between; + margin-top: 30px; +} + +.backButton, .clearButton { + padding: 12px 25px; + background-color: #4CAF50; + color: white; + border-radius: 8px; + border: none; + cursor: pointer; + font-size: 16px; + transition: background-color 0.3s, transform 0.3s; +} + +.backButton:hover, .clearButton:hover { + background-color: #45a049; + transform: scale(1.05); +} + +.clearButton { + background-color: #f44336; +} + +.clearButton:hover { + background-color: #e53935; +} + +/* No Messages */ +.noMessages { + text-align: center; + color: #777; + font-size: 18px; + font-style: italic; +} diff --git a/src/components/Chatbot/History.css b/src/components/Chatbot/History.css deleted file mode 100644 index c3f76ff..0000000 --- a/src/components/Chatbot/History.css +++ /dev/null @@ -1,29 +0,0 @@ -.history-container { - padding: 20px; - margin-left: 220px; /* Same as above, keeping it consistent with the sidebar */ - } - - .history-container h2 { - font-size: 24px; - margin-bottom: 20px; - } - - .history-list { - list-style: none; - padding: 0; - } - - .history-list li { - background-color: #f5f5f5; - padding: 10px; - margin-bottom: 10px; - border-radius: 5px; - cursor: pointer; - transition: background-color 0.3s; - } - - .history-list li:hover { - background-color: #52a7bc; - color: white; - } - \ No newline at end of file diff --git a/src/components/Chatbot/History.jsx b/src/components/Chatbot/History.jsx index 36e921a..cf9bdfb 100644 --- a/src/components/Chatbot/History.jsx +++ b/src/components/Chatbot/History.jsx @@ -1,25 +1,61 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; -import styles from "./styles.module.css"; +import Navbar from "./Navbar"; // استيراد الـ Navbar +import styles from "./History.module.css"; // CSS خاص بالصفحة -const History = () => { - const historyMessages = JSON.parse(localStorage.getItem("historyMessages")) || []; +const HistoryPage = () => { const navigate = useNavigate(); + const [chatHistory, setChatHistory] = useState([]); + + // تحميل بيانات الـ History عند فتح الصفحة + useEffect(() => { + const storedHistory = JSON.parse(localStorage.getItem("chatHistory")) || []; + setChatHistory(storedHistory); + }, []); + + // التعامل مع اختيار شات معين + const handleSelectChat = (index) => { + const selectedChat = chatHistory[index]; + localStorage.setItem("currentChat", JSON.stringify(selectedChat)); + navigate("/chatbot"); + }; + + // مسح كل المحادثات + const handleClearAllHistory = () => { + setChatHistory([]); + localStorage.removeItem("chatHistory"); + }; return ( -
-

Chat History

- {historyMessages.length > 0 ? ( - historyMessages.map((msg, index) => ( -
-

{msg}

+
+ +
+

Chat History

+ {chatHistory.length > 0 ? ( +
+ {chatHistory.map((chat, index) => ( +
handleSelectChat(index)} + > +

+ {chat.messages[0].text} ... ({chat.date}) +

+
+ ))}
- )) - ) : ( -

No history messages yet.

- )} + ) : ( +

No chat history available.

+ )} +
+ +
+
); }; -export default History; +export default HistoryPage; diff --git a/src/components/Chatbot/History.module.css b/src/components/Chatbot/History.module.css new file mode 100644 index 0000000..9a9cbd5 --- /dev/null +++ b/src/components/Chatbot/History.module.css @@ -0,0 +1,60 @@ +.pageContainer { + padding: 20px; + max-width: 800px; + margin: auto; +} + +.title { + font-size: 24px; + text-align: center; + margin-bottom: 20px; +} + +.historyList { + display: flex; + flex-direction: column; + gap: 10px; +} + +.historyItem { + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; + cursor: pointer; + background-color: #f9f9f9; + transition: background-color 0.2s; +} + +.historyItem:hover { + background-color: #ececec; +} + +.chatPreview { + margin: 0; + font-size: 16px; +} + +.noHistory { + text-align: center; + font-size: 18px; + color: #777; +} + +.buttonsContainer { + display: flex; + justify-content: center; + margin-top: 20px; +} + +.clearButton { + background-color: #ff4d4d; + color: white; + border: none; + padding: 10px 20px; + border-radius: 5px; + cursor: pointer; +} + +.clearButton:hover { + background-color: #ff1a1a; +} diff --git a/src/components/Chatbot/Navbar.module.css b/src/components/Chatbot/Navbar.module.css index e339947..7faa92a 100644 --- a/src/components/Chatbot/Navbar.module.css +++ b/src/components/Chatbot/Navbar.module.css @@ -1,17 +1,20 @@ /* Navbar.module.css */ .navbar { + height: 70px; + padding: 0 20px; + margin: 0; display: flex; justify-content: space-between; align-items: center; padding: 15px 30px; - background-color: #333; + background-color: #52a7bc; color: white; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); } .logo { - font-size: 24px; + font-size: 25px; font-weight: bold; cursor: pointer; color: white; @@ -26,17 +29,22 @@ } .nav_links li { + color: white; + padding: 10px; + font-weight: bold; margin-left: 20px; cursor: pointer; font-size: 16px; - transition: color 0.3s; + transition: color 0.2s; } .nav_links li:hover { - color: #4CAF50; + color: #cfcfcf; } .user_menu { + display: flex; + align-items: center; position: relative; cursor: pointer; } @@ -44,13 +52,18 @@ .user_icon { font-size: 22px; color: white; + cursor: pointer; + margin-left: 20px; } .dropdown_content { + display: flex; + flex-direction: column; position: absolute; - top: 30px; + top: 40px; right: 0; background-color: #fff; + min-width: 160px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); border-radius: 8px; padding: 10px; @@ -67,7 +80,11 @@ } .dropdown_content a:hover { - background-color: #f1f1f1; + background-color: #52a7bc; + color: white; + cursor: pointer; + transition: background-color 0.3s; + border-radius: 5px; } .favourited { diff --git a/src/components/Chatbot/index.jsx b/src/components/Chatbot/index.jsx index dbc4c53..1c02457 100644 --- a/src/components/Chatbot/index.jsx +++ b/src/components/Chatbot/index.jsx @@ -14,6 +14,7 @@ const Chatbot = () => { const [darkMode, setDarkMode] = useState(false); const [savedMessages, setSavedMessages] = useState([]); const [favouriteMessages, setFavouriteMessages] = useState([]); + const [history, setHistory] = useState([]); // قراءة الـ API Key من متغير البيئة const apiKey = process.env.REACT_APP_OPENAI_API_KEY; @@ -70,6 +71,11 @@ const Chatbot = () => { ...prevMessages, { text: botReply, sender: "bot" }, ]); + + // حفظ المحادثة في التاريخ + const updatedHistory = [...history, { userMessage, botReply }]; + setHistory(updatedHistory); + localStorage.setItem("chatHistory", JSON.stringify(updatedHistory)); } catch (error) { console.error("Error sending message to ChatGPT:", error.response?.data || error); alert( @@ -80,22 +86,18 @@ const Chatbot = () => { // إضافة رسالة إلى الـ Saved Messages const handleSaveMessage = (msg) => { - // استرجاع الرسائل القديمة من localStorage أو تعيين قائمة فارغة إذا لم تكن موجودة const savedMessages = JSON.parse(localStorage.getItem("savedMessages")) || []; - - // إضافة الرسالة الجديدة إلى الرسائل المحفوظة const updatedSavedMessages = [...savedMessages, msg]; - - // تحديث الحالة setSavedMessages(updatedSavedMessages); - - // تخزين الرسائل في localStorage localStorage.setItem("savedMessages", JSON.stringify(updatedSavedMessages)); }; // إضافة رسالة إلى الـ Favourite Messages const handleFavouriteMessage = (msg) => { - setFavouriteMessages((prevFav) => [...prevFav, msg]); + const favourites = JSON.parse(localStorage.getItem("favouriteMessages")) || []; + const updatedFavourites = [...favourites, msg]; + setFavouriteMessages(updatedFavourites); + localStorage.setItem("favouriteMessages", JSON.stringify(updatedFavourites)); }; // متابعة حالة المستخدم @@ -110,6 +112,17 @@ const Chatbot = () => { return () => unsubscribe(); }, []); + useEffect(() => { + // تحميل السجل المحفوظ من localStorage عند بدء التشغيل + const savedHistory = JSON.parse(localStorage.getItem("history")) || []; + setHistory(savedHistory); + }, []); + + // بدء محادثة جديدة + const startNewChat = () => { + setMessages([]); // مسح المحادثات السابقة + }; + return (
{/* Navbar */} @@ -152,6 +165,9 @@ const Chatbot = () => { {/* Chatbox */}
+
{messages.map((msg, index) => (
{ />
diff --git a/src/components/Chatbot/styles.module.css b/src/components/Chatbot/styles.module.css index 34f30ce..ffa963a 100644 --- a/src/components/Chatbot/styles.module.css +++ b/src/components/Chatbot/styles.module.css @@ -33,9 +33,11 @@ input, textarea, select { } .logo { - color: white; - font-size: 25px; - cursor: pointer; + font-size: 25px; + font-weight: bold; + cursor: pointer; + color: white; + text-transform: uppercase; } /* Navbar Links Section */ @@ -64,20 +66,13 @@ input, textarea, select { align-items: center; position: relative; cursor: pointer; - margin-left: 20px; } /* User Icon Style (دائرة تمثل نوع المستخدم) */ .user_icon { - width: 40px; - height: 40px; - border-radius: 50%; - background-color: #f1f1f1; - display: flex; - align-items: center; - justify-content: center; + width: 70px; + font-size: 22px; color: white; - font-size: 1.2rem; cursor: pointer; margin-left: 20px; } @@ -86,25 +81,31 @@ input, textarea, select { display: flex; flex-direction: column; position: absolute; - top: 50px; + top: 40px; right: 0; - background-color: white; + background-color: #fff; min-width: 160px; - box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.2); - z-index: 1; - border-radius: 5px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + border-radius: 8px; + padding: 10px; + z-index: 10; } .dropdown_content a { - padding: 10px; + display: block; + padding: 8px 15px; text-decoration: none; - color: black; - border-radius: 5px; + color: #333; + font-size: 16px; + transition: background-color 0.3s; } .dropdown_content a:hover { background-color: #52a7bc; color: white; + cursor: pointer; + transition: background-color 0.3s; + border-radius: 5px; } /* Sidebar Styles */