// ─────────────────────────────────────────────── // Profile Page (/(dashboard)/profile) // ─────────────────────────────────────────────── // Route: /profile // Purpose: View and edit the current user's // profile — avatar upload, personal info, // role, join date, and status. // Layout: Two-column grid — left card with avatar // + metadata badges, right card with account // details in a 2-column grid. // ─────────────────────────────────────────────── "use client" import { useRef } from "react" import { PageHeader } from "@/components/shared/page-header" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Badge } from "@/components/ui/badge" import { useUser } from "@/providers/user-provider" import { Mail, Calendar, Shield, Activity, Camera } from "lucide-react" import { toast } from "sonner" /** * ProfilePage * ─────────── * Displays the currently logged-in user's profile. * Allows avatar upload (PNG/JPEG) via a hidden * file input with a hover-triggered camera overlay. * * @returns Profile layout or null if no user. */ export default function ProfilePage() { const { user, updateAvatar } = useUser() if (!user) return null const fileInputRef = useRef(null) // ── Avatar upload handler ── const handleAvatarChange = async (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file) return // Validate file type — only PNG and JPEG accepted const validTypes = ["image/png", "image/jpeg"] if (!validTypes.includes(file.type)) { toast.error("Only PNG and JPEG files are allowed") return } // Read as data URL, then POST to API const reader = new FileReader() reader.onload = async () => { const dataUrl = reader.result as string try { const res = await fetch("/api/users/avatar", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ avatar: dataUrl }), }) if (res.ok) { const data = await res.json() updateAvatar(data.avatar) toast.success("Avatar updated") } else { toast.error("Failed to save avatar") } } catch { console.warn("Failed to save avatar") toast.error("Failed to save avatar") } } reader.readAsDataURL(file) } return (
{/* ── Left column: Avatar + quick info ── */} {/* Avatar with hover-to-upload overlay */}
{user.name.split(" ").map((n) => n[0]).join("")}

{user.name}

{user.role} {/* Metadata rows */}
{user.email}
Joined {new Date(user.createdAt).toLocaleDateString("en-US", { month: "long", year: "numeric" })}
{user.role} access
{user.active ? "Active" : "Inactive"}
{/* ── Right column: Full account details ── */}
Account Details
Full Name

{user.name}

Email

{user.email}

Role

{user.role}

Status

{user.active ? "Active" : "Inactive"}

Member Since

{new Date(user.createdAt).toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" })}

User ID

{user.id}

) }