SarahXia0405's picture
Update web/src/components/RightPanel.tsx
8a3c12f verified
// web/src/components/RightPanel.tsx
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, FileText, MessageSquare, Download, ClipboardList, Sparkles } from 'lucide-react';
import type { User } from '../App';
import { toast } from 'sonner';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogFooter,
} from './ui/dialog';
interface RightPanelProps {
user: User | null;
onLogin: (name: string, emailOrId: string) => 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;
// ✅ Actions buttons callbacks (来自 App.tsx)
onExport: () => void;
onQuiz: () => void;
onSummary: () => void;
}
export function RightPanel({
user,
onLogin,
onLogout,
isLoggedIn,
exportResult,
setExportResult,
resultType,
setResultType,
onExport,
onQuiz,
onSummary,
}: RightPanelProps) {
const [showLoginForm, setShowLoginForm] = useState(false);
const [name, setName] = useState('');
const [emailOrId, setEmailOrId] = useState('');
const [feedbackDialogOpen, setFeedbackDialogOpen] = useState(false);
const [feedbackText, setFeedbackText] = useState('');
const [feedbackCategory, setFeedbackCategory] = useState<'general' | 'bug' | 'feature'>('general');
const handleLoginClick = () => {
if (!name.trim() || !emailOrId.trim()) {
toast.error('Please fill in all fields');
return;
}
onLogin(name.trim(), emailOrId.trim());
setShowLoginForm(false);
setName('');
setEmailOrId('');
};
const handleLogoutClick = () => {
onLogout();
setShowLoginForm(false);
};
const handleFeedbackSubmit = () => {
if (!feedbackText.trim()) {
toast.error('Please provide feedback text');
return;
}
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">
{/* Account */}
<Card className="p-4">
{!isLoggedIn ? (
<div className="space-y-4">
<div className="flex flex-col items-center py-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 learning session
</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="emailOrId">Email / Student ID</Label>
<Input
id="emailOrId"
value={emailOrId}
onChange={(e) => setEmailOrId(e.target.value)}
placeholder="Enter your email or ID"
/>
</div>
<div className="flex gap-2">
<Button onClick={handleLoginClick} 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>
<p className="text-xs text-muted-foreground truncate">user_id: {user?.user_id}</p>
</div>
</div>
<Button variant="destructive" onClick={handleLogoutClick} className="w-full gap-2">
<LogOut className="h-4 w-4" />
Log Out
</Button>
</div>
)}
</Card>
{/* ✅ Actions:三按钮并排(同宽) */}
{/* Actions */}
{/* Actions */}
<Card className="p-4">
<div className="flex items-center justify-between mb-3">
<h3 className="text-base font-medium">Actions TEST 123</h3>
</div>
{/* 强制一排三个:不用 grid,直接 flex,最稳 */}
<div className="flex items-stretch gap-2">
<Button
variant="outline"
onClick={onExport}
disabled={!isLoggedIn}
title="Export"
className="flex-1 h-10 p-0 flex items-center justify-center rounded-xl"
>
<Download className="h-4 w-4" />
</Button>
<Button
variant="outline"
onClick={onQuiz}
disabled={!isLoggedIn}
title="Quiz"
className="flex-1 h-10 p-0 flex items-center justify-center rounded-xl"
>
<ClipboardList className="h-4 w-4" />
</Button>
<Button
variant="outline"
onClick={onSummary}
disabled={!isLoggedIn}
title="Summary"
className="flex-1 h-10 p-0 flex items-center justify-center rounded-xl"
>
<Sparkles className="h-4 w-4" />
</Button>
</div>
</Card>
<Separator />
{/* Results */}
<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 actions run
</div>
)}
</Card>
</div>
<Separator />
{/* Feedback */}
<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>
<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={(v) => setFeedbackCategory(v as any)}>
<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 onClick={handleFeedbackSubmit}>Submit</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
}