diff --git a/actions/user-actions.ts b/actions/user-actions.ts index a986604..7138fb9 100644 --- a/actions/user-actions.ts +++ b/actions/user-actions.ts @@ -1,16 +1,20 @@ "use server"; +import { revalidatePath } from "next/cache"; +import { redirect } from "next/navigation"; 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 { handleApiResponse } from "@/utils/tryCatch"; -export async function VerifyUser(userId: string) { +export async function VerifyUser(_userId: string) { // const user = await prisma.user.findUnique({ // where: { // id: userId, // }, - // include: { + // include: {Rejectuser // atoll: true, // island: true, // }, @@ -87,3 +91,41 @@ export async function getProfileById(userId: string) { return handleApiResponse(response, "getProfilebyId"); } +export async function rejectUser( + _prevState: RejectUserFormState, + formData: FormData +): Promise { + const userId = formData.get("userId") as string; + const rejection_details = formData.get("rejection_details") as string; + const session = await getServerSession(authOptions); + const response = await fetch( + `${process.env.SARLINK_API_BASE_URL}/api/auth/users/${userId}/reject/`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${session?.apiToken}`, + }, + body: JSON.stringify({ rejection_details: rejection_details }), + }, + ); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || errorData.detail || "Failed to reject user"); + } + + // Handle 204 No Content response (successful deletion) + if (response.status === 204) { + revalidatePath("/users"); + redirect("/users"); + } + + revalidatePath("/users"); + const error = await response.json() + return { + message: (error as ApiError).message || (error as ApiError).detail || "An unexpected error occurred.", + fieldErrors: {}, + payload: formData + }; +} diff --git a/app/(dashboard)/users/[userId]/update/page.tsx b/app/(dashboard)/users/[userId]/update/page.tsx new file mode 100644 index 0000000..14328a7 --- /dev/null +++ b/app/(dashboard)/users/[userId]/update/page.tsx @@ -0,0 +1,14 @@ + +export default async function UserUpdate({ + params, +}: { + params: Promise<{ + userId: string; + }>; +}) { + const { userId } = await params; + + return ( +
UserUpdate: {userId}
+ ) +} diff --git a/app/(dashboard)/users/[userId]/verify/page.tsx b/app/(dashboard)/users/[userId]/verify/page.tsx index 5d71178..cfad444 100644 --- a/app/(dashboard)/users/[userId]/verify/page.tsx +++ b/app/(dashboard)/users/[userId]/verify/page.tsx @@ -1,3 +1,14 @@ +import Image from "next/image"; +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 UserRejectDialog from "@/components/user/user-reject-dialog"; +import { UserVerifyDialog } from "@/components/user/user-verify-dialog"; +import { getNationalPerson } from "@/lib/person"; +import { tryCatch } from "@/utils/tryCatch"; + export default async function VerifyUserPage({ params, }: { @@ -6,164 +17,168 @@ export default async function VerifyUserPage({ }>; }) { const userId = (await params).userId; - console.log("userId", userId); - // const dbUser = await prisma.user.findUnique({ - // where: { - // id: userId, - // }, - // include: { - // island: { - // include: { - // atoll: true - // } - // } - // } - // }) + const [error, dbUser] = await tryCatch(getProfileById(userId)); - // const nationalData = await getNationalPerson({ idCard: dbUser?.id_card ?? "" }) + const [nationalDataEror, nationalData] = await tryCatch(getNationalPerson({ idCard: dbUser?.id_card ?? "" })) + if (nationalDataEror) { + console.warn("Error fetching national data:", nationalDataEror); + } + if (error) { + if (error.message === "UNAUTHORIZED") { + redirect("/auth/signin"); + } else { + return ; + } + } - return null; - // return ( - //
- //
- //

Verify user

+ // return
{JSON.stringify(nationalData, null, 2)}
+ const fullName = `${dbUser?.first_name} ${dbUser?.last_name}`; + const nationalDob = nationalData?.dob?.split("T")[0]; + const dbUserDob = new Date(dbUser?.dob).toISOString().split("T")[0]; - //
- // {dbUser && !dbUser?.verified && } - // {dbUser && !dbUser?.verified && } - // {dbUser?.verified && ( - // - // Verified - // - // )} - //
- //
- //
- //
- //

Database Information

- //
- // - // - // - // - // + return ( +
+
+

Verify user

- // - // - //
- //
- //
- //

National Information

- //
- // - // - // - // - // - // - // - //
- // id photo - //
- //
- //
- //
- //
- // ); +
+ {dbUser && !dbUser?.verified && } + {dbUser && !dbUser?.verified && } + {dbUser?.verified && ( + + Verified + + )} +
+
+
+
+

Database Information

+
+ + + + + + + + +
+
+ {( +
+

National Information

+
+ + + + + + + +
+ id photo +
+
+
+ )} +
+
+ ); } diff --git a/components/user/user-reject-dialog.tsx b/components/user/user-reject-dialog.tsx index b010210..c4a22ef 100644 --- a/components/user/user-reject-dialog.tsx +++ b/components/user/user-reject-dialog.tsx @@ -1,6 +1,10 @@ "use client"; -import { Rejectuser } from "@/actions/user-actions"; +import { UserX } from "lucide-react"; +import { useActionState, useEffect, useState } from "react"; +import { toast } from "sonner"; +import { rejectUser } from "@/actions/user-actions"; +// import { Rejectuser } from "@/actions/user-actions"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -12,68 +16,55 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; -import type { User } from "@/lib/types/user"; +import type { UserProfile } from "@/lib/types/user"; import { cn } from "@/lib/utils"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { UserX } from "lucide-react"; -import { useState } from "react"; -import { type SubmitHandler, useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; import { Textarea } from "../ui/textarea"; -const validationSchema = z.object({ - reason: z.string().min(5, { message: "Reason is required" }), -}); -export default function UserRejectDialog({ user }: { user: User }) { - const [disabled, setDisabled] = useState(false); - const [open, setOpen] = useState(false); - const { - register, - handleSubmit, - formState: { errors }, - } = useForm>({ - resolver: zodResolver(validationSchema), - }); - - const onSubmit: SubmitHandler> = (data) => { - setDisabled(true); - console.log(data); - toast.promise( - Rejectuser({ - userId: String(user.id), - reason: data.reason, - }), - { - loading: "Rejecting...", - success: () => { - setDisabled(false); - setOpen((prev) => !prev); - return "Rejected!"; - }, - error: (error) => { - setDisabled(false); - return error.message || "Something went wrong"; - }, - }, - ); - setDisabled(false); +export type RejectUserFormState = { + message: string; + fieldErrors?: { + rejection_details?: string[]; }; + payload?: FormData; +}; + +export const initialState: RejectUserFormState = { + message: "", + fieldErrors: {}, +}; + +export default function UserRejectDialog({ user }: { user: UserProfile }) { + const [open, setOpen] = useState(false); + + const [state, formAction, isPending] = useActionState(rejectUser, initialState); + + useEffect(() => { + if (state.message && state !== initialState) { + if (state.fieldErrors && Object.keys(state.fieldErrors).length > 0) { + toast.error(state.message); + } else if (!state.fieldErrors) { + toast.success("User rejected successfully!"); + setOpen(false); + } else { + toast.error(state.message); + } + } + }, [state]); + return ( - - - Are you sure you want to{" "} - reject this user? + + Are you sure?
  • @@ -92,28 +83,30 @@ export default function UserRejectDialog({ user }: { user: User }) {
  • Phone Number: {user.mobile}
  • -
    +
    -
    -