feat(user-update-form): display field errors in user update form
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 7m52s

This commit is contained in:
2025-07-28 10:03:17 +05:00
parent 21938657ca
commit 17aa65a686
3 changed files with 43 additions and 22 deletions

View File

@ -129,18 +129,19 @@ export type UpdateUserFormState = {
payload?: FormData;
};
export async function updateUser(
_prevState: UpdateUserFormState,
formData: FormData,
): Promise<UpdateUserFormState> {
const userId = formData.get("userId") as string;
const data: Record<string, string | number | boolean> = {};
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(
@ -154,29 +155,42 @@ export async function updateUser(
body: JSON.stringify(data),
},
);
console.log("response in update user action", response);
const json = await response.json();
if (!response.ok) {
const errorData = await response.json();
const isFieldErrorObject =
json &&
typeof json === "object" &&
!Array.isArray(json) &&
Object.values(json).every(
(val) => Array.isArray(val) && val.every((v) => typeof v === "string"),
);
return {
message:
errorData.message ||
errorData.detail ||
"An error occurred while updating the user.",
fieldErrors: errorData.field_errors || {},
json.message ||
json.detail ||
(isFieldErrorObject
? "Please correct the highlighted fields."
: "An error occurred while updating the user."),
fieldErrors: isFieldErrorObject ? json : json.field_errors || {},
payload: formData,
};
}
const updatedUser = (await response.json()) as User;
// Successful update
const updatedUser = json as User;
revalidatePath("/users/[userId]/update", "page");
revalidatePath("/users/[userId]/verify", "page");
return {
...updatedUser,
message: "User updated successfully",
};
}
export async function updateUserAgreement(
_prevState: UpdateUserFormState,
formData: FormData,

View File

@ -1,17 +1,18 @@
"use client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { zodResolver } from "@hookform/resolvers/zod";
import { Loader2 } from "lucide-react";
import { signIn } from "next-auth/react";
import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
import { signIn } from "next-auth/react";
import { useTransition } from "react";
import { type SubmitHandler, useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
const OTPSchema = z.object({
pin: z.string().min(6, {
message: "Your one-time password must be 6 characters.",
@ -59,7 +60,7 @@ export default function VerifyOTPForm({
return (
<form
onSubmit={handleSubmit(onSubmit)}
className="w-full max-w-xs title-bg border rounded-lg shadow my-4"
className="w-full max-w-xs title-bg border-2 border-sarLinkOrange/50 rounded-lg shadow my-4"
>
<div className="grid pb-4 pt-4 gap-4 px-4">
<div className="flex flex-col gap-4">

View File

@ -6,15 +6,6 @@ import { type UpdateUserFormState, updateUser } from "@/actions/user-actions";
import { Button } from "@/components/ui/button";
import { FloatingLabelInput } from "@/components/ui/floating-label";
import type { UserProfile } from "@/lib/types/user";
// import {
// Select,
// SelectContent,
// SelectGroup,
// SelectItem,
// SelectLabel,
// SelectTrigger,
// SelectValue,
// } from "@/components/ui/select";
export default function UserUpdateForm({ user }: { user: UserProfile }) {
const initialState: UpdateUserFormState = {
@ -59,6 +50,21 @@ export default function UserUpdateForm({ user }: { user: UserProfile }) {
Update User Information
</h4>
<div className="border border-dashed border-sarLinkOrange p-4 rounded-lg max-w-2xl">
<div>
{state.fieldErrors && Object.keys(state.fieldErrors).length > 0 ? (
<div className="text-red-500 mb-5 border border-red-500 p-2 rounded">
{Object.entries(state.fieldErrors).map(([field, errors]) => (
<div key={field}>
{errors.map((error, index) => (
<p key={`${index + 1}`}>
Error in {field}: {error}
</p>
))}
</div>
))}
</div>
) : null}
</div>
<fieldset
disabled={isPending}
className="space-y-1 my-2 grid grid-cols-1 md:grid-cols-2 gap-4"