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 (
+
+ );
+ }
+
+ if (status === "unauthenticated") {
+ return null;
+ }
+
+ return (
+
+ {/* Hero Section */}
+
+
+
+
+
+
Edit Profile
+
+ Update your account information
+
+
+
+
+ {/* Main Content */}
+
+ {/* Back Button */}
+
+
+ Back to Profile
+
+
+ {/* Edit Form */}
+
+
+
+ );
+};
+
+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"}