From fd25b103a23809a1655a4528bfba5480ca93d000 Mon Sep 17 00:00:00 2001 From: Jerry Musico Date: Thu, 5 Feb 2026 08:59:15 +0800 Subject: [PATCH 1/2] add edit page --- app/profile/edit/page.tsx | 247 ++++++++++++++++++++++++++++++++++++++ app/profile/page.tsx | 2 +- 2 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 app/profile/edit/page.tsx diff --git a/app/profile/edit/page.tsx b/app/profile/edit/page.tsx new file mode 100644 index 0000000..148611a --- /dev/null +++ b/app/profile/edit/page.tsx @@ -0,0 +1,247 @@ +"use client"; + +import { useSession } from "next-auth/react"; +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import { User, Mail, Phone, MapPin, ChevronLeft } from "lucide-react"; +import SidebarLayout from "@/Components/SidebarLayout"; +import Link from "next/link"; +import toast from "react-hot-toast"; + +const EditProfilePage = () => { + const { data: session, status } = useSession(); + const router = useRouter(); + const [loading, setLoading] = useState(false); + const [formData, setFormData] = useState({ + name: "", + email: "", + phone: "", + address: "", + city: "", + country: "", + }); + + useEffect(() => { + if (status === "unauthenticated") { + router.push("/login?callbackUrl=/profile/edit"); + return; + } + + if (session?.user) { + setFormData({ + name: session.user.name || "", + email: session.user.email || "", + phone: "", + address: "", + city: "", + country: "", + }); + } + }, [session, status, router]); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + + try { + const response = await fetch("/api/profile/update", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }); + + if (!response.ok) { + throw new Error("Failed to update profile"); + } + + toast.success("Profile updated successfully!"); + router.push("/profile"); + } catch (error) { + toast.error("Failed to update profile. Please try again."); + console.error("Error updating profile:", error); + } finally { + setLoading(false); + } + }; + + if (status === "loading") { + return ( +
+
+
+

Loading...

+
+
+ ); + } + + if (status === "unauthenticated") { + return null; + } + + return ( + + {/* Hero Section */} +
+
+
+ +
+

Edit Profile

+

+ Update your account information +

+
+
+ + {/* Main Content */} +
+ {/* Back Button */} + + + Back to Profile + + + {/* Edit Form */} +
+
+ {/* Full Name */} +
+ +
+ + +
+
+ + {/* Email */} +
+ +
+ + +
+

Email cannot be changed

+
+ + {/* Phone */} +
+ +
+ + +
+
+ + {/* Address */} +
+ +
+ + +
+
+ + {/* City and Country */} +
+
+ + +
+
+ + +
+
+ + {/* Action Buttons */} +
+ + + Cancel + +
+
+
+
+
+ ); +}; + +export default EditProfilePage; diff --git a/app/profile/page.tsx b/app/profile/page.tsx index a997f1e..a3a2ca2 100644 --- a/app/profile/page.tsx +++ b/app/profile/page.tsx @@ -201,7 +201,7 @@ const ProfilePage = () => {

Manage Settings From dc7f274ab9735231f23cd59095024b2c968d3e0a Mon Sep 17 00:00:00 2001 From: Jerry Musico Date: Thu, 5 Feb 2026 09:41:12 +0800 Subject: [PATCH 2/2] update fetch in profile page --- app/api/profile/update/route.ts | 72 +++++++++++++++++++++++++++++++++ app/profile/edit/page.tsx | 39 +++++++++++++++--- lib/auth.ts | 29 ++++++++++--- 3 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 app/api/profile/update/route.ts diff --git a/app/api/profile/update/route.ts b/app/api/profile/update/route.ts new file mode 100644 index 0000000..5b41d63 --- /dev/null +++ b/app/api/profile/update/route.ts @@ -0,0 +1,72 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getServerSession } from "next-auth/next"; +import { revalidatePath } from "next/cache"; +import pool from "@/backend/config/db"; + +export async function POST(request: NextRequest) { + try { + const session = await getServerSession(); + + if (!session || !session.user) { + return NextResponse.json( + { error: "Unauthorized" }, + { status: 401 } + ); + } + + const body = await request.json(); + const { name, phone, address, city, country } = body; + const userEmail = session.user.email; + + // Validate phone number: only digits and max 11 characters + if (phone && !/^\d{0,11}$/.test(phone)) { + return NextResponse.json( + { error: "Phone number must contain only digits" }, + { status: 400 } + ); + } + + // Update user in database + const query = ` + UPDATE users + SET name = $2, + updated_at = CURRENT_TIMESTAMP + WHERE email = $1 + RETURNING user_id, email, name, picture, created_at, updated_at + `; + + const result = await pool.query(query, [userEmail, name]); + + if (result.rows.length === 0) { + return NextResponse.json( + { error: "User not found" }, + { status: 404 } + ); + } + + const updatedUser = result.rows[0]; + console.log("✅ User updated in database:", updatedUser); + + // Revalidate the profile page to clear cache + revalidatePath("/profile"); + revalidatePath("/profile/edit"); + + return NextResponse.json( + { + message: "Profile updated successfully", + data: { + name: updatedUser.name, + email: updatedUser.email, + id: updatedUser.user_id + } + }, + { status: 200 } + ); + } catch (error) { + console.error("❌ Profile update error:", error); + return NextResponse.json( + { error: error instanceof Error ? error.message : "Failed to update profile" }, + { status: 500 } + ); + } +} diff --git a/app/profile/edit/page.tsx b/app/profile/edit/page.tsx index 148611a..6a135e1 100644 --- a/app/profile/edit/page.tsx +++ b/app/profile/edit/page.tsx @@ -41,9 +41,32 @@ const EditProfilePage = () => { const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; + + // For phone field: only allow digits and max 11 characters + if (name === "phone") { + const digitsOnly = value.replace(/\D/g, "").slice(0, 11); + setFormData((prev) => ({ + ...prev, + [name]: digitsOnly, + })); + return; + } + + // Capitalize first letter of each word for name and other text fields + let processedValue = value; + if (name === "name" || name === "address" || name === "city" || name === "country") { + processedValue = value + .split(" ") + .map((word) => { + if (word.length === 0) return word; + return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + }) + .join(" "); + } + setFormData((prev) => ({ ...prev, - [name]: value, + [name]: processedValue, })); }; @@ -60,16 +83,20 @@ const EditProfilePage = () => { body: JSON.stringify(formData), }); + const data = await response.json(); + if (!response.ok) { - throw new Error("Failed to update profile"); + throw new Error(data.error || "Failed to update profile"); } + console.log("✅ Profile updated:", data); toast.success("Profile updated successfully!"); - router.push("/profile"); + + // Navigate to profile page and force full reload to get fresh session + window.location.href = "/profile"; } catch (error) { toast.error("Failed to update profile. Please try again."); - console.error("Error updating profile:", error); - } finally { + console.error("❌ Error updating profile:", error); setLoading(false); } }; @@ -228,7 +255,7 @@ const EditProfilePage = () => { disabled={loading} className="flex-1 px-6 py-3 bg-brand-primary hover:bg-brand-primaryDark text-white font-semibold rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed" > - {loading ? "Saving..." : "Save Changes"} + {loading ? "Saving..." : "Save changes"}