diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx new file mode 100644 index 0000000..b96adb4 --- /dev/null +++ b/src/app/profile/page.tsx @@ -0,0 +1,219 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { User, Mail, Pencil, CheckCircle2 } from 'lucide-react'; +import Link from 'next/link'; +import { usersApi } from '@/lib/api/services'; +import { useAuthStore } from '@/lib/hooks/use-auth-store'; +import { AvatarUpload } from '@/components/ui/AvatarUpload'; +import { Breadcrumb } from '@/components/ui'; +import { ProfileCompletion } from '@/components/dashboard/ProfileCompletion'; +import type { User as UserType } from '@/types'; + +export default function ProfilePage() { + const router = useRouter(); + const updateUser = useAuthStore((s) => s.updateUser); + const authUser = useAuthStore((s) => s.user); + + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + const [saved, setSaved] = useState(false); + + const [me, setMe] = useState(null); + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [bio, setBio] = useState(''); + + useEffect(() => { + let mounted = true; + usersApi + .getMe() + .then((u) => { + if (!mounted) return; + setMe(u); + setName(u.name ?? ''); + setEmail(u.email ?? ''); + setBio(u.bio ?? ''); + }) + .catch((e) => { if (mounted) setError(e?.message ?? 'Failed to load profile'); }) + .finally(() => { if (mounted) setLoading(false); }); + return () => { mounted = false; }; + }, []); + + const handleSave = async () => { + if (!name.trim()) return; + setSaving(true); + setError(null); + setSaved(false); + try { + const updated = await usersApi.updateMe({ + name: name.trim(), + email: email.trim() || undefined, + bio: bio.trim() || undefined, + }); + setMe(updated); + updateUser({ name: updated.name, email: updated.email }); + setSaved(true); + setTimeout(() => setSaved(false), 2500); + } catch (e: any) { + setError(e?.message ?? 'Failed to save profile'); + } finally { + setSaving(false); + } + }; + + if (loading) { + return ( +
+
+
Loading…
+
+ ); + } + + return ( +
+ {/* ── Page header ── */} +
+ +

Profile

+

Update your public information.

+
+ + {error && ( +
+ {error} +
+ )} + +
+ + {/* ── Col 1: Avatar upload ── */} +
+
+

Profile photo

+ + setMe((prev) => (prev ? { ...prev, avatarUrl: url } : prev)) + } + /> +
+ + {/* Profile Completion Indicator */} + +
+ + {/* ── Col 2: Info form ── */} +
+
+
+
+ +
+
+

Your identity

+

+ {me?.name ?? 'Your name'} +

+

+ Role: {me?.role?.toLowerCase() ?? 'student'} +

+
+
+ +
+ + + + +