SarahXia0405 commited on
Commit
52fd618
·
verified ·
1 Parent(s): 6fd4b72

Update web/src/components/RightPanel.tsx

Browse files
Files changed (1) hide show
  1. web/src/components/RightPanel.tsx +171 -318
web/src/components/RightPanel.tsx CHANGED
@@ -6,20 +6,9 @@ import { Card } from './ui/card';
6
  import { Separator } from './ui/separator';
7
  import { Textarea } from './ui/textarea';
8
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
9
- import {
10
- LogIn,
11
- LogOut,
12
- Download,
13
- ClipboardList,
14
- FileText,
15
- Sparkles,
16
- ChevronUp,
17
- ChevronDown,
18
- PanelRightClose,
19
- MessageSquare
20
- } from 'lucide-react';
21
  import type { User } from '../App';
22
- import { toast } from 'sonner@2.0.3';
23
  import {
24
  Dialog,
25
  DialogContent,
@@ -32,109 +21,49 @@ import {
32
 
33
  interface RightPanelProps {
34
  user: User | null;
35
- onLogin: (user: User) => void;
36
  onLogout: () => void;
37
  isLoggedIn: boolean;
38
  onClose?: () => void;
 
39
  exportResult: string;
40
  setExportResult: (result: string) => void;
41
  resultType: 'export' | 'quiz' | 'summary' | null;
42
  setResultType: (type: 'export' | 'quiz' | 'summary' | null) => void;
43
  }
44
 
45
- export function RightPanel({ user, onLogin, onLogout, isLoggedIn, onClose, exportResult, setExportResult, resultType, setResultType }: RightPanelProps) {
 
 
 
 
 
 
 
 
 
46
  const [showLoginForm, setShowLoginForm] = useState(false);
47
  const [name, setName] = useState('');
48
  const [email, setEmail] = useState('');
49
- const [isExpanded, setIsExpanded] = useState(true);
50
  const [feedbackDialogOpen, setFeedbackDialogOpen] = useState(false);
51
  const [feedbackText, setFeedbackText] = useState('');
52
  const [feedbackCategory, setFeedbackCategory] = useState<'general' | 'bug' | 'feature'>('general');
53
 
54
- const handleLogin = () => {
55
  if (!name.trim() || !email.trim()) {
56
  toast.error('Please fill in all fields');
57
  return;
58
  }
59
-
60
- onLogin({ name: name.trim(), email: email.trim() });
61
  setShowLoginForm(false);
62
  setName('');
63
  setEmail('');
64
- toast.success(`Welcome, ${name}!`);
65
  };
66
 
67
  const handleLogout = () => {
68
  onLogout();
69
  setShowLoginForm(false);
70
- toast.success('Logged out successfully');
71
- };
72
-
73
- const handleExport = () => {
74
- const result = `# Conversation Export
75
- Date: ${new Date().toLocaleDateString()}
76
- Student: ${user?.name}
77
-
78
- ## Summary
79
- This conversation covered key concepts in Module 10 – Responsible AI, including ethical considerations, fairness, transparency, and accountability in AI systems.
80
-
81
- ## Key Takeaways
82
- 1. Understanding the principles of Responsible AI
83
- 2. Real-world applications and implications
84
- 3. Best practices for ethical AI development
85
-
86
- Exported successfully! ✓`;
87
-
88
- setExportResult(result);
89
- setResultType('export');
90
- toast.success('Conversation exported!');
91
- };
92
-
93
- const handleQuiz = () => {
94
- const quiz = `# Micro-Quiz: Responsible AI
95
-
96
- **Question 1:** Which of the following is a key principle of Responsible AI?
97
- a) Profit maximization
98
- b) Transparency
99
- c) Rapid deployment
100
- d) Cost reduction
101
-
102
- **Question 2:** What is algorithmic fairness?
103
- (Short answer expected)
104
-
105
- **Question 3:** True or False: AI systems should always prioritize accuracy over fairness.
106
-
107
- Generate quiz based on your conversation!`;
108
-
109
- setExportResult(quiz);
110
- setResultType('quiz');
111
- toast.success('Quiz generated!');
112
- };
113
-
114
- const handleSummary = () => {
115
- const summary = `# Learning Summary
116
-
117
- ## Today's Session
118
- **Duration:** 25 minutes
119
- **Topics Covered:** 3
120
- **Messages Exchanged:** 12
121
-
122
- ## Key Concepts Discussed
123
- • Principles of Responsible AI
124
- • Ethical considerations in AI development
125
- • Fairness and transparency in algorithms
126
-
127
- ## Recommended Next Steps
128
- 1. Review Module 10, Section 2.3
129
- 2. Complete practice quiz on algorithmic fairness
130
- 3. Read additional resources on AI ethics
131
-
132
- ## Progress Update
133
- You've covered 65% of Module 10 content. Keep up the great work! 🎉`;
134
-
135
- setExportResult(summary);
136
- setResultType('summary');
137
- toast.success('Summary generated!');
138
  };
139
 
140
  const handleFeedbackSubmit = () => {
@@ -142,8 +71,7 @@ You've covered 65% of Module 10 content. Keep up the great work! 🎉`;
142
  toast.error('Please provide feedback text');
143
  return;
144
  }
145
-
146
- // Here you can add logic to send feedback to your server or handle it as needed
147
  console.log('Feedback submitted:', feedbackText, feedbackCategory);
148
  setFeedbackDialogOpen(false);
149
  setFeedbackText('');
@@ -152,247 +80,172 @@ You've covered 65% of Module 10 content. Keep up the great work! 🎉`;
152
 
153
  return (
154
  <div className="flex-1 overflow-auto p-4 space-y-4">
155
- {isExpanded && (
156
- <>
157
- {/* Login Section */}
158
- <Card className="p-4">
159
- {!isLoggedIn ? (
160
- <div className="space-y-4">
161
- <div className="flex flex-col items-center py-4">
162
- <img
163
- src="https://images.unsplash.com/photo-1588912914049-d2664f76a947?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzdHVkZW50JTIwc3R1ZHlpbmclMjBpbGx1c3RyYXRpb258ZW58MXx8fHwxNzY2MDY2NjcyfDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
164
- alt="Student studying"
165
- className="w-20 h-20 rounded-full object-cover mb-4"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  />
167
- <h3 className="mb-2">Welcome to Clare!</h3>
168
- <p className="text-sm text-muted-foreground text-center mb-4">
169
- Log in to start your personalized learning journey
170
- </p>
171
  </div>
172
-
173
- {!showLoginForm ? (
174
- <Button onClick={() => setShowLoginForm(true)} className="w-full gap-2">
175
- <LogIn className="h-4 w-4" />
176
- Student Login
 
 
 
 
 
 
 
 
 
 
177
  </Button>
178
- ) : (
179
- <div className="space-y-3">
180
- <div className="space-y-2">
181
- <Label htmlFor="name">Name</Label>
182
- <Input
183
- id="name"
184
- value={name}
185
- onChange={(e) => setName(e.target.value)}
186
- placeholder="Enter your name"
187
- />
188
- </div>
189
- <div className="space-y-2">
190
- <Label htmlFor="email">Email / Student ID</Label>
191
- <Input
192
- id="email"
193
- type="email"
194
- value={email}
195
- onChange={(e) => setEmail(e.target.value)}
196
- placeholder="Enter your email or ID"
197
- />
198
- </div>
199
- <div className="flex gap-2">
200
- <Button onClick={handleLogin} className="flex-1">
201
- Enter
202
- </Button>
203
- <Button
204
- variant="outline"
205
- onClick={() => setShowLoginForm(false)}
206
- >
207
- Cancel
208
- </Button>
209
- </div>
210
- </div>
211
- )}
212
- </div>
213
- ) : (
214
- <div className="space-y-4">
215
- <div className="flex items-center gap-3">
216
- <div className="w-12 h-12 rounded-full bg-gradient-to-br from-purple-500 to-blue-500 flex items-center justify-center text-white">
217
- {user.name.charAt(0).toUpperCase()}
218
- </div>
219
- <div className="flex-1 min-w-0">
220
- <h4 className="truncate">{user.name}</h4>
221
- <p className="text-sm text-muted-foreground truncate">{user.email}</p>
222
- </div>
223
  </div>
224
- <Button
225
- variant="destructive"
226
- onClick={handleLogout}
227
- className="w-full gap-2"
228
- >
229
- <LogOut className="h-4 w-4" />
230
- Log Out
231
- </Button>
232
  </div>
233
  )}
234
- </Card>
235
-
236
- <Separator />
237
-
238
- {/* Actions section removed - functionality available via floating buttons */}
239
- {/* <div className="space-y-3">
240
- <h3>Actions</h3>
241
-
242
- <Dialog>
243
- <DialogTrigger asChild>
244
- <Button
245
- variant="outline"
246
- className="w-full justify-start gap-2"
247
- disabled={!isLoggedIn}
248
- onClick={handleExport}
249
- >
250
- <Download className="h-4 w-4" />
251
- Export Conversation
252
- </Button>
253
- </DialogTrigger>
254
- </Dialog>
255
-
256
- <Dialog>
257
- <DialogTrigger asChild>
258
- <Button
259
- variant="outline"
260
- className="w-full justify-start gap-2"
261
- disabled={!isLoggedIn}
262
- onClick={handleQuiz}
263
- >
264
- <ClipboardList className="h-4 w-4" />
265
- Let's Try (Micro-Quiz)
266
- </Button>
267
- </DialogTrigger>
268
- </Dialog>
269
-
270
- <Dialog>
271
- <DialogTrigger asChild>
272
  <Button
273
- variant="outline"
274
- className="w-full justify-start gap-2"
275
- disabled={!isLoggedIn}
276
- onClick={handleSummary}
 
 
277
  >
278
- <Sparkles className="h-4 w-4" />
279
- Summarization
280
  </Button>
281
- </DialogTrigger>
282
- </Dialog>
283
-
284
- {!isLoggedIn && (
285
- <p className="text-xs text-muted-foreground text-center pt-2">
286
- Log in to unlock all features
287
- </p>
288
- )}
289
- </div> */}
290
-
291
- <Separator />
292
-
293
- {/* Results Section */}
294
- <div className="space-y-3">
295
- <h3>
296
- {resultType === 'export' && 'Exported Conversation'}
297
- {resultType === 'quiz' && 'Micro-Quiz'}
298
- {resultType === 'summary' && 'Summarization'}
299
- {!resultType && 'Results'}
300
- </h3>
301
- <Card className="p-4 min-h-[200px] bg-muted/30">
302
- {exportResult ? (
303
- <div className="space-y-3">
304
- <div className="flex items-center justify-between">
305
- <FileText className="h-4 w-4 text-muted-foreground" />
306
- <Button
307
- variant="ghost"
308
- size="sm"
309
- onClick={() => {
310
- navigator.clipboard.writeText(exportResult);
311
- toast.success('Copied to clipboard!');
312
- }}
313
- >
314
- Copy
315
- </Button>
316
- </div>
317
- <div className="text-sm whitespace-pre-wrap text-foreground">
318
- {exportResult}
319
- </div>
320
- </div>
321
- ) : (
322
- <div className="flex items-center justify-center h-full text-sm text-muted-foreground text-left">
323
- Results (export / summary / quiz) will appear here after using the actions above
324
- </div>
325
- )}
326
- </Card>
327
- </div>
328
-
329
- <Separator />
330
-
331
- {/* Feedback Section */}
332
- <div className="space-y-3">
333
- <h3>Feedback</h3>
334
- <Button
335
- variant="outline"
336
- className="w-full justify-start gap-2"
337
- onClick={() => setFeedbackDialogOpen(true)}
338
- >
339
  <MessageSquare className="h-4 w-4" />
340
  Provide Feedback
341
  </Button>
342
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
- {/* Feedback Dialog */}
345
- <Dialog open={feedbackDialogOpen} onOpenChange={setFeedbackDialogOpen}>
346
- <DialogContent className="sm:max-w-[425px]">
347
- <DialogHeader>
348
- <DialogTitle>Provide Feedback</DialogTitle>
349
- <DialogDescription>
350
- Help us improve Clare by sharing your thoughts and suggestions.
351
- </DialogDescription>
352
- </DialogHeader>
353
- <div className="space-y-3">
354
- <div className="space-y-2">
355
- <Label htmlFor="feedbackCategory">Category</Label>
356
- <Select value={feedbackCategory} onValueChange={(value) => setFeedbackCategory(value as 'general' | 'bug' | 'feature')}>
357
- <SelectTrigger>
358
- <SelectValue placeholder="Select a category" />
359
- </SelectTrigger>
360
- <SelectContent>
361
- <SelectItem value="general">General Feedback</SelectItem>
362
- <SelectItem value="bug">Bug Report</SelectItem>
363
- <SelectItem value="feature">Feature Request</SelectItem>
364
- </SelectContent>
365
- </Select>
366
- </div>
367
- <div className="space-y-2">
368
- <Label htmlFor="feedbackText">Feedback</Label>
369
- <Textarea
370
- id="feedbackText"
371
- value={feedbackText}
372
- onChange={(e) => setFeedbackText(e.target.value)}
373
- placeholder="Enter your feedback here..."
374
- className="min-h-[100px]"
375
- />
376
- </div>
377
  </div>
378
- <DialogFooter>
379
- <Button
380
- variant="outline"
381
- onClick={() => setFeedbackDialogOpen(false)}
382
- >
383
- Cancel
384
- </Button>
385
- <Button
386
- variant="primary"
387
- onClick={handleFeedbackSubmit}
388
- >
389
- Submit
390
- </Button>
391
- </DialogFooter>
392
- </DialogContent>
393
- </Dialog>
394
- </>
395
- )}
396
  </div>
397
  );
398
- }
 
6
  import { Separator } from './ui/separator';
7
  import { Textarea } from './ui/textarea';
8
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
9
+ import { LogIn, LogOut, FileText, MessageSquare } from 'lucide-react';
 
 
 
 
 
 
 
 
 
 
 
10
  import type { User } from '../App';
11
+ import { toast } from 'sonner';
12
  import {
13
  Dialog,
14
  DialogContent,
 
21
 
22
  interface RightPanelProps {
23
  user: User | null;
24
+ onLogin: (payload: { name: string; email: string }) => Promise<void> | void;
25
  onLogout: () => void;
26
  isLoggedIn: boolean;
27
  onClose?: () => void;
28
+
29
  exportResult: string;
30
  setExportResult: (result: string) => void;
31
  resultType: 'export' | 'quiz' | 'summary' | null;
32
  setResultType: (type: 'export' | 'quiz' | 'summary' | null) => void;
33
  }
34
 
35
+ export function RightPanel({
36
+ user,
37
+ onLogin,
38
+ onLogout,
39
+ isLoggedIn,
40
+ exportResult,
41
+ setExportResult,
42
+ resultType,
43
+ setResultType,
44
+ }: RightPanelProps) {
45
  const [showLoginForm, setShowLoginForm] = useState(false);
46
  const [name, setName] = useState('');
47
  const [email, setEmail] = useState('');
48
+
49
  const [feedbackDialogOpen, setFeedbackDialogOpen] = useState(false);
50
  const [feedbackText, setFeedbackText] = useState('');
51
  const [feedbackCategory, setFeedbackCategory] = useState<'general' | 'bug' | 'feature'>('general');
52
 
53
+ const handleLoginClick = async () => {
54
  if (!name.trim() || !email.trim()) {
55
  toast.error('Please fill in all fields');
56
  return;
57
  }
58
+ await onLogin({ name: name.trim(), email: email.trim() });
 
59
  setShowLoginForm(false);
60
  setName('');
61
  setEmail('');
 
62
  };
63
 
64
  const handleLogout = () => {
65
  onLogout();
66
  setShowLoginForm(false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  };
68
 
69
  const handleFeedbackSubmit = () => {
 
71
  toast.error('Please provide feedback text');
72
  return;
73
  }
74
+ // MVP: keep console log; next step we can POST to /api/feedback
 
75
  console.log('Feedback submitted:', feedbackText, feedbackCategory);
76
  setFeedbackDialogOpen(false);
77
  setFeedbackText('');
 
80
 
81
  return (
82
  <div className="flex-1 overflow-auto p-4 space-y-4">
83
+ {/* Login Section */}
84
+ <Card className="p-4">
85
+ {!isLoggedIn ? (
86
+ <div className="space-y-4">
87
+ <div className="flex flex-col items-center py-4">
88
+ <img
89
+ src="https://images.unsplash.com/photo-1588912914049-d2664f76a947?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzdHVkZW50JTIwc3R1ZHlpbmclMjBpbGx1c3RyYXRpb258ZW58MXx8fHwxNzY2MDY2NjcyfDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
90
+ alt="Student studying"
91
+ className="w-20 h-20 rounded-full object-cover mb-4"
92
+ />
93
+ <h3 className="mb-2">Welcome to Clare!</h3>
94
+ <p className="text-sm text-muted-foreground text-center mb-4">
95
+ Log in to start your personalized learning journey
96
+ </p>
97
+ </div>
98
+
99
+ {!showLoginForm ? (
100
+ <Button onClick={() => setShowLoginForm(true)} className="w-full gap-2">
101
+ <LogIn className="h-4 w-4" />
102
+ Student Login
103
+ </Button>
104
+ ) : (
105
+ <div className="space-y-3">
106
+ <div className="space-y-2">
107
+ <Label htmlFor="name">Name</Label>
108
+ <Input
109
+ id="name"
110
+ value={name}
111
+ onChange={(e) => setName(e.target.value)}
112
+ placeholder="Enter your name"
113
  />
 
 
 
 
114
  </div>
115
+ <div className="space-y-2">
116
+ <Label htmlFor="email">Email / Student ID</Label>
117
+ <Input
118
+ id="email"
119
+ value={email}
120
+ onChange={(e) => setEmail(e.target.value)}
121
+ placeholder="Enter your email or ID"
122
+ />
123
+ </div>
124
+ <div className="flex gap-2">
125
+ <Button onClick={handleLoginClick} className="flex-1">
126
+ Enter
127
+ </Button>
128
+ <Button variant="outline" onClick={() => setShowLoginForm(false)}>
129
+ Cancel
130
  </Button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  </div>
 
 
 
 
 
 
 
 
132
  </div>
133
  )}
134
+ </div>
135
+ ) : (
136
+ <div className="space-y-4">
137
+ <div className="flex items-center gap-3">
138
+ <div className="w-12 h-12 rounded-full bg-gradient-to-br from-purple-500 to-blue-500 flex items-center justify-center text-white">
139
+ {user?.name?.charAt(0)?.toUpperCase() ?? 'U'}
140
+ </div>
141
+ <div className="flex-1 min-w-0">
142
+ <h4 className="truncate">{user?.name}</h4>
143
+ <p className="text-sm text-muted-foreground truncate">{user?.email}</p>
144
+ </div>
145
+ </div>
146
+ <Button variant="destructive" onClick={handleLogout} className="w-full gap-2">
147
+ <LogOut className="h-4 w-4" />
148
+ Log Out
149
+ </Button>
150
+ </div>
151
+ )}
152
+ </Card>
153
+
154
+ <Separator />
155
+
156
+ {/* Results Section */}
157
+ <div className="space-y-3">
158
+ <h3>
159
+ {resultType === 'export' && 'Exported Conversation'}
160
+ {resultType === 'quiz' && 'Micro-Quiz'}
161
+ {resultType === 'summary' && 'Summarization'}
162
+ {!resultType && 'Results'}
163
+ </h3>
164
+
165
+ <Card className="p-4 min-h-[200px] bg-muted/30">
166
+ {exportResult ? (
167
+ <div className="space-y-3">
168
+ <div className="flex items-center justify-between">
169
+ <FileText className="h-4 w-4 text-muted-foreground" />
 
 
170
  <Button
171
+ variant="ghost"
172
+ size="sm"
173
+ onClick={() => {
174
+ navigator.clipboard.writeText(exportResult);
175
+ toast.success('Copied to clipboard!');
176
+ }}
177
  >
178
+ Copy
 
179
  </Button>
180
+ </div>
181
+ <div className="text-sm whitespace-pre-wrap text-foreground">{exportResult}</div>
182
+ </div>
183
+ ) : (
184
+ <div className="flex items-center justify-center h-full text-sm text-muted-foreground text-left">
185
+ Results (export / summary / quiz) will appear here after using the actions
186
+ </div>
187
+ )}
188
+ </Card>
189
+ </div>
190
+
191
+ <Separator />
192
+
193
+ {/* Feedback Section */}
194
+ <div className="space-y-3">
195
+ <h3>Feedback</h3>
196
+ <Dialog open={feedbackDialogOpen} onOpenChange={setFeedbackDialogOpen}>
197
+ <DialogTrigger asChild>
198
+ <Button variant="outline" className="w-full justify-start gap-2">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  <MessageSquare className="h-4 w-4" />
200
  Provide Feedback
201
  </Button>
202
+ </DialogTrigger>
203
+
204
+ <DialogContent className="sm:max-w-[425px]">
205
+ <DialogHeader>
206
+ <DialogTitle>Provide Feedback</DialogTitle>
207
+ <DialogDescription>Help us improve Clare by sharing your thoughts and suggestions.</DialogDescription>
208
+ </DialogHeader>
209
+
210
+ <div className="space-y-3">
211
+ <div className="space-y-2">
212
+ <Label htmlFor="feedbackCategory">Category</Label>
213
+ <Select
214
+ value={feedbackCategory}
215
+ onValueChange={(value) => setFeedbackCategory(value as 'general' | 'bug' | 'feature')}
216
+ >
217
+ <SelectTrigger>
218
+ <SelectValue placeholder="Select a category" />
219
+ </SelectTrigger>
220
+ <SelectContent>
221
+ <SelectItem value="general">General Feedback</SelectItem>
222
+ <SelectItem value="bug">Bug Report</SelectItem>
223
+ <SelectItem value="feature">Feature Request</SelectItem>
224
+ </SelectContent>
225
+ </Select>
226
+ </div>
227
 
228
+ <div className="space-y-2">
229
+ <Label htmlFor="feedbackText">Feedback</Label>
230
+ <Textarea
231
+ id="feedbackText"
232
+ value={feedbackText}
233
+ onChange={(e) => setFeedbackText(e.target.value)}
234
+ placeholder="Enter your feedback here..."
235
+ className="min-h-[100px]"
236
+ />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  </div>
238
+ </div>
239
+
240
+ <DialogFooter>
241
+ <Button variant="outline" onClick={() => setFeedbackDialogOpen(false)}>
242
+ Cancel
243
+ </Button>
244
+ <Button onClick={handleFeedbackSubmit}>Submit</Button>
245
+ </DialogFooter>
246
+ </DialogContent>
247
+ </Dialog>
248
+ </div>
 
 
 
 
 
 
 
249
  </div>
250
  );
251
+ }