Spaces:
Sleeping
Sleeping
| import React, { useState } from 'react'; | |
| import { Button } from './ui/button'; | |
| import { Input } from './ui/input'; | |
| import { Label } from './ui/label'; | |
| import { Card } from './ui/card'; | |
| import { Separator } from './ui/separator'; | |
| import { Textarea } from './ui/textarea'; | |
| import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; | |
| import { | |
| LogIn, | |
| LogOut, | |
| Download, | |
| ClipboardList, | |
| FileText, | |
| Sparkles, | |
| ChevronUp, | |
| ChevronDown, | |
| PanelRightClose, | |
| MessageSquare | |
| } from 'lucide-react'; | |
| import type { User } from '../App'; | |
| import { toast } from 'sonner@2.0.3'; | |
| import { | |
| Dialog, | |
| DialogContent, | |
| DialogDescription, | |
| DialogHeader, | |
| DialogTitle, | |
| DialogTrigger, | |
| DialogFooter, | |
| } from './ui/dialog'; | |
| interface RightPanelProps { | |
| user: User | null; | |
| onLogin: (user: User) => void; | |
| onLogout: () => void; | |
| isLoggedIn: boolean; | |
| onClose?: () => void; | |
| exportResult: string; | |
| setExportResult: (result: string) => void; | |
| resultType: 'export' | 'quiz' | 'summary' | null; | |
| setResultType: (type: 'export' | 'quiz' | 'summary' | null) => void; | |
| } | |
| export function RightPanel({ user, onLogin, onLogout, isLoggedIn, onClose, exportResult, setExportResult, resultType, setResultType }: RightPanelProps) { | |
| const [showLoginForm, setShowLoginForm] = useState(false); | |
| const [name, setName] = useState(''); | |
| const [email, setEmail] = useState(''); | |
| const [isExpanded, setIsExpanded] = useState(true); | |
| const [feedbackDialogOpen, setFeedbackDialogOpen] = useState(false); | |
| const [feedbackText, setFeedbackText] = useState(''); | |
| const [feedbackCategory, setFeedbackCategory] = useState<'general' | 'bug' | 'feature'>('general'); | |
| const handleLogin = () => { | |
| if (!name.trim() || !email.trim()) { | |
| toast.error('Please fill in all fields'); | |
| return; | |
| } | |
| onLogin({ name: name.trim(), email: email.trim() }); | |
| setShowLoginForm(false); | |
| setName(''); | |
| setEmail(''); | |
| toast.success(`Welcome, ${name}!`); | |
| }; | |
| const handleLogout = () => { | |
| onLogout(); | |
| setShowLoginForm(false); | |
| toast.success('Logged out successfully'); | |
| }; | |
| const handleExport = () => { | |
| const result = `# Conversation Export | |
| Date: ${new Date().toLocaleDateString()} | |
| Student: ${user?.name} | |
| ## Summary | |
| This conversation covered key concepts in Module 10 – Responsible AI, including ethical considerations, fairness, transparency, and accountability in AI systems. | |
| ## Key Takeaways | |
| 1. Understanding the principles of Responsible AI | |
| 2. Real-world applications and implications | |
| 3. Best practices for ethical AI development | |
| Exported successfully! ✓`; | |
| setExportResult(result); | |
| setResultType('export'); | |
| toast.success('Conversation exported!'); | |
| }; | |
| const handleQuiz = () => { | |
| const quiz = `# Micro-Quiz: Responsible AI | |
| **Question 1:** Which of the following is a key principle of Responsible AI? | |
| a) Profit maximization | |
| b) Transparency | |
| c) Rapid deployment | |
| d) Cost reduction | |
| **Question 2:** What is algorithmic fairness? | |
| (Short answer expected) | |
| **Question 3:** True or False: AI systems should always prioritize accuracy over fairness. | |
| Generate quiz based on your conversation!`; | |
| setExportResult(quiz); | |
| setResultType('quiz'); | |
| toast.success('Quiz generated!'); | |
| }; | |
| const handleSummary = () => { | |
| const summary = `# Learning Summary | |
| ## Today's Session | |
| **Duration:** 25 minutes | |
| **Topics Covered:** 3 | |
| **Messages Exchanged:** 12 | |
| ## Key Concepts Discussed | |
| • Principles of Responsible AI | |
| • Ethical considerations in AI development | |
| • Fairness and transparency in algorithms | |
| ## Recommended Next Steps | |
| 1. Review Module 10, Section 2.3 | |
| 2. Complete practice quiz on algorithmic fairness | |
| 3. Read additional resources on AI ethics | |
| ## Progress Update | |
| You've covered 65% of Module 10 content. Keep up the great work! 🎉`; | |
| setExportResult(summary); | |
| setResultType('summary'); | |
| toast.success('Summary generated!'); | |
| }; | |
| const handleFeedbackSubmit = () => { | |
| if (!feedbackText.trim()) { | |
| toast.error('Please provide feedback text'); | |
| return; | |
| } | |
| // Here you can add logic to send feedback to your server or handle it as needed | |
| console.log('Feedback submitted:', feedbackText, feedbackCategory); | |
| setFeedbackDialogOpen(false); | |
| setFeedbackText(''); | |
| toast.success('Feedback submitted!'); | |
| }; | |
| return ( | |
| <div className="flex-1 overflow-auto p-4 space-y-4"> | |
| {isExpanded && ( | |
| <> | |
| {/* Login Section */} | |
| <Card className="p-4"> | |
| {!isLoggedIn ? ( | |
| <div className="space-y-4"> | |
| <div className="flex flex-col items-center py-4"> | |
| <img | |
| 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" | |
| alt="Student studying" | |
| className="w-20 h-20 rounded-full object-cover mb-4" | |
| /> | |
| <h3 className="mb-2">Welcome to Clare!</h3> | |
| <p className="text-sm text-muted-foreground text-center mb-4"> | |
| Log in to start your personalized learning journey | |
| </p> | |
| </div> | |
| {!showLoginForm ? ( | |
| <Button onClick={() => setShowLoginForm(true)} className="w-full gap-2"> | |
| <LogIn className="h-4 w-4" /> | |
| Student Login | |
| </Button> | |
| ) : ( | |
| <div className="space-y-3"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="name">Name</Label> | |
| <Input | |
| id="name" | |
| value={name} | |
| onChange={(e) => setName(e.target.value)} | |
| placeholder="Enter your name" | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="email">Email / Student ID</Label> | |
| <Input | |
| id="email" | |
| type="email" | |
| value={email} | |
| onChange={(e) => setEmail(e.target.value)} | |
| placeholder="Enter your email or ID" | |
| /> | |
| </div> | |
| <div className="flex gap-2"> | |
| <Button onClick={handleLogin} className="flex-1"> | |
| Enter | |
| </Button> | |
| <Button | |
| variant="outline" | |
| onClick={() => setShowLoginForm(false)} | |
| > | |
| Cancel | |
| </Button> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ) : ( | |
| <div className="space-y-4"> | |
| <div className="flex items-center gap-3"> | |
| <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"> | |
| {user.name.charAt(0).toUpperCase()} | |
| </div> | |
| <div className="flex-1 min-w-0"> | |
| <h4 className="truncate">{user.name}</h4> | |
| <p className="text-sm text-muted-foreground truncate">{user.email}</p> | |
| </div> | |
| </div> | |
| <Button | |
| variant="destructive" | |
| onClick={handleLogout} | |
| className="w-full gap-2" | |
| > | |
| <LogOut className="h-4 w-4" /> | |
| Log Out | |
| </Button> | |
| </div> | |
| )} | |
| </Card> | |
| <Separator /> | |
| {/* Actions section removed - functionality available via floating buttons */} | |
| {/* <div className="space-y-3"> | |
| <h3>Actions</h3> | |
| <Dialog> | |
| <DialogTrigger asChild> | |
| <Button | |
| variant="outline" | |
| className="w-full justify-start gap-2" | |
| disabled={!isLoggedIn} | |
| onClick={handleExport} | |
| > | |
| <Download className="h-4 w-4" /> | |
| Export Conversation | |
| </Button> | |
| </DialogTrigger> | |
| </Dialog> | |
| <Dialog> | |
| <DialogTrigger asChild> | |
| <Button | |
| variant="outline" | |
| className="w-full justify-start gap-2" | |
| disabled={!isLoggedIn} | |
| onClick={handleQuiz} | |
| > | |
| <ClipboardList className="h-4 w-4" /> | |
| Let's Try (Micro-Quiz) | |
| </Button> | |
| </DialogTrigger> | |
| </Dialog> | |
| <Dialog> | |
| <DialogTrigger asChild> | |
| <Button | |
| variant="outline" | |
| className="w-full justify-start gap-2" | |
| disabled={!isLoggedIn} | |
| onClick={handleSummary} | |
| > | |
| <Sparkles className="h-4 w-4" /> | |
| Summarization | |
| </Button> | |
| </DialogTrigger> | |
| </Dialog> | |
| {!isLoggedIn && ( | |
| <p className="text-xs text-muted-foreground text-center pt-2"> | |
| Log in to unlock all features | |
| </p> | |
| )} | |
| </div> */} | |
| <Separator /> | |
| {/* Results Section */} | |
| <div className="space-y-3"> | |
| <h3> | |
| {resultType === 'export' && 'Exported Conversation'} | |
| {resultType === 'quiz' && 'Micro-Quiz'} | |
| {resultType === 'summary' && 'Summarization'} | |
| {!resultType && 'Results'} | |
| </h3> | |
| <Card className="p-4 min-h-[200px] bg-muted/30"> | |
| {exportResult ? ( | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between"> | |
| <FileText className="h-4 w-4 text-muted-foreground" /> | |
| <Button | |
| variant="ghost" | |
| size="sm" | |
| onClick={() => { | |
| navigator.clipboard.writeText(exportResult); | |
| toast.success('Copied to clipboard!'); | |
| }} | |
| > | |
| Copy | |
| </Button> | |
| </div> | |
| <div className="text-sm whitespace-pre-wrap text-foreground"> | |
| {exportResult} | |
| </div> | |
| </div> | |
| ) : ( | |
| <div className="flex items-center justify-center h-full text-sm text-muted-foreground text-left"> | |
| Results (export / summary / quiz) will appear here after using the actions above | |
| </div> | |
| )} | |
| </Card> | |
| </div> | |
| <Separator /> | |
| {/* Feedback Section */} | |
| <div className="space-y-3"> | |
| <h3>Feedback</h3> | |
| <Button | |
| variant="outline" | |
| className="w-full justify-start gap-2" | |
| onClick={() => setFeedbackDialogOpen(true)} | |
| > | |
| <MessageSquare className="h-4 w-4" /> | |
| Provide Feedback | |
| </Button> | |
| </div> | |
| {/* Feedback Dialog */} | |
| <Dialog open={feedbackDialogOpen} onOpenChange={setFeedbackDialogOpen}> | |
| <DialogContent className="sm:max-w-[425px]"> | |
| <DialogHeader> | |
| <DialogTitle>Provide Feedback</DialogTitle> | |
| <DialogDescription> | |
| Help us improve Clare by sharing your thoughts and suggestions. | |
| </DialogDescription> | |
| </DialogHeader> | |
| <div className="space-y-3"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="feedbackCategory">Category</Label> | |
| <Select value={feedbackCategory} onValueChange={(value) => setFeedbackCategory(value as 'general' | 'bug' | 'feature')}> | |
| <SelectTrigger> | |
| <SelectValue placeholder="Select a category" /> | |
| </SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="general">General Feedback</SelectItem> | |
| <SelectItem value="bug">Bug Report</SelectItem> | |
| <SelectItem value="feature">Feature Request</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="feedbackText">Feedback</Label> | |
| <Textarea | |
| id="feedbackText" | |
| value={feedbackText} | |
| onChange={(e) => setFeedbackText(e.target.value)} | |
| placeholder="Enter your feedback here..." | |
| className="min-h-[100px]" | |
| /> | |
| </div> | |
| </div> | |
| <DialogFooter> | |
| <Button | |
| variant="outline" | |
| onClick={() => setFeedbackDialogOpen(false)} | |
| > | |
| Cancel | |
| </Button> | |
| <Button | |
| variant="primary" | |
| onClick={handleFeedbackSubmit} | |
| > | |
| Submit | |
| </Button> | |
| </DialogFooter> | |
| </DialogContent> | |
| </Dialog> | |
| </> | |
| )} | |
| </div> | |
| ); | |
| } |