Girish Jeswani commited on
Commit
772490a
·
1 Parent(s): 61fb852

add new component and styles

Browse files
phd-advisor-frontend/src/pages/ChatPage.js CHANGED
@@ -1,7 +1,7 @@
1
  // src/pages/ChatPage.js
2
  import React, { useState, useEffect, useRef } from 'react';
3
- import { Home, MessageCircle, Reply, X, Sparkles, Users, Settings2 } from 'lucide-react';
4
- import ChatInput from '../components/ChatInput';
5
  import MessageBubble from '../components/MessageBubble';
6
  import ThinkingIndicator from '../components/ThinkingIndicator';
7
  import SuggestionsPanel from '../components/SuggestionsPanel';
@@ -10,6 +10,7 @@ import ProviderDropdown from '../components/ProviderDropdown';
10
  import { advisors, getAdvisorColors } from '../data/advisors';
11
  import { useTheme } from '../contexts/ThemeContext';
12
  import '../styles/ChatPage.css';
 
13
 
14
  const ChatPage = ({ onNavigateToHome }) => {
15
  const [messages, setMessages] = useState([]);
@@ -19,6 +20,7 @@ const ChatPage = ({ onNavigateToHome }) => {
19
  const [replyingTo, setReplyingTo] = useState(null);
20
  const [currentProvider, setCurrentProvider] = useState('gemini');
21
  const [isProviderSwitching, setIsProviderSwitching] = useState(false);
 
22
  const messagesEndRef = useRef(null);
23
  const { isDark } = useTheme();
24
 
@@ -102,6 +104,39 @@ const ChatPage = ({ onNavigateToHome }) => {
102
  return Date.now().toString() + Math.random().toString(36).substr(2, 9);
103
  };
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  const handleSendMessage = async (inputMessage) => {
106
  if (replyingTo) {
107
  await handleReplyToAdvisor(inputMessage, replyingTo);
@@ -199,21 +234,17 @@ const ChatPage = ({ onNavigateToHome }) => {
199
  };
200
 
201
  const handleReplyToAdvisor = async (inputMessage, replyInfo) => {
202
- const userReplyMessage = {
203
  id: generateMessageId(),
204
  type: 'user',
205
  content: inputMessage,
206
- replyTo: {
207
- advisorId: replyInfo.advisorId,
208
- advisorName: replyInfo.advisorName,
209
- messageId: replyInfo.messageId
210
- },
211
- timestamp: new Date()
212
  };
213
- setMessages(prev => [...prev, userReplyMessage]);
214
-
215
- setThinkingAdvisors([replyInfo.advisorId]);
216
  setIsLoading(true);
 
217
 
218
  try {
219
  const response = await fetch('http://localhost:8000/reply-to-advisor', {
@@ -233,24 +264,34 @@ const ChatPage = ({ onNavigateToHome }) => {
233
  }
234
 
235
  const data = await response.json();
 
236
 
237
- const advisorReplyMessage = {
238
- id: generateMessageId(),
239
- type: 'advisor',
240
- advisorId: replyInfo.advisorId,
241
- content: data.response,
242
- isReply: true,
243
- timestamp: new Date()
244
- };
245
-
246
- setMessages(prev => [...prev, advisorReplyMessage]);
 
 
 
 
 
 
 
 
 
247
 
248
  } catch (error) {
249
  console.error('Error sending reply:', error);
250
  const errorMessage = {
251
  id: generateMessageId(),
252
  type: 'error',
253
- content: 'Sorry, I encountered an error with your reply. Please try again.',
254
  timestamp: new Date()
255
  };
256
  setMessages(prev => [...prev, errorMessage]);
@@ -322,6 +363,14 @@ const ChatPage = ({ onNavigateToHome }) => {
322
  </div>
323
 
324
  <div className="header-controls">
 
 
 
 
 
 
 
 
325
  <ProviderDropdown
326
  currentProvider={currentProvider}
327
  onProviderChange={handleProviderSwitch}
@@ -378,6 +427,17 @@ const ChatPage = ({ onNavigateToHome }) => {
378
  </div>
379
  );
380
  }
 
 
 
 
 
 
 
 
 
 
 
381
 
382
  return (
383
  <MessageBubble
@@ -417,7 +477,6 @@ const ChatPage = ({ onNavigateToHome }) => {
417
  )}
418
  </div>
419
 
420
- {/* Floating Input Area */}
421
  <div className="floating-input-area">
422
  {replyingTo && (
423
  <div className="reply-banner">
@@ -431,17 +490,17 @@ const ChatPage = ({ onNavigateToHome }) => {
431
  </div>
432
  )}
433
 
434
- <div className="input-wrapper">
435
- <ChatInput
436
- onSendMessage={handleSendMessage}
437
- isLoading={isLoading}
438
- placeholder={
439
- replyingTo
440
- ? `Reply to ${replyingTo.advisorName}...`
441
- : "Ask your advisors anything about your PhD journey..."
442
- }
443
- />
444
- </div>
445
  </div>
446
  </div>
447
  );
 
1
  // src/pages/ChatPage.js
2
  import React, { useState, useEffect, useRef } from 'react';
3
+ import { Home, MessageCircle, Reply, X, Sparkles, Users, Settings2, FileText } from 'lucide-react';
4
+ import EnhancedChatInput from '../components/EnhancedChatInput';
5
  import MessageBubble from '../components/MessageBubble';
6
  import ThinkingIndicator from '../components/ThinkingIndicator';
7
  import SuggestionsPanel from '../components/SuggestionsPanel';
 
10
  import { advisors, getAdvisorColors } from '../data/advisors';
11
  import { useTheme } from '../contexts/ThemeContext';
12
  import '../styles/ChatPage.css';
13
+ import '../styles/EnhancedChatInput.css';
14
 
15
  const ChatPage = ({ onNavigateToHome }) => {
16
  const [messages, setMessages] = useState([]);
 
20
  const [replyingTo, setReplyingTo] = useState(null);
21
  const [currentProvider, setCurrentProvider] = useState('gemini');
22
  const [isProviderSwitching, setIsProviderSwitching] = useState(false);
23
+ const [uploadedDocuments, setUploadedDocuments] = useState([]);
24
  const messagesEndRef = useRef(null);
25
  const { isDark } = useTheme();
26
 
 
104
  return Date.now().toString() + Math.random().toString(36).substr(2, 9);
105
  };
106
 
107
+ const handleFileUploaded = (file, response) => {
108
+ // Add the uploaded document to our list
109
+ const docInfo = {
110
+ id: generateMessageId(),
111
+ name: file.name,
112
+ size: file.size,
113
+ type: file.type,
114
+ uploadTime: new Date()
115
+ };
116
+ setUploadedDocuments(prev => [...prev, docInfo]);
117
+
118
+ // Add a system message about the upload
119
+ const uploadMessage = {
120
+ id: generateMessageId(),
121
+ type: 'document_upload',
122
+ content: `📄 Successfully uploaded "${file.name}". Your advisors can now reference this document in their responses.`,
123
+ timestamp: new Date(),
124
+ documentInfo: docInfo
125
+ };
126
+
127
+ // Force update messages state
128
+ setMessages(prev => {
129
+ const newMessages = [...prev, uploadMessage];
130
+ console.log('Added upload message:', uploadMessage); // Debug log
131
+ return newMessages;
132
+ });
133
+
134
+ // Scroll to bottom to show the new message
135
+ setTimeout(() => {
136
+ scrollToBottom();
137
+ }, 100);
138
+ };
139
+
140
  const handleSendMessage = async (inputMessage) => {
141
  if (replyingTo) {
142
  await handleReplyToAdvisor(inputMessage, replyingTo);
 
234
  };
235
 
236
  const handleReplyToAdvisor = async (inputMessage, replyInfo) => {
237
+ const userMessage = {
238
  id: generateMessageId(),
239
  type: 'user',
240
  content: inputMessage,
241
+ timestamp: new Date(),
242
+ replyingTo: replyInfo
 
 
 
 
243
  };
244
+ setMessages(prev => [...prev, userMessage]);
245
+
 
246
  setIsLoading(true);
247
+ setThinkingAdvisors([replyInfo.advisorId]);
248
 
249
  try {
250
  const response = await fetch('http://localhost:8000/reply-to-advisor', {
 
264
  }
265
 
266
  const data = await response.json();
267
+ setThinkingAdvisors([]);
268
 
269
+ if (data.type === 'advisor_reply') {
270
+ const replyMessage = {
271
+ id: generateMessageId(),
272
+ type: 'advisor',
273
+ advisorId: replyInfo.advisorId,
274
+ content: data.response,
275
+ timestamp: new Date(),
276
+ isReply: true
277
+ };
278
+ setMessages(prev => [...prev, replyMessage]);
279
+ } else if (data.type === 'error') {
280
+ const errorMessage = {
281
+ id: generateMessageId(),
282
+ type: 'error',
283
+ content: data.response,
284
+ timestamp: new Date()
285
+ };
286
+ setMessages(prev => [...prev, errorMessage]);
287
+ }
288
 
289
  } catch (error) {
290
  console.error('Error sending reply:', error);
291
  const errorMessage = {
292
  id: generateMessageId(),
293
  type: 'error',
294
+ content: 'Sorry, I encountered an error. Please try again.',
295
  timestamp: new Date()
296
  };
297
  setMessages(prev => [...prev, errorMessage]);
 
363
  </div>
364
 
365
  <div className="header-controls">
366
+ {/* Document Count Indicator */}
367
+ {uploadedDocuments.length > 0 && (
368
+ <div className="document-indicator" title={`${uploadedDocuments.length} document(s) uploaded`}>
369
+ <FileText size={16} />
370
+ <span className="doc-count">{uploadedDocuments.length}</span>
371
+ </div>
372
+ )}
373
+
374
  <ProviderDropdown
375
  currentProvider={currentProvider}
376
  onProviderChange={handleProviderSwitch}
 
427
  </div>
428
  );
429
  }
430
+
431
+ if (message.type === 'document_upload') {
432
+ return (
433
+ <div key={message.id || index} className="document-upload-notification">
434
+ <div className="upload-notification-content">
435
+ <FileText size={16} className="upload-icon" />
436
+ <span>{message.content}</span>
437
+ </div>
438
+ </div>
439
+ );
440
+ }
441
 
442
  return (
443
  <MessageBubble
 
477
  )}
478
  </div>
479
 
 
480
  <div className="floating-input-area">
481
  {replyingTo && (
482
  <div className="reply-banner">
 
490
  </div>
491
  )}
492
 
493
+ <EnhancedChatInput
494
+ onSendMessage={handleSendMessage}
495
+ onFileUploaded={handleFileUploaded}
496
+ uploadedDocuments={uploadedDocuments}
497
+ isLoading={isLoading}
498
+ placeholder={
499
+ replyingTo
500
+ ? `Reply to ${replyingTo.advisorName}...`
501
+ : "Ask your advisors anything about your PhD journey..."
502
+ }
503
+ />
504
  </div>
505
  </div>
506
  );
phd-advisor-frontend/src/styles/ChatPage.css CHANGED
@@ -1,4 +1,3 @@
1
- /* ChatPage.css - Modern Chat Interface Styles */
2
 
3
  /* Modern Chat Page Layout */
4
  .modern-chat-page {
@@ -95,6 +94,12 @@
95
  gap: 20px;
96
  }
97
 
 
 
 
 
 
 
98
  /* Advisor Pills */
99
  .advisor-pills {
100
  display: flex;
@@ -113,42 +118,30 @@
113
  font-weight: 500;
114
  transition: all 0.3s ease;
115
  position: relative;
116
- overflow: hidden;
117
  }
118
 
119
  .advisor-pill.thinking {
120
- animation: pulse 2s infinite;
121
- }
122
-
123
- .advisor-pill span {
124
- font-weight: 600;
125
  }
126
 
127
  .thinking-pulse {
128
  position: absolute;
129
- right: 6px;
130
- top: 50%;
131
- transform: translateY(-50%);
132
- width: 6px;
133
- height: 6px;
134
  border-radius: 50%;
135
- animation: pulse-dot 1.5s infinite;
136
  }
137
 
138
  @keyframes pulse {
139
- 0%, 100% { transform: scale(1); }
140
- 50% { transform: scale(1.05); }
141
- }
142
-
143
- @keyframes pulse-dot {
144
  0%, 100% { opacity: 1; }
145
- 50% { opacity: 0.3; }
146
  }
147
 
148
- .header-controls {
149
- display: flex;
150
- align-items: center;
151
- gap: 12px;
152
  }
153
 
154
  /* Main Content Area */
@@ -156,25 +149,24 @@
156
  flex: 1;
157
  display: flex;
158
  flex-direction: column;
159
- padding: 32px 24px 120px 24px;
160
- max-width: 1200px;
161
- margin: 0 auto;
162
- width: 100%;
163
  }
164
 
165
- /* Welcome State - Simplified */
166
  .welcome-state {
 
167
  display: flex;
168
- flex-direction: column;
169
  align-items: center;
170
  justify-content: center;
171
- max-width: 900px;
172
- margin: 0 auto;
173
  min-height: 60vh;
174
  }
175
 
176
  .suggestions-container {
177
  width: 100%;
 
178
  }
179
 
180
  /* Chat State */
@@ -186,42 +178,33 @@
186
 
187
  .messages-area {
188
  flex: 1;
189
- background: var(--bg-primary);
190
- border-radius: 24px;
191
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
192
- border: 1px solid var(--border-primary);
193
  overflow: hidden;
194
- max-width: 900px;
195
- margin: 0 auto;
196
- width: 100%;
197
  }
198
 
199
  .messages-scroll {
200
  height: 100%;
201
  overflow-y: auto;
202
- padding: 32px;
203
- display: flex;
204
- flex-direction: column;
205
- gap: 24px;
206
- scroll-behavior: smooth;
207
  }
208
 
209
- /* Message Styles */
210
  .orchestrator-message {
211
  display: flex;
212
  gap: 16px;
 
213
  align-items: flex-start;
214
  }
215
 
216
  .orchestrator-avatar {
217
- width: 44px;
218
- height: 44px;
219
- background: var(--bg-tertiary);
220
- border-radius: 12px;
221
  display: flex;
222
  align-items: center;
223
  justify-content: center;
224
- color: var(--text-secondary);
225
  flex-shrink: 0;
226
  }
227
 
@@ -229,15 +212,16 @@
229
  flex: 1;
230
  background: var(--bg-secondary);
231
  border-radius: 16px;
232
- padding: 20px;
233
  border: 1px solid var(--border-primary);
 
234
  }
235
 
236
  .orchestrator-header {
237
  display: flex;
238
  align-items: center;
239
  justify-content: space-between;
240
- margin-bottom: 12px;
241
  }
242
 
243
  .orchestrator-name {
@@ -282,6 +266,7 @@
282
  gap: 16px;
283
  align-items: center;
284
  opacity: 0.8;
 
285
  }
286
 
287
  .thinking-content {
@@ -322,64 +307,15 @@
322
  40% { transform: scale(1); }
323
  }
324
 
325
- /* Floating Input Area */
326
  .floating-input-area {
327
  position: fixed;
328
- bottom: 0;
329
  left: 0;
330
  right: 0;
331
  z-index: 50;
332
- background: linear-gradient(to top, var(--bg-primary), transparent);
333
- padding: 20px 24px 32px 24px;
334
- backdrop-filter: blur(20px);
335
- -webkit-backdrop-filter: blur(20px);
336
- }
337
-
338
- .reply-banner {
339
- display: flex;
340
- align-items: center;
341
- justify-content: space-between;
342
- background: var(--bg-secondary);
343
- border: 1px solid var(--border-primary);
344
- border-radius: 12px;
345
- padding: 12px 16px;
346
- margin-bottom: 12px;
347
- max-width: 900px;
348
- margin-left: auto;
349
- margin-right: auto;
350
- margin-bottom: 12px;
351
- }
352
-
353
- .reply-info {
354
- display: flex;
355
- align-items: center;
356
- gap: 8px;
357
- font-size: 14px;
358
- color: var(--text-secondary);
359
- }
360
-
361
- .reply-info strong {
362
- color: var(--accent-primary);
363
- }
364
-
365
- .cancel-reply {
366
- background: none;
367
- border: none;
368
- color: var(--text-tertiary);
369
- cursor: pointer;
370
- padding: 4px;
371
- border-radius: 6px;
372
- transition: all 0.2s ease;
373
- }
374
-
375
- .cancel-reply:hover {
376
- background: var(--bg-tertiary);
377
- color: var(--text-secondary);
378
- }
379
-
380
- .input-wrapper {
381
- max-width: 900px;
382
- margin: 0 auto;
383
  }
384
 
385
  /* Provider Dropdown Styles */
@@ -579,7 +515,7 @@
579
  }
580
 
581
  .main-content {
582
- padding: 24px 16px 120px 16px;
583
  }
584
 
585
  .welcome-state {
@@ -587,10 +523,21 @@
587
  }
588
 
589
  .messages-scroll {
590
- padding: 20px;
 
591
  }
592
 
593
  .floating-input-area {
594
- padding: 16px 16px 24px 16px;
 
 
 
 
 
 
 
 
 
 
595
  }
596
  }
 
 
1
 
2
  /* Modern Chat Page Layout */
3
  .modern-chat-page {
 
94
  gap: 20px;
95
  }
96
 
97
+ .header-controls {
98
+ display: flex;
99
+ align-items: center;
100
+ gap: 12px;
101
+ }
102
+
103
  /* Advisor Pills */
104
  .advisor-pills {
105
  display: flex;
 
118
  font-weight: 500;
119
  transition: all 0.3s ease;
120
  position: relative;
 
121
  }
122
 
123
  .advisor-pill.thinking {
124
+ animation: pulse 2s ease-in-out infinite;
 
 
 
 
125
  }
126
 
127
  .thinking-pulse {
128
  position: absolute;
129
+ top: -2px;
130
+ right: -2px;
131
+ width: 8px;
132
+ height: 8px;
 
133
  border-radius: 50%;
134
+ animation: pulse-dot 1.5s ease-in-out infinite;
135
  }
136
 
137
  @keyframes pulse {
 
 
 
 
 
138
  0%, 100% { opacity: 1; }
139
+ 50% { opacity: 0.7; }
140
  }
141
 
142
+ @keyframes pulse-dot {
143
+ 0%, 100% { transform: scale(1); opacity: 1; }
144
+ 50% { transform: scale(1.2); opacity: 0.8; }
 
145
  }
146
 
147
  /* Main Content Area */
 
149
  flex: 1;
150
  display: flex;
151
  flex-direction: column;
152
+ padding-top: 24px;
153
+ padding-bottom: 140px; /* Space for floating input */
154
+ padding-left: 16px;
155
+ padding-right: 16px;
156
  }
157
 
158
+ /* Welcome State */
159
  .welcome-state {
160
+ flex: 1;
161
  display: flex;
 
162
  align-items: center;
163
  justify-content: center;
 
 
164
  min-height: 60vh;
165
  }
166
 
167
  .suggestions-container {
168
  width: 100%;
169
+ max-width: 1200px;
170
  }
171
 
172
  /* Chat State */
 
178
 
179
  .messages-area {
180
  flex: 1;
 
 
 
 
181
  overflow: hidden;
 
 
 
182
  }
183
 
184
  .messages-scroll {
185
  height: 100%;
186
  overflow-y: auto;
187
+ padding: 20px;
188
+ padding-bottom: 40px; /* Extra space before floating input */
 
 
 
189
  }
190
 
191
+ /* Message Components */
192
  .orchestrator-message {
193
  display: flex;
194
  gap: 16px;
195
+ margin-bottom: 24px;
196
  align-items: flex-start;
197
  }
198
 
199
  .orchestrator-avatar {
200
+ width: 40px;
201
+ height: 40px;
202
+ border-radius: 20px;
203
+ background: var(--accent-gradient);
204
  display: flex;
205
  align-items: center;
206
  justify-content: center;
207
+ color: white;
208
  flex-shrink: 0;
209
  }
210
 
 
212
  flex: 1;
213
  background: var(--bg-secondary);
214
  border-radius: 16px;
215
+ padding: 16px 20px;
216
  border: 1px solid var(--border-primary);
217
+ max-width: 600px;
218
  }
219
 
220
  .orchestrator-header {
221
  display: flex;
222
  align-items: center;
223
  justify-content: space-between;
224
+ margin-bottom: 8px;
225
  }
226
 
227
  .orchestrator-name {
 
266
  gap: 16px;
267
  align-items: center;
268
  opacity: 0.8;
269
+ margin-bottom: 24px;
270
  }
271
 
272
  .thinking-content {
 
307
  40% { transform: scale(1); }
308
  }
309
 
310
+ /* Floating Input Area - New Style */
311
  .floating-input-area {
312
  position: fixed;
313
+ bottom: 20px;
314
  left: 0;
315
  right: 0;
316
  z-index: 50;
317
+ background: transparent; /* No background or blur */
318
+ padding: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  }
320
 
321
  /* Provider Dropdown Styles */
 
515
  }
516
 
517
  .main-content {
518
+ padding: 24px 12px 120px 12px; /* Adjusted for mobile */
519
  }
520
 
521
  .welcome-state {
 
523
  }
524
 
525
  .messages-scroll {
526
+ padding: 16px;
527
+ padding-bottom: 30px;
528
  }
529
 
530
  .floating-input-area {
531
+ bottom: 10px;
532
+ }
533
+
534
+ .orchestrator-content {
535
+ max-width: none;
536
+ }
537
+
538
+ .system-content {
539
+ max-width: 90%;
540
+ padding: 10px 16px;
541
+ font-size: 13px;
542
  }
543
  }