Spaces:
Sleeping
Sleeping
| import React, { useState, useRef } from 'react'; | |
| import { Button } from './ui/button'; | |
| import { Input } from './ui/input'; | |
| import { Label } from './ui/label'; | |
| import { Textarea } from './ui/textarea'; | |
| import { Dialog, DialogContent, DialogTitle } from './ui/dialog'; | |
| import type { User as UserType } from '../App'; | |
| import { toast } from 'sonner'; | |
| import { | |
| Select, | |
| SelectContent, | |
| SelectItem, | |
| SelectTrigger, | |
| SelectValue, | |
| } from './ui/select'; | |
| interface ProfileEditorProps { | |
| user: UserType; | |
| onSave: (user: UserType) => void; | |
| onClose: () => void; | |
| } | |
| export function ProfileEditor({ user, onSave, onClose }: ProfileEditorProps) { | |
| const [name, setName] = useState(user.name); | |
| const [email, setEmail] = useState(user.email); | |
| const [studentId, setStudentId] = useState('S12345678'); | |
| const [department, setDepartment] = useState('Computer Science'); | |
| const [year, setYear] = useState('3rd Year'); | |
| const [major, setMajor] = useState('Artificial Intelligence'); | |
| const [bio, setBio] = useState('Passionate about AI and machine learning'); | |
| const [photoPreview, setPhotoPreview] = useState<string | null>(null); | |
| const fileInputRef = useRef<HTMLInputElement>(null); | |
| const handleSave = () => { | |
| if (name.trim() && email.trim()) { | |
| onSave({ name: name.trim(), email: email.trim() }); | |
| toast.success('Profile updated successfully!'); | |
| onClose(); | |
| } else { | |
| toast.error('Please fill in all required fields'); | |
| } | |
| }; | |
| const handlePhotoSelect = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| const file = e.target.files?.[0]; | |
| if (file) { | |
| // Validate file type | |
| if (!file.type.startsWith('image/')) { | |
| toast.error('Please select an image file'); | |
| return; | |
| } | |
| // Validate file size (2MB) | |
| if (file.size > 2 * 1024 * 1024) { | |
| toast.error('File size must be less than 2MB'); | |
| return; | |
| } | |
| // Create preview | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| setPhotoPreview(e.target?.result as string); | |
| toast.success('Photo updated successfully!'); | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| }; | |
| const handleChangePhotoClick = () => { | |
| fileInputRef.current?.click(); | |
| }; | |
| return ( | |
| <Dialog open onOpenChange={(open) => { if (!open) onClose(); }}> | |
| <DialogContent | |
| className="sm:max-w-[800px] p-0 gap-0 max-h-[90vh] overflow-hidden" | |
| style={{ zIndex: 1001, maxWidth: '800px', width: '800px' }} | |
| overlayClassName="!top-16 !left-0 !right-0 !bottom-0 !z-[99]" | |
| overlayStyle={{ | |
| top: '64px', | |
| left: 0, | |
| right: 0, | |
| bottom: 0, | |
| zIndex: 99, | |
| position: 'fixed' | |
| }} | |
| > | |
| <div className="flex flex-col max-h-[90vh]"> | |
| {/* Header */} | |
| <div className="p-4 flex items-center justify-between flex-shrink-0"> | |
| <DialogTitle className="text-xl font-medium">Edit Profile</DialogTitle> | |
| </div> | |
| {/* Content */} | |
| <div className="p-6 space-y-6 overflow-y-auto flex-1"> | |
| {/* Profile Picture */} | |
| <div className="flex items-center gap-4"> | |
| <div className="w-20 h-20 rounded-full bg-gradient-to-br from-red-500 to-orange-500 flex items-center justify-center text-white text-2xl overflow-hidden"> | |
| {photoPreview ? ( | |
| <img src={photoPreview} alt="Profile" className="w-full h-full object-cover" /> | |
| ) : ( | |
| name.charAt(0).toUpperCase() | |
| )} | |
| </div> | |
| <div> | |
| <input | |
| ref={fileInputRef} | |
| type="file" | |
| accept="image/jpeg,image/png,image/gif,image/webp" | |
| onChange={handlePhotoSelect} | |
| className="hidden" | |
| /> | |
| <Button | |
| variant="outline" | |
| size="sm" | |
| onClick={handleChangePhotoClick} | |
| > | |
| Change Photo | |
| </Button> | |
| <p className="text-xs text-muted-foreground mt-1"> | |
| JPG, PNG or GIF. Max size 2MB | |
| </p> | |
| </div> | |
| </div> | |
| {/* Basic Information */} | |
| <div className="space-y-4"> | |
| <h3 className="text-sm font-medium">Basic Information</h3> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="edit-name">Full Name *</Label> | |
| <Input | |
| id="edit-name" | |
| value={name} | |
| onChange={(e) => setName(e.target.value)} | |
| placeholder="Enter your full name" | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="edit-email">Email *</Label> | |
| <Input | |
| id="edit-email" | |
| type="email" | |
| value={email} | |
| onChange={(e) => setEmail(e.target.value)} | |
| placeholder="Enter your email" | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="edit-student-id">Student ID</Label> | |
| <Input | |
| id="edit-student-id" | |
| value={studentId} | |
| onChange={(e) => setStudentId(e.target.value)} | |
| placeholder="Enter your student ID" | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="edit-department">Department</Label> | |
| <Input | |
| id="edit-department" | |
| value={department} | |
| onChange={(e) => setDepartment(e.target.value)} | |
| placeholder="Enter your department" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Academic Background */} | |
| <div className="space-y-4"> | |
| <h3 className="text-sm font-medium">Academic Background</h3> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="edit-year">Year Level</Label> | |
| <Select value={year} onValueChange={setYear}> | |
| <SelectTrigger id="edit-year"> | |
| <SelectValue /> | |
| </SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="1st Year">1st Year</SelectItem> | |
| <SelectItem value="2nd Year">2nd Year</SelectItem> | |
| <SelectItem value="3rd Year">3rd Year</SelectItem> | |
| <SelectItem value="4th Year">4th Year</SelectItem> | |
| <SelectItem value="Graduate">Graduate</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="edit-major">Major</Label> | |
| <Input | |
| id="edit-major" | |
| value={major} | |
| onChange={(e) => setMajor(e.target.value)} | |
| placeholder="Enter your major" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Bio */} | |
| <div className="space-y-2"> | |
| <Label htmlFor="edit-bio">Bio</Label> | |
| <Textarea | |
| id="edit-bio" | |
| value={bio} | |
| onChange={(e) => setBio(e.target.value)} | |
| placeholder="Tell us about yourself..." | |
| className="min-h-[100px] resize-none" | |
| /> | |
| <p className="text-xs text-muted-foreground"> | |
| Brief description for your profile. Max 200 characters. | |
| </p> | |
| </div> | |
| {/* Learning Preferences */} | |
| <div className="space-y-4"> | |
| <h3 className="text-sm font-medium">Learning Preferences</h3> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="edit-learning-style">Preferred Learning Style</Label> | |
| <Select defaultValue="visual"> | |
| <SelectTrigger id="edit-learning-style"> | |
| <SelectValue /> | |
| </SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="visual">Visual</SelectItem> | |
| <SelectItem value="auditory">Auditory</SelectItem> | |
| <SelectItem value="reading">Reading/Writing</SelectItem> | |
| <SelectItem value="kinesthetic">Kinesthetic</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="edit-pace">Learning Pace</Label> | |
| <Select defaultValue="moderate"> | |
| <SelectTrigger id="edit-pace"> | |
| <SelectValue /> | |
| </SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="slow">Slow & Steady</SelectItem> | |
| <SelectItem value="moderate">Moderate</SelectItem> | |
| <SelectItem value="fast">Fast-paced</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Footer */} | |
| <div className="border-t border-border p-4 flex justify-end gap-2 flex-shrink-0"> | |
| <Button variant="outline" onClick={onClose}> | |
| Cancel | |
| </Button> | |
| <Button onClick={handleSave}> | |
| Save Changes | |
| </Button> | |
| </div> | |
| </div> | |
| </DialogContent> | |
| </Dialog> | |
| ); | |
| } | |