From 325a561b1c5a3276de6e3abcf6845bfd9eb371ab Mon Sep 17 00:00:00 2001 From: ChrQR Date: Mon, 3 Jun 2024 12:51:55 +0200 Subject: [PATCH 1/2] refactored addRegion server action to match new convention for form validation --- src/server/actions/addCountry.ts | 8 +-- src/server/actions/addRegion.ts | 87 ++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/src/server/actions/addCountry.ts b/src/server/actions/addCountry.ts index 2bb4b66..bbd7728 100644 --- a/src/server/actions/addCountry.ts +++ b/src/server/actions/addCountry.ts @@ -7,7 +7,9 @@ import { ZodError, z } from "zod"; import { eq } from "drizzle-orm"; export async function addCountry(prevstate: any, formData: FormData) { + //assign formdaata to variables. const name = (formData.get("name") as string).toLowerCase(); + //check if country already exists const exists = await db .select({ name: countries.name }) .from(countries) @@ -26,10 +28,7 @@ export async function addCountry(prevstate: any, formData: FormData) { name, }); //If the name doesn't exist, add the country to the database abd revalidate the page - await db - .insert(countries) - .values({ name }) - .returning({ name: countries.name }); + await db.insert(countries).values({ name }); revalidatePath("/"); //Return a success message return { @@ -42,6 +41,7 @@ export async function addCountry(prevstate: any, formData: FormData) { } catch (error) { const zodError = error as ZodError; const errorMap = zodError.flatten().fieldErrors; + //Return an error object with the field values and errors. return { message: "error", errors: { diff --git a/src/server/actions/addRegion.ts b/src/server/actions/addRegion.ts index 989538c..5b885fe 100644 --- a/src/server/actions/addRegion.ts +++ b/src/server/actions/addRegion.ts @@ -3,40 +3,63 @@ import { revalidatePath } from "next/cache"; import { db } from "../db"; import { regions } from "../db/schema"; -import { z } from "zod"; - -const schema = z.object({ - countryId: z.string().min(1, "No country selected"), - name: z.string().min(1, "Name is required"), -}); +import { ZodError, z } from "zod"; +import { eq } from "drizzle-orm"; export const addRegion = async (prevstate: any, formData: FormData) => { - const regionData = { - name: (formData.get("name") as string).toLowerCase(), - countryId: formData.get("country") as string, - }; - const newRegion = schema.safeParse(regionData); - if (!newRegion.success) { - return { - message: newRegion.error.issues[0]?.message, - data: newRegion.data, - }; - } - const confirmedRegion = await db - .insert(regions) - .values(newRegion.data) - .onConflictDoNothing() - .returning({ name: regions.name }); - if (!confirmedRegion[0]) { - return { - message: `${newRegion.data.name} already exists`, - data: newRegion.data, - }; - } else { - const message = `${newRegion.data.name} added`; - const errors = newRegion.error; - const data = newRegion.data; + //assign formdaata to variables. + const name = (formData.get("name") as string).toLowerCase(); + const countryId = formData.get("country") as string; + + //check if region already exists in country + const exists = await db + .select({ name: regions.name }) + .from(regions) + .where(eq(regions.countryId, countryId) && eq(regions.name, name)); + + //Define the schema for the form data + const schema = z.object({ + countryId: z.string().min(1, "No country selected"), + name: z + .string() + .min(1, "Name is required") + .refine(() => !exists[0], { + message: `${name} already exists in selected country`, + }), + }); + + //Parse the form data using the schema for validation, and check if the name already exists + try { + schema.parse({ + countryId, + name, + }); + //If the name doesn't exist, add the country to the database abd revalidate the page + await db.insert(regions).values({ countryId, name }); revalidatePath("/"); - return { message, errors, data }; + //Return a success message + return { + message: "success", + errors: undefined, + fieldValues: { + name: "", + countryId: "", + }, + }; + } catch (error) { + const zodError = error as ZodError; + const errorMap = zodError.flatten().fieldErrors; + //Return an error object with the field values and errors. + return { + message: "error", + errors: { + name: errorMap["name"]?.[0] ?? "", + countryId: errorMap["countryId"]?.[0] ?? "", + }, + fieldValues: { + name, + countryId, + }, + }; } }; -- 2.43.4 From 54454aad6dade5b1a542e3c32377170c401b57e6 Mon Sep 17 00:00:00 2001 From: ChrQR Date: Mon, 3 Jun 2024 13:27:28 +0200 Subject: [PATCH 2/2] updated forms to handle errors more descriptive --- .../_components/admin/CreateCountryForm.tsx | 11 ++- .../_components/admin/CreateRegionForm.tsx | 83 +++++++++++++++---- 2 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/app/_components/admin/CreateCountryForm.tsx b/src/app/_components/admin/CreateCountryForm.tsx index 086509a..1a026bf 100644 --- a/src/app/_components/admin/CreateCountryForm.tsx +++ b/src/app/_components/admin/CreateCountryForm.tsx @@ -23,19 +23,22 @@ export default function CreateCountryForm() { }, [formState.message]); return (
-
+
- {formState.message === "success" ? ( + {formState.message !== "" && !formState.errors?.name ? ( ) : ( "" )} - {formState.message === "error" ? ( - + {formState.errors?.name ? ( +
+ + {formState.errors?.name} +
) : ( "" )} diff --git a/src/app/_components/admin/CreateRegionForm.tsx b/src/app/_components/admin/CreateRegionForm.tsx index 2c3ba9b..88f33b4 100644 --- a/src/app/_components/admin/CreateRegionForm.tsx +++ b/src/app/_components/admin/CreateRegionForm.tsx @@ -1,7 +1,7 @@ "use client"; import { Input } from "~/components/ui/input"; import SubmitButton from "../SubmitButton"; -import { useActionState } from "react"; +import { useEffect, useRef } from "react"; import { addRegion } from "~/server/actions/addRegion"; import { Select, @@ -11,6 +11,8 @@ import { SelectValue, } from "~/components/ui/select"; import { useFormState } from "react-dom"; +import clsx from "clsx"; +import { Check, CircleX } from "lucide-react"; interface Country { id: string; @@ -18,25 +20,72 @@ interface Country { } export default function CreateRegionForm(props: { countries: Country[] }) { - const [state, formAction] = useFormState(addRegion, null); + const [formState, formAction] = useFormState(addRegion, { + message: "", + errors: undefined, + fieldValues: { + name: "", + countryId: "", + }, + }); const { countries } = props; + const formRef = useRef(null); + useEffect(() => { + if (formState.message === "success") { + formRef.current?.reset(); + } + }, [formState.message]); return ( - - - + +
+ + {formState.message === "success" && !formState.errors?.name ? ( + + ) : ( + "" + )} + {formState.errors?.name ? ( +
+ + {formState.errors?.name} +
+ ) : ( + "" + )} +
+
+ + {formState.message !== "" && !formState.errors?.countryId ? ( + + ) : ( + "" + )} + {formState.errors?.countryId ? ( + + ) : ( + "" + )} +
+ - {state?.message} ); } -- 2.43.4