Spaces:
Sleeping
Sleeping
Update web/src/components/ProfileEditor.tsx
Browse files
web/src/components/ProfileEditor.tsx
CHANGED
|
@@ -15,27 +15,46 @@ interface ProfileEditorProps {
|
|
| 15 |
}
|
| 16 |
|
| 17 |
export function ProfileEditor({ user, onSave, onClose }: ProfileEditorProps) {
|
| 18 |
-
const [name, setName] = useState(user.name);
|
| 19 |
-
const [email, setEmail] = useState(user.email);
|
| 20 |
|
| 21 |
-
|
| 22 |
-
const [
|
| 23 |
-
const [
|
| 24 |
-
const [
|
| 25 |
-
const [major, setMajor] = useState("Artificial Intelligence");
|
| 26 |
|
| 27 |
-
// ✅ IMPORTANT: initialize from user.bio (so it reflects Clare-generated bio)
|
| 28 |
const [bio, setBio] = useState(user.bio ?? "");
|
| 29 |
|
| 30 |
-
const [
|
|
|
|
|
|
|
|
|
|
| 31 |
const fileInputRef = useRef<HTMLInputElement>(null);
|
| 32 |
|
| 33 |
-
//
|
| 34 |
useEffect(() => {
|
| 35 |
-
setName(user.name);
|
| 36 |
-
setEmail(user.email);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
setBio(user.bio ?? "");
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
const handleSave = () => {
|
| 41 |
if (!name.trim() || !email.trim()) {
|
|
@@ -47,7 +66,14 @@ export function ProfileEditor({ user, onSave, onClose }: ProfileEditorProps) {
|
|
| 47 |
...user,
|
| 48 |
name: name.trim(),
|
| 49 |
email: email.trim(),
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
};
|
| 52 |
|
| 53 |
onSave(next);
|
|
@@ -76,9 +102,7 @@ export function ProfileEditor({ user, onSave, onClose }: ProfileEditorProps) {
|
|
| 76 |
reader.readAsDataURL(file);
|
| 77 |
};
|
| 78 |
|
| 79 |
-
const handleChangePhotoClick = () =>
|
| 80 |
-
fileInputRef.current?.click();
|
| 81 |
-
};
|
| 82 |
|
| 83 |
return (
|
| 84 |
<Dialog
|
|
@@ -91,14 +115,7 @@ export function ProfileEditor({ user, onSave, onClose }: ProfileEditorProps) {
|
|
| 91 |
className="sm:max-w-[800px] p-0 gap-0 max-h-[90vh] overflow-hidden"
|
| 92 |
style={{ zIndex: 1001, maxWidth: "800px", width: "800px" }}
|
| 93 |
overlayClassName="!top-16 !left-0 !right-0 !bottom-0 !z-[99]"
|
| 94 |
-
overlayStyle={{
|
| 95 |
-
top: "64px",
|
| 96 |
-
left: 0,
|
| 97 |
-
right: 0,
|
| 98 |
-
bottom: 0,
|
| 99 |
-
zIndex: 99,
|
| 100 |
-
position: "fixed",
|
| 101 |
-
}}
|
| 102 |
>
|
| 103 |
<div className="flex flex-col max-h-[90vh]">
|
| 104 |
{/* Header */}
|
|
@@ -114,7 +131,7 @@ export function ProfileEditor({ user, onSave, onClose }: ProfileEditorProps) {
|
|
| 114 |
{photoPreview ? (
|
| 115 |
<img src={photoPreview} alt="Profile" className="w-full h-full object-cover" />
|
| 116 |
) : (
|
| 117 |
-
name.charAt(0).toUpperCase()
|
| 118 |
)}
|
| 119 |
</div>
|
| 120 |
<div>
|
|
@@ -182,9 +199,9 @@ export function ProfileEditor({ user, onSave, onClose }: ProfileEditorProps) {
|
|
| 182 |
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 183 |
<div className="space-y-2">
|
| 184 |
<Label htmlFor="edit-year">Year Level</Label>
|
| 185 |
-
<Select value={
|
| 186 |
<SelectTrigger id="edit-year">
|
| 187 |
-
<SelectValue />
|
| 188 |
</SelectTrigger>
|
| 189 |
<SelectContent>
|
| 190 |
<SelectItem value="1st Year">1st Year</SelectItem>
|
|
@@ -227,7 +244,7 @@ export function ProfileEditor({ user, onSave, onClose }: ProfileEditorProps) {
|
|
| 227 |
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 228 |
<div className="space-y-2">
|
| 229 |
<Label htmlFor="edit-learning-style">Preferred Learning Style</Label>
|
| 230 |
-
<Select
|
| 231 |
<SelectTrigger id="edit-learning-style">
|
| 232 |
<SelectValue />
|
| 233 |
</SelectTrigger>
|
|
@@ -239,9 +256,10 @@ export function ProfileEditor({ user, onSave, onClose }: ProfileEditorProps) {
|
|
| 239 |
</SelectContent>
|
| 240 |
</Select>
|
| 241 |
</div>
|
|
|
|
| 242 |
<div className="space-y-2">
|
| 243 |
<Label htmlFor="edit-pace">Learning Pace</Label>
|
| 244 |
-
<Select
|
| 245 |
<SelectTrigger id="edit-pace">
|
| 246 |
<SelectValue />
|
| 247 |
</SelectTrigger>
|
|
|
|
| 15 |
}
|
| 16 |
|
| 17 |
export function ProfileEditor({ user, onSave, onClose }: ProfileEditorProps) {
|
| 18 |
+
const [name, setName] = useState(user.name ?? "");
|
| 19 |
+
const [email, setEmail] = useState(user.email ?? "");
|
| 20 |
|
| 21 |
+
const [studentId, setStudentId] = useState(user.studentId ?? "");
|
| 22 |
+
const [department, setDepartment] = useState(user.department ?? "");
|
| 23 |
+
const [yearLevel, setYearLevel] = useState(user.yearLevel ?? "");
|
| 24 |
+
const [major, setMajor] = useState(user.major ?? "");
|
|
|
|
| 25 |
|
|
|
|
| 26 |
const [bio, setBio] = useState(user.bio ?? "");
|
| 27 |
|
| 28 |
+
const [learningStyle, setLearningStyle] = useState(user.learningStyle ?? "visual");
|
| 29 |
+
const [learningPace, setLearningPace] = useState(user.learningPace ?? "moderate");
|
| 30 |
+
|
| 31 |
+
const [photoPreview, setPhotoPreview] = useState<string | null>(user.avatarUrl ?? null);
|
| 32 |
const fileInputRef = useRef<HTMLInputElement>(null);
|
| 33 |
|
| 34 |
+
// keep in sync if user changes while dialog is open
|
| 35 |
useEffect(() => {
|
| 36 |
+
setName(user.name ?? "");
|
| 37 |
+
setEmail(user.email ?? "");
|
| 38 |
+
setStudentId(user.studentId ?? "");
|
| 39 |
+
setDepartment(user.department ?? "");
|
| 40 |
+
setYearLevel(user.yearLevel ?? "");
|
| 41 |
+
setMajor(user.major ?? "");
|
| 42 |
setBio(user.bio ?? "");
|
| 43 |
+
setLearningStyle(user.learningStyle ?? "visual");
|
| 44 |
+
setLearningPace(user.learningPace ?? "moderate");
|
| 45 |
+
setPhotoPreview(user.avatarUrl ?? null);
|
| 46 |
+
}, [
|
| 47 |
+
user.name,
|
| 48 |
+
user.email,
|
| 49 |
+
user.studentId,
|
| 50 |
+
user.department,
|
| 51 |
+
user.yearLevel,
|
| 52 |
+
user.major,
|
| 53 |
+
user.bio,
|
| 54 |
+
user.learningStyle,
|
| 55 |
+
user.learningPace,
|
| 56 |
+
user.avatarUrl,
|
| 57 |
+
]);
|
| 58 |
|
| 59 |
const handleSave = () => {
|
| 60 |
if (!name.trim() || !email.trim()) {
|
|
|
|
| 66 |
...user,
|
| 67 |
name: name.trim(),
|
| 68 |
email: email.trim(),
|
| 69 |
+
studentId: studentId.trim() || undefined,
|
| 70 |
+
department: department.trim() || undefined,
|
| 71 |
+
yearLevel: yearLevel || undefined,
|
| 72 |
+
major: major.trim() || undefined,
|
| 73 |
+
bio: (bio ?? "").slice(0, 200) || undefined,
|
| 74 |
+
learningStyle: learningStyle || undefined,
|
| 75 |
+
learningPace: learningPace || undefined,
|
| 76 |
+
avatarUrl: photoPreview || undefined,
|
| 77 |
};
|
| 78 |
|
| 79 |
onSave(next);
|
|
|
|
| 102 |
reader.readAsDataURL(file);
|
| 103 |
};
|
| 104 |
|
| 105 |
+
const handleChangePhotoClick = () => fileInputRef.current?.click();
|
|
|
|
|
|
|
| 106 |
|
| 107 |
return (
|
| 108 |
<Dialog
|
|
|
|
| 115 |
className="sm:max-w-[800px] p-0 gap-0 max-h-[90vh] overflow-hidden"
|
| 116 |
style={{ zIndex: 1001, maxWidth: "800px", width: "800px" }}
|
| 117 |
overlayClassName="!top-16 !left-0 !right-0 !bottom-0 !z-[99]"
|
| 118 |
+
overlayStyle={{ top: "64px", left: 0, right: 0, bottom: 0, zIndex: 99, position: "fixed" }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
>
|
| 120 |
<div className="flex flex-col max-h-[90vh]">
|
| 121 |
{/* Header */}
|
|
|
|
| 131 |
{photoPreview ? (
|
| 132 |
<img src={photoPreview} alt="Profile" className="w-full h-full object-cover" />
|
| 133 |
) : (
|
| 134 |
+
(name?.charAt(0) || "U").toUpperCase()
|
| 135 |
)}
|
| 136 |
</div>
|
| 137 |
<div>
|
|
|
|
| 199 |
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 200 |
<div className="space-y-2">
|
| 201 |
<Label htmlFor="edit-year">Year Level</Label>
|
| 202 |
+
<Select value={yearLevel} onValueChange={setYearLevel}>
|
| 203 |
<SelectTrigger id="edit-year">
|
| 204 |
+
<SelectValue placeholder="Select year level" />
|
| 205 |
</SelectTrigger>
|
| 206 |
<SelectContent>
|
| 207 |
<SelectItem value="1st Year">1st Year</SelectItem>
|
|
|
|
| 244 |
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 245 |
<div className="space-y-2">
|
| 246 |
<Label htmlFor="edit-learning-style">Preferred Learning Style</Label>
|
| 247 |
+
<Select value={learningStyle} onValueChange={setLearningStyle}>
|
| 248 |
<SelectTrigger id="edit-learning-style">
|
| 249 |
<SelectValue />
|
| 250 |
</SelectTrigger>
|
|
|
|
| 256 |
</SelectContent>
|
| 257 |
</Select>
|
| 258 |
</div>
|
| 259 |
+
|
| 260 |
<div className="space-y-2">
|
| 261 |
<Label htmlFor="edit-pace">Learning Pace</Label>
|
| 262 |
+
<Select value={learningPace} onValueChange={setLearningPace}>
|
| 263 |
<SelectTrigger id="edit-pace">
|
| 264 |
<SelectValue />
|
| 265 |
</SelectTrigger>
|