diff --git a/frontend/module/profile/UserProfilePage.tsx b/frontend/module/profile/UserProfilePage.tsx new file mode 100644 index 0000000..27635a7 --- /dev/null +++ b/frontend/module/profile/UserProfilePage.tsx @@ -0,0 +1,168 @@ +"use client"; + +import { useEffect, useState, useCallback } from "react"; +import Link from "next/link"; +import { CheckCircle, XCircle, ShieldCheck, ShieldOff } from "lucide-react"; + +interface UserProfile { + fullName: string; + email: string; + role: string; + isVerified: boolean; + preferredLanguage: string; + twoFactorEnabled: boolean; + createdAt: string; +} + +function SkeletonField() { + return ( +
+
+
+
+ ); +} + +export default function UserProfilePage() { + const [profile, setProfile] = useState(null); + const [loading, setLoading] = useState(true); + const [resending, setResending] = useState(false); + const [resendMsg, setResendMsg] = useState(null); + + const fetchProfile = useCallback(async () => { + setLoading(true); + try { + const res = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/api/module/users/me`, + { + headers: { + Authorization: `Bearer ${localStorage.getItem("access_token")}`, + }, + } + ); + if (!res.ok) throw new Error(); + setProfile(await res.json()); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + fetchProfile(); + }, [fetchProfile]); + + async function handleResend() { + setResending(true); + setResendMsg(null); + try { + await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/api/auth/resend-verification`, + { + method: "POST", + headers: { + Authorization: `Bearer ${localStorage.getItem("access_token")}`, + }, + } + ); + setResendMsg("Verification email sent."); + } catch { + setResendMsg("Failed to send. Please try again."); + } finally { + setResending(false); + } + } + + return ( +
+
+

Profile

+ {!loading && ( + + Edit Profile + + )} +
+ + {!loading && profile && !profile.isVerified && ( +
+

+ Your email is not verified. Please check your inbox. +

+ + {resendMsg && {resendMsg}} +
+ )} + +
+
+ {loading ? ( + Array.from({ length: 6 }).map((_, i) => ) + ) : profile ? ( + <> +
+
Full Name
+
{profile.fullName}
+
+
+
Email
+
+ {profile.email} + {profile.isVerified ? ( + + ) : ( + + )} +
+
+
+
Role
+
{profile.role}
+
+
+
Language
+
{profile.preferredLanguage}
+
+
+
Member Since
+
+ {new Date(profile.createdAt).toLocaleDateString()} +
+
+
+
Two-Factor Auth
+
+ {profile.twoFactorEnabled ? ( + <> +
+
+ + ) : ( +

Failed to load profile.

+ )} +
+
+
+ ); +}