From 8fac07bb606c736a09dedb45b31e6f88fb136e0b Mon Sep 17 00:00:00 2001 From: i701 Date: Mon, 14 Jul 2025 21:37:51 +0500 Subject: [PATCH] =?UTF-8?q?feat(user):=20implement=20user=20update=20funct?= =?UTF-8?q?ionality=20and=20enhance=20verification=20page=20UI=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- actions/user-actions.ts | 79 ++++++--- .../users/[userId]/update/page.tsx | 37 ++++- .../users/[userId]/verify/page.tsx | 17 +- components/user/user-update-form.tsx | 151 ++++++++++++++++++ queries/users.ts | 17 ++ 5 files changed, 276 insertions(+), 25 deletions(-) create mode 100644 components/user/user-update-form.tsx diff --git a/actions/user-actions.ts b/actions/user-actions.ts index 7138fb9..bfad649 100644 --- a/actions/user-actions.ts +++ b/actions/user-actions.ts @@ -6,7 +6,7 @@ import { getServerSession } from "next-auth"; import { authOptions } from "@/app/auth"; import type { RejectUserFormState } from "@/components/user/user-reject-dialog"; import type { ApiError } from "@/lib/backend-types"; -import type { User, UserProfile } from "@/lib/types/user"; +import type { User } from "@/lib/types/user"; import { handleApiResponse } from "@/utils/tryCatch"; export async function VerifyUser(_userId: string) { @@ -73,24 +73,6 @@ export async function getProfile() { return handleApiResponse(response, "getProfile"); } - - -export async function getProfileById(userId: string) { - const session = await getServerSession(authOptions); - const response = await fetch( - `${process.env.SARLINK_API_BASE_URL}/api/auth/users/${userId}/`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Token ${session?.apiToken}`, - }, - }, - ); - - return handleApiResponse(response, "getProfilebyId"); -} - export async function rejectUser( _prevState: RejectUserFormState, formData: FormData @@ -129,3 +111,62 @@ export async function rejectUser( payload: formData }; } + +export type UpdateUserFormState = { + message: string; + fieldErrors?: { + id_card?: string[]; + first_name?: string[]; + last_name?: string[]; + dob?: string[]; + mobile?: string[]; + address?: string[]; + }; + payload?: FormData; +}; + + +export async function updateUser( + _prevState: UpdateUserFormState, + formData: FormData +): Promise { + const userId = formData.get("userId") as string; + const data: Record = {}; + for (const [key, value] of formData.entries()) { + if (value !== undefined && value !== "") { + data[key] = typeof value === "number" ? value : String(value); + } + } + console.log("data in update user action", data) + + const session = await getServerSession(authOptions); + const response = await fetch( + `${process.env.SARLINK_API_BASE_URL}/api/auth/users/${userId}/update/`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${session?.apiToken}`, + }, + body: JSON.stringify(data), + }, + ); + console.log("response in update user action", response) + + if (!response.ok) { + const errorData = await response.json(); + return { + message: errorData.message || errorData.detail || "An error occurred while updating the user.", + fieldErrors: errorData.field_errors || {}, + payload: formData, + } + } + + const updatedUser = await response.json() as User; + revalidatePath("/users/[userId]/update", "page"); + revalidatePath("/users/[userId]/verify", "page"); + return { + ...updatedUser, + message: "User updated successfully", + }; +} \ No newline at end of file diff --git a/app/(dashboard)/users/[userId]/update/page.tsx b/app/(dashboard)/users/[userId]/update/page.tsx index 14328a7..2d08730 100644 --- a/app/(dashboard)/users/[userId]/update/page.tsx +++ b/app/(dashboard)/users/[userId]/update/page.tsx @@ -1,3 +1,19 @@ +import { redirect } from "next/navigation"; +import { getServerSession } from "next-auth"; +import { authOptions } from "@/app/auth"; +import ClientErrorMessage from "@/components/client-error-message"; +import UserUpdateForm from "@/components/user/user-update-form"; +import { getProfileById } from "@/queries/users"; +import { tryCatch } from "@/utils/tryCatch"; +// import { +// Select, +// SelectContent, +// SelectGroup, +// SelectItem, +// SelectLabel, +// SelectTrigger, +// SelectValue, +// } from "@/components/ui/select"; export default async function UserUpdate({ params, @@ -7,8 +23,25 @@ export default async function UserUpdate({ }>; }) { const { userId } = await params; + const session = await getServerSession(authOptions); + if (!session?.user?.is_admin) return null + const [error, user] = await tryCatch(getProfileById(userId)); + + + if (error) { + if (error.message === "UNAUTHORIZED") { + redirect("/auth/signin"); + } else { + return ; + } + } return ( -
UserUpdate: {userId}
- ) +
+
+

Verify user

+
+ +
+ ); } diff --git a/app/(dashboard)/users/[userId]/verify/page.tsx b/app/(dashboard)/users/[userId]/verify/page.tsx index cfad444..318855b 100644 --- a/app/(dashboard)/users/[userId]/verify/page.tsx +++ b/app/(dashboard)/users/[userId]/verify/page.tsx @@ -1,12 +1,15 @@ +import { PencilIcon } from "lucide-react"; import Image from "next/image"; +import Link from "next/link"; import { redirect } from "next/navigation"; -import { getProfileById } from "@/actions/user-actions"; import ClientErrorMessage from "@/components/client-error-message"; import InputReadOnly from "@/components/input-read-only"; import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; import UserRejectDialog from "@/components/user/user-reject-dialog"; import { UserVerifyDialog } from "@/components/user/user-verify-dialog"; import { getNationalPerson } from "@/lib/person"; +import { getProfileById } from "@/queries/users"; import { tryCatch } from "@/utils/tryCatch"; export default async function VerifyUserPage({ @@ -44,6 +47,12 @@ export default async function VerifyUserPage({
{dbUser && !dbUser?.verified && } {dbUser && !dbUser?.verified && } + + + {dbUser?.verified && ( Verified @@ -54,7 +63,7 @@ export default async function VerifyUserPage({

Database Information

-
+

National Information

-
+
{ + if (state.message) { + if (state.fieldErrors) { + Object.entries(state.fieldErrors).forEach(([field, errors]) => { + errors.forEach((error) => { + toast.error(`Error in ${field}: ${error}`); + }); + }); + } else { + toast.success("Success", { + description: "User updated successfully", + closeButton: true, + }); + } + } + }, [state]); + + return ( +
+ +
+

+ Update User Information +

+
+
+ + + + + + {/* + */} + + +
+ + +
+
+
+ ); +} diff --git a/queries/users.ts b/queries/users.ts index c786390..1e1758c 100644 --- a/queries/users.ts +++ b/queries/users.ts @@ -29,3 +29,20 @@ export async function getUsers(params: ParamProps) { return handleApiResponse>(response, "getUsers"); } + + +export async function getProfileById(userId: string) { + const session = await getServerSession(authOptions); + const response = await fetch( + `${process.env.SARLINK_API_BASE_URL}/api/auth/users/${userId}/`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${session?.apiToken}`, + }, + }, + ); + + return handleApiResponse(response, "getProfilebyId"); +} \ No newline at end of file