SarahXia0405 commited on
Commit
f9b75bc
·
verified ·
1 Parent(s): a720e8f

Update web/src/components/ChatArea.tsx

Browse files
Files changed (1) hide show
  1. web/src/components/ChatArea.tsx +63 -42
web/src/components/ChatArea.tsx CHANGED
@@ -1,10 +1,11 @@
1
- // web/src/components/ChatArea.tsx
2
  import React, { useState, useRef, useEffect } from 'react';
3
  import { Button } from './ui/button';
4
  import { Textarea } from './ui/textarea';
5
- import { Send, ArrowDown, Trash2, Share2 } from 'lucide-react';
6
  import { Message } from './Message';
7
  import { FileUploadArea } from './FileUploadArea';
 
 
8
  import type { Message as MessageType, LearningMode, UploadedFile, FileType, SpaceType } from '../App';
9
  import { toast } from 'sonner';
10
  import {
@@ -15,13 +16,7 @@ import {
15
  } from './ui/dropdown-menu';
16
  import clareAvatar from '../assets/dfe44dab3ad8cd93953eac4a3e68bd1a5f999653.png';
17
 
18
- // ✅ NEW: ApiUser type for feedback submission
19
- import type { User as ApiUser } from '../lib/api';
20
-
21
  interface ChatAreaProps {
22
- // ✅ NEW: user passed from App (ApiUser)
23
- user?: ApiUser | null;
24
-
25
  messages: MessageType[];
26
  onSendMessage: (content: string) => void;
27
  uploadedFiles: UploadedFile[];
@@ -37,7 +32,6 @@ interface ChatAreaProps {
37
  }
38
 
39
  export function ChatArea({
40
- user = null,
41
  messages,
42
  onSendMessage,
43
  uploadedFiles,
@@ -121,22 +115,17 @@ export function ChatArea({
121
  return;
122
  }
123
 
 
124
  const conversationText = messages
125
- .map((msg) => `${msg.role === 'user' ? (msg.sender?.name ?? 'You') : 'Clare'}: ${msg.content}`)
126
  .join('\n\n');
127
 
128
- navigator.clipboard
129
- .writeText(conversationText)
130
- .then(() => toast.success('Conversation copied to clipboard!'))
131
- .catch(() => toast.error('Failed to copy conversation'));
132
- };
133
-
134
- // ✅ helper: find previous user message for an assistant message at index i
135
- const getPrevUserText = (i: number) => {
136
- for (let j = i - 1; j >= 0; j--) {
137
- if (messages[j]?.role === 'user') return messages[j].content || '';
138
- }
139
- return '';
140
  };
141
 
142
  return (
@@ -183,6 +172,7 @@ export function ChatArea({
183
  const isAtTop = scrollTop === 0;
184
  const isAtBottom = scrollTop + clientHeight >= scrollHeight - 1;
185
 
 
186
  if (isScrollable && ((isAtTop && e.deltaY < 0) || (isAtBottom && e.deltaY > 0))) {
187
  e.preventDefault();
188
  }
@@ -192,16 +182,11 @@ export function ChatArea({
192
  }}
193
  >
194
  <div className="max-w-4xl mx-auto space-y-6">
195
- {messages.map((message, idx) => (
196
  <Message
197
  key={message.id}
198
  message={message}
199
  showSenderInfo={spaceType === 'group'}
200
- // ✅ critical: pass user to Message so feedback can submit
201
- user={user ?? null}
202
- learningMode={learningMode}
203
- docType={'Syllabus'}
204
- getContextUserText={() => getPrevUserText(idx)}
205
  />
206
  ))}
207
 
@@ -224,7 +209,7 @@ export function ChatArea({
224
  </div>
225
  </div>
226
 
227
- {/* Scroll to Bottom Button */}
228
  {showScrollButton && (
229
  <div className="absolute bottom-24 left-1/2 -translate-x-1/2 z-20">
230
  <Button
@@ -243,6 +228,7 @@ export function ChatArea({
243
  <div className="max-w-4xl mx-auto px-4 py-4">
244
  <form onSubmit={handleSubmit}>
245
  <div className="relative">
 
246
  <DropdownMenu>
247
  <DropdownMenuTrigger asChild>
248
  <Button
@@ -253,40 +239,75 @@ export function ChatArea({
253
  type="button"
254
  >
255
  <span>{modeLabels[learningMode]}</span>
256
- <svg className="h-3 w-3 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
257
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
 
 
 
 
 
 
 
 
 
 
258
  </svg>
259
  </Button>
260
  </DropdownMenuTrigger>
261
  <DropdownMenuContent align="start" className="w-56">
262
- <DropdownMenuItem onClick={() => onLearningModeChange('concept')} className={learningMode === 'concept' ? 'bg-accent' : ''}>
 
 
 
263
  <div className="flex flex-col">
264
  <span className="font-medium">Concept Explainer</span>
265
- <span className="text-xs text-muted-foreground">Get detailed explanations of concepts</span>
 
 
266
  </div>
267
  </DropdownMenuItem>
268
- <DropdownMenuItem onClick={() => onLearningModeChange('socratic')} className={learningMode === 'socratic' ? 'bg-accent' : ''}>
 
 
 
269
  <div className="flex flex-col">
270
  <span className="font-medium">Socratic Tutor</span>
271
- <span className="text-xs text-muted-foreground">Learn through guided questions</span>
 
 
272
  </div>
273
  </DropdownMenuItem>
274
- <DropdownMenuItem onClick={() => onLearningModeChange('exam')} className={learningMode === 'exam' ? 'bg-accent' : ''}>
 
 
 
275
  <div className="flex flex-col">
276
  <span className="font-medium">Exam Prep</span>
277
- <span className="text-xs text-muted-foreground">Practice with quiz questions</span>
 
 
278
  </div>
279
  </DropdownMenuItem>
280
- <DropdownMenuItem onClick={() => onLearningModeChange('assignment')} className={learningMode === 'assignment' ? 'bg-accent' : ''}>
 
 
 
281
  <div className="flex flex-col">
282
  <span className="font-medium">Assignment Helper</span>
283
- <span className="text-xs text-muted-foreground">Get help with assignments</span>
 
 
284
  </div>
285
  </DropdownMenuItem>
286
- <DropdownMenuItem onClick={() => onLearningModeChange('summary')} className={learningMode === 'summary' ? 'bg-accent' : ''}>
 
 
 
287
  <div className="flex flex-col">
288
  <span className="font-medium">Quick Summary</span>
289
- <span className="text-xs text-muted-foreground">Get concise summaries</span>
 
 
290
  </div>
291
  </DropdownMenuItem>
292
  </DropdownMenuContent>
@@ -334,4 +355,4 @@ export function ChatArea({
334
  </div>
335
  </div>
336
  );
337
- }
 
 
1
  import React, { useState, useRef, useEffect } from 'react';
2
  import { Button } from './ui/button';
3
  import { Textarea } from './ui/textarea';
4
+ import { Send, ArrowDown, AlertCircle, Trash2, Share2 } from 'lucide-react';
5
  import { Message } from './Message';
6
  import { FileUploadArea } from './FileUploadArea';
7
+ import { Alert, AlertDescription } from './ui/alert';
8
+ import { Badge } from './ui/badge';
9
  import type { Message as MessageType, LearningMode, UploadedFile, FileType, SpaceType } from '../App';
10
  import { toast } from 'sonner';
11
  import {
 
16
  } from './ui/dropdown-menu';
17
  import clareAvatar from '../assets/dfe44dab3ad8cd93953eac4a3e68bd1a5f999653.png';
18
 
 
 
 
19
  interface ChatAreaProps {
 
 
 
20
  messages: MessageType[];
21
  onSendMessage: (content: string) => void;
22
  uploadedFiles: UploadedFile[];
 
32
  }
33
 
34
  export function ChatArea({
 
35
  messages,
36
  onSendMessage,
37
  uploadedFiles,
 
115
  return;
116
  }
117
 
118
+ // Create a shareable text version of the conversation
119
  const conversationText = messages
120
+ .map(msg => `${msg.role === 'user' ? (msg.sender?.name ?? 'You') : 'Clare'}: ${msg.content}`)
121
  .join('\n\n');
122
 
123
+ // Copy to clipboard
124
+ navigator.clipboard.writeText(conversationText).then(() => {
125
+ toast.success('Conversation copied to clipboard!');
126
+ }).catch(() => {
127
+ toast.error('Failed to copy conversation');
128
+ });
 
 
 
 
 
 
129
  };
130
 
131
  return (
 
172
  const isAtTop = scrollTop === 0;
173
  const isAtBottom = scrollTop + clientHeight >= scrollHeight - 1;
174
 
175
+ // If scrolling up at top or down at bottom, prevent default to stop propagation
176
  if (isScrollable && ((isAtTop && e.deltaY < 0) || (isAtBottom && e.deltaY > 0))) {
177
  e.preventDefault();
178
  }
 
182
  }}
183
  >
184
  <div className="max-w-4xl mx-auto space-y-6">
185
+ {messages.map((message) => (
186
  <Message
187
  key={message.id}
188
  message={message}
189
  showSenderInfo={spaceType === 'group'}
 
 
 
 
 
190
  />
191
  ))}
192
 
 
209
  </div>
210
  </div>
211
 
212
+ {/* Scroll to Bottom Button - Floating above input */}
213
  {showScrollButton && (
214
  <div className="absolute bottom-24 left-1/2 -translate-x-1/2 z-20">
215
  <Button
 
228
  <div className="max-w-4xl mx-auto px-4 py-4">
229
  <form onSubmit={handleSubmit}>
230
  <div className="relative">
231
+ {/* Mode Selector - ChatGPT style at bottom left */}
232
  <DropdownMenu>
233
  <DropdownMenuTrigger asChild>
234
  <Button
 
239
  type="button"
240
  >
241
  <span>{modeLabels[learningMode]}</span>
242
+ <svg
243
+ className="h-3 w-3 opacity-50"
244
+ fill="none"
245
+ stroke="currentColor"
246
+ viewBox="0 0 24 24"
247
+ >
248
+ <path
249
+ strokeLinecap="round"
250
+ strokeLinejoin="round"
251
+ strokeWidth={2}
252
+ d="M19 9l-7 7-7-7"
253
+ />
254
  </svg>
255
  </Button>
256
  </DropdownMenuTrigger>
257
  <DropdownMenuContent align="start" className="w-56">
258
+ <DropdownMenuItem
259
+ onClick={() => onLearningModeChange('concept')}
260
+ className={learningMode === 'concept' ? 'bg-accent' : ''}
261
+ >
262
  <div className="flex flex-col">
263
  <span className="font-medium">Concept Explainer</span>
264
+ <span className="text-xs text-muted-foreground">
265
+ Get detailed explanations of concepts
266
+ </span>
267
  </div>
268
  </DropdownMenuItem>
269
+ <DropdownMenuItem
270
+ onClick={() => onLearningModeChange('socratic')}
271
+ className={learningMode === 'socratic' ? 'bg-accent' : ''}
272
+ >
273
  <div className="flex flex-col">
274
  <span className="font-medium">Socratic Tutor</span>
275
+ <span className="text-xs text-muted-foreground">
276
+ Learn through guided questions
277
+ </span>
278
  </div>
279
  </DropdownMenuItem>
280
+ <DropdownMenuItem
281
+ onClick={() => onLearningModeChange('exam')}
282
+ className={learningMode === 'exam' ? 'bg-accent' : ''}
283
+ >
284
  <div className="flex flex-col">
285
  <span className="font-medium">Exam Prep</span>
286
+ <span className="text-xs text-muted-foreground">
287
+ Practice with quiz questions
288
+ </span>
289
  </div>
290
  </DropdownMenuItem>
291
+ <DropdownMenuItem
292
+ onClick={() => onLearningModeChange('assignment')}
293
+ className={learningMode === 'assignment' ? 'bg-accent' : ''}
294
+ >
295
  <div className="flex flex-col">
296
  <span className="font-medium">Assignment Helper</span>
297
+ <span className="text-xs text-muted-foreground">
298
+ Get help with assignments
299
+ </span>
300
  </div>
301
  </DropdownMenuItem>
302
+ <DropdownMenuItem
303
+ onClick={() => onLearningModeChange('summary')}
304
+ className={learningMode === 'summary' ? 'bg-accent' : ''}
305
+ >
306
  <div className="flex flex-col">
307
  <span className="font-medium">Quick Summary</span>
308
+ <span className="text-xs text-muted-foreground">
309
+ Get concise summaries
310
+ </span>
311
  </div>
312
  </DropdownMenuItem>
313
  </DropdownMenuContent>
 
355
  </div>
356
  </div>
357
  );
358
+ }