tanmaivan commited on
Commit
e065eb6
·
1 Parent(s): f47fc7e

feat: rebrand to HutMind, adjust bot response width, add copy button

Browse files
FrontEnd/package-lock.json CHANGED
@@ -8,6 +8,7 @@
8
  "name": "frontend",
9
  "version": "0.0.0",
10
  "dependencies": {
 
11
  "react": "^18.3.1",
12
  "react-dom": "^18.3.1",
13
  "react-icons": "^5.4.0",
@@ -943,6 +944,16 @@
943
  "@jridgewell/sourcemap-codec": "^1.4.14"
944
  }
945
  },
 
 
 
 
 
 
 
 
 
 
946
  "node_modules/@rollup/rollup-android-arm-eabi": {
947
  "version": "4.30.1",
948
  "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz",
@@ -4337,6 +4348,7 @@
4337
  "version": "18.3.1",
4338
  "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
4339
  "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
 
4340
  "dependencies": {
4341
  "loose-envify": "^1.1.0",
4342
  "scheduler": "^0.23.2"
 
8
  "name": "frontend",
9
  "version": "0.0.0",
10
  "dependencies": {
11
+ "@react-oauth/google": "^0.13.4",
12
  "react": "^18.3.1",
13
  "react-dom": "^18.3.1",
14
  "react-icons": "^5.4.0",
 
944
  "@jridgewell/sourcemap-codec": "^1.4.14"
945
  }
946
  },
947
+ "node_modules/@react-oauth/google": {
948
+ "version": "0.13.4",
949
+ "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.13.4.tgz",
950
+ "integrity": "sha512-hGKyNEH+/PK8M0sFEuo3MAEk0txtHpgs94tDQit+s2LXg7b6z53NtzHfqDvoB2X8O6lGB+FRg80hY//X6hfD+w==",
951
+ "license": "MIT",
952
+ "peerDependencies": {
953
+ "react": ">=16.8.0",
954
+ "react-dom": ">=16.8.0"
955
+ }
956
+ },
957
  "node_modules/@rollup/rollup-android-arm-eabi": {
958
  "version": "4.30.1",
959
  "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz",
 
4348
  "version": "18.3.1",
4349
  "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
4350
  "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
4351
+ "peer": true,
4352
  "dependencies": {
4353
  "loose-envify": "^1.1.0",
4354
  "scheduler": "^0.23.2"
FrontEnd/package.json CHANGED
@@ -10,6 +10,7 @@
10
  "preview": "vite preview"
11
  },
12
  "dependencies": {
 
13
  "react": "^18.3.1",
14
  "react-dom": "^18.3.1",
15
  "react-icons": "^5.4.0",
 
10
  "preview": "vite preview"
11
  },
12
  "dependencies": {
13
+ "@react-oauth/google": "^0.13.4",
14
  "react": "^18.3.1",
15
  "react-dom": "^18.3.1",
16
  "react-icons": "^5.4.0",
FrontEnd/src/ChatBot.css CHANGED
@@ -184,7 +184,8 @@
184
  }
185
 
186
  .message-content {
187
- max-width: 80%;
 
188
  overflow-wrap: break-word;
189
  font-size: 16px;
190
  line-height: 1.6;
@@ -193,6 +194,7 @@
193
  .message-row.user .message-content {
194
  display: flex;
195
  justify-content: flex-end;
 
196
  }
197
 
198
  .user-text {
@@ -204,9 +206,49 @@
204
  box-shadow: 0 2px 8px rgba(227, 24, 55, 0.15);
205
  }
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  /* React Markdown Styles */
208
  .markdown-body {
209
  color: #374151;
 
210
  }
211
 
212
  .markdown-body p {
 
184
  }
185
 
186
  .message-content {
187
+ width: 100%;
188
+ max-width: 100%;
189
  overflow-wrap: break-word;
190
  font-size: 16px;
191
  line-height: 1.6;
 
194
  .message-row.user .message-content {
195
  display: flex;
196
  justify-content: flex-end;
197
+ max-width: 80%;
198
  }
199
 
200
  .user-text {
 
206
  box-shadow: 0 2px 8px rgba(227, 24, 55, 0.15);
207
  }
208
 
209
+ /* Bot message wrapper for copy button positioning */
210
+ .bot-message-wrapper {
211
+ position: relative;
212
+ width: 100%;
213
+ }
214
+
215
+ .copy-btn {
216
+ background-color: transparent;
217
+ color: #9ca3af; /* Gray icon */
218
+ margin-top: 12px;
219
+ margin-left: -8px; /* Alignment with text */
220
+ border: none;
221
+ cursor: pointer;
222
+ padding: 8px;
223
+ display: flex;
224
+ align-items: center;
225
+ justify-content: center;
226
+ transition: all 0.2s ease;
227
+ border-radius: 50%; /* Circular */
228
+ opacity: 0; /* Hidden by default */
229
+ width: 36px;
230
+ height: 36px;
231
+ }
232
+
233
+ /* Always show icon for the latest response */
234
+ .bot-message-wrapper.latest .copy-btn {
235
+ opacity: 1;
236
+ }
237
+
238
+ /* Show icon on hover for all responses */
239
+ .bot-message-wrapper:hover .copy-btn {
240
+ opacity: 1;
241
+ }
242
+
243
+ .copy-btn:hover {
244
+ background-color: #f3f4f6; /* Light circular hover */
245
+ color: #374151; /* Darker on hover */
246
+ }
247
+
248
  /* React Markdown Styles */
249
  .markdown-body {
250
  color: #374151;
251
+ width: 100%;
252
  }
253
 
254
  .markdown-body p {
FrontEnd/src/ChatBot.jsx CHANGED
@@ -1,5 +1,6 @@
1
  import { useState, useEffect, useRef } from "react";
2
  import { FaUser, FaRobot, FaPaperPlane, FaCircle, FaPlus, FaPizzaSlice } from "react-icons/fa";
 
3
  import ReactMarkdown from "react-markdown";
4
  import "./ChatBot.css";
5
 
@@ -9,6 +10,7 @@ const ChatBot = () => {
9
  const [messages, setMessages] = useState([]);
10
  const [userInput, setUserInput] = useState("");
11
  const [isLoading, setIsLoading] = useState(false);
 
12
  const chatWindowRef = useRef(null);
13
  const textareaRef = useRef(null);
14
 
@@ -115,14 +117,20 @@ const ChatBot = () => {
115
  }
116
  };
117
 
 
 
 
 
 
 
 
118
  return (
119
  <div className="chatbot-container">
120
  <div className="chat-header">
121
  <div className="header-logo" onClick={handleNewChat} title="Quay về trang chủ">
122
  <img src="/pizzahut.png" alt="Pizza Hut Logo" width={50} height={50} />
123
  <div className="header-title">
124
- <h1>HutMind - JRG Chatbot</h1>
125
- <span>Powered by Tan Mai</span>
126
  </div>
127
  </div>
128
  <button className="new-chat-btn" onClick={handleNewChat} title="Cuộc trò chuyện mới">
@@ -162,28 +170,45 @@ const ChatBot = () => {
162
  )}
163
 
164
  <div className="messages-list">
165
- {messages.map((message, index) => (
166
- <div
167
- key={index}
168
- className={`message-row ${message.sender}`}
169
- >
170
- <div className="message-content">
171
- {message.sender === "bot" && message.text === "..." ? (
172
- <div className="typing-indicator">
173
- <FaCircle className="dot dot1" />
174
- <FaCircle className="dot dot2" />
175
- <FaCircle className="dot dot3" />
176
- </div>
177
- ) : message.sender === "bot" ? (
178
- <div className="markdown-body">
179
- <ReactMarkdown>{message.text}</ReactMarkdown>
180
- </div>
181
- ) : (
182
- <div className="user-text">{message.text}</div>
183
- )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  </div>
185
- </div>
186
- ))}
187
  </div>
188
  </div>
189
 
@@ -213,7 +238,7 @@ const ChatBot = () => {
213
  </button>
214
  </div>
215
  <div className="footer-text">
216
- Pizza Hut Assistant có thể mắc lỗi. Vui lòng kiểm tra lại các thông tin quan trọng.
217
  </div>
218
  </div>
219
  </div>
 
1
  import { useState, useEffect, useRef } from "react";
2
  import { FaUser, FaRobot, FaPaperPlane, FaCircle, FaPlus, FaPizzaSlice } from "react-icons/fa";
3
+ import { MdOutlineContentCopy, MdCheck } from "react-icons/md";
4
  import ReactMarkdown from "react-markdown";
5
  import "./ChatBot.css";
6
 
 
10
  const [messages, setMessages] = useState([]);
11
  const [userInput, setUserInput] = useState("");
12
  const [isLoading, setIsLoading] = useState(false);
13
+ const [copiedIndex, setCopiedIndex] = useState(null);
14
  const chatWindowRef = useRef(null);
15
  const textareaRef = useRef(null);
16
 
 
117
  }
118
  };
119
 
120
+ const copyToClipboard = (text, index) => {
121
+ navigator.clipboard.writeText(text).then(() => {
122
+ setCopiedIndex(index);
123
+ setTimeout(() => setCopiedIndex(null), 2000);
124
+ });
125
+ };
126
+
127
  return (
128
  <div className="chatbot-container">
129
  <div className="chat-header">
130
  <div className="header-logo" onClick={handleNewChat} title="Quay về trang chủ">
131
  <img src="/pizzahut.png" alt="Pizza Hut Logo" width={50} height={50} />
132
  <div className="header-title">
133
+ <h1>HutMind</h1>
 
134
  </div>
135
  </div>
136
  <button className="new-chat-btn" onClick={handleNewChat} title="Cuộc trò chuyện mới">
 
170
  )}
171
 
172
  <div className="messages-list">
173
+ {messages.map((message, index) => {
174
+ const lastBotIndex = [...messages].reverse().findIndex(m => m.sender === 'bot');
175
+ const actualLastBotIndex = lastBotIndex !== -1 ? messages.length - 1 - lastBotIndex : -1;
176
+ const isLatestBotMessage = index === actualLastBotIndex;
177
+
178
+ return (
179
+ <div
180
+ key={index}
181
+ className={`message-row ${message.sender}`}
182
+ >
183
+ <div className="message-content">
184
+ {message.sender === "bot" && message.text === "..." ? (
185
+ <div className="typing-indicator">
186
+ <FaCircle className="dot dot1" />
187
+ <FaCircle className="dot dot2" />
188
+ <FaCircle className="dot dot3" />
189
+ </div>
190
+ ) : message.sender === "bot" ? (
191
+ <div className={`bot-message-wrapper ${isLatestBotMessage ? 'latest' : ''}`}>
192
+ <div className="markdown-body">
193
+ <ReactMarkdown>{message.text}</ReactMarkdown>
194
+ </div>
195
+ {message.text !== "..." && (
196
+ <button
197
+ className="copy-btn"
198
+ onClick={() => copyToClipboard(message.text, index)}
199
+ title="Sao chép"
200
+ >
201
+ {copiedIndex === index ? <MdCheck size={18} /> : <MdOutlineContentCopy size={18} />}
202
+ </button>
203
+ )}
204
+ </div>
205
+ ) : (
206
+ <div className="user-text">{message.text}</div>
207
+ )}
208
+ </div>
209
  </div>
210
+ );
211
+ })}
212
  </div>
213
  </div>
214
 
 
238
  </button>
239
  </div>
240
  <div className="footer-text">
241
+ HutMind có thể mắc lỗi. Vui lòng kiểm tra lại các thông tin quan trọng.
242
  </div>
243
  </div>
244
  </div>