From bae8a2aa3ed198a5a6f8af0be14ca4a58ceb6dfb Mon Sep 17 00:00:00 2001 From: Sianida26 Date: Sun, 28 Jan 2024 05:14:42 +0700 Subject: [PATCH] Add error handling to upsert role --- .../roles/_modals/FormModal/FormModal.tsx | 73 +++++++----- .../dashboard/errors/DashboardError.ts | 4 +- .../dashboard/roles/actions/upsertRole.ts | 111 ++++++++++-------- .../roles/formSchemas/RoleFormData.ts | 4 +- 4 files changed, 108 insertions(+), 84 deletions(-) diff --git a/src/app/dashboard/(auth)/roles/_modals/FormModal/FormModal.tsx b/src/app/dashboard/(auth)/roles/_modals/FormModal/FormModal.tsx index 975b835..3388ae5 100644 --- a/src/app/dashboard/(auth)/roles/_modals/FormModal/FormModal.tsx +++ b/src/app/dashboard/(auth)/roles/_modals/FormModal/FormModal.tsx @@ -1,9 +1,11 @@ /* eslint-disable react-hooks/exhaustive-deps */ +import DashboardError from "@/features/dashboard/errors/DashboardError"; import getRoleById from "@/features/dashboard/roles/actions/getRoleById"; import upsertRole from "@/features/dashboard/roles/actions/upsertRole"; import roleFormDataSchema, { RoleFormData, } from "@/features/dashboard/roles/formSchemas/RoleFormData"; +import withErrorHandling from "@/features/dashboard/utils/withServerAction"; import { showNotification } from "@/utils/notifications"; import { Flex, @@ -17,6 +19,7 @@ import { Checkbox, Skeleton, Fieldset, + Alert, } from "@mantine/core"; import { useForm, zodResolver } from "@mantine/form"; import { useRouter } from "next/navigation"; @@ -39,27 +42,28 @@ export interface ModalProps { */ export default function FormModal(props: ModalProps) { const router = useRouter(); - const [isSubmitting, setSubmitting] = useState(false); - const [isFetching, setFetching] = useState(false); + const [isSubmitting, setSubmitting] = useState(false); + const [isFetching, setFetching] = useState(false); + const [errorMessage, setErrorMessage] = useState(""); - const form = useForm({ - initialValues: { - code: "", - description: "", - id: "", - isActive: false, - name: "", - }, - validate: zodResolver(roleFormDataSchema), - validateInputOnChange: false, - onValuesChange: (values) => { - console.log(values); - }, - }); + const form = useForm({ + initialValues: { + code: "", + description: "", + id: "", + isActive: false, + name: "", + }, + validate: zodResolver(roleFormDataSchema), + validateInputOnChange: false, + onValuesChange: (values) => { + console.log(values); + }, + }); /** - * Fetches role data by ID and populates the form if the modal is opened and an ID is provided. - */ + * Fetches role data by ID and populates the form if the modal is opened and an ID is provided. + */ useEffect(() => { if (!props.opened || !props.id) { return; @@ -93,21 +97,29 @@ export default function FormModal(props: ModalProps) { }; const handleSubmit = (values: RoleFormData) => { - upsertRole(values) + setSubmitting(true); + withErrorHandling(() => upsertRole(values)) .then((response) => { - if (response.success) { - showNotification(response.message, "success"); - return closeModal(); - } else { - form.setErrors(response.errors ?? {}); - if (!response.errors) { - showNotification(response.message, "error"); - } - } + showNotification(response.message!, "success"); + closeModal(); }) .catch((e) => { - //TODO: Handle Error - console.log(e); + if (e instanceof DashboardError) { + if (e.errorCode === "INVALID_FORM_DATA") { + form.setErrors(e.formErrors ?? {}); + } else { + setErrorMessage(`ERROR: ${e.message} (${e.errorCode})`); + } + } else if (e instanceof Error) { + setErrorMessage(`ERROR: ${e.message}`); + } else { + setErrorMessage( + `Unkown error is occured. Please contact administrator` + ); + } + }) + .finally(() => { + setSubmitting(false); }); }; @@ -121,6 +133,7 @@ export default function FormModal(props: ModalProps) { >
+ {errorMessage && {errorMessage}} {/* ID */} {form.values.id ? ( } An object containing the success status, message, and any errors. + * @param data - The data for creating or updating the role. + * @returns An object containing the success status, message, and any errors. */ -export default async function upsertRole(data: RoleFormData) { - const isInsert = !data.id; +export default async function upsertRole( + data: RoleFormData +): Promise { + try { + const isInsert = !data.id; - // Authorization check - const permissionType = isInsert ? "role.create" : "role.update"; - if (!await checkPermission(permissionType)) { - return unauthorized(); - } + // Authorization check + const permissionType = isInsert ? "role.create" : "role.update"; + if (!(await checkPermission(permissionType))) { + return unauthorized(); + } - // Validate form data - const validatedFields = roleFormDataSchema.safeParse(data); - if (!validatedFields.success) { - return { - success: false, - message: "Invalid Form Data", - errors: mapObjectToFirstValue(validatedFields.error.flatten().fieldErrors), - }; - } + // Validate form data + const validatedFields = roleFormDataSchema.safeParse(data); + if (!validatedFields.success) { + throw new DashboardError({ + errorCode: "INVALID_FORM_DATA", + formErrors: mapObjectToFirstValue(validatedFields.error.flatten().fieldErrors) + }) + } + const roleData = { + code: validatedFields.data.code, + description: validatedFields.data.description, + name: validatedFields.data.name, + isActive: validatedFields.data.isActive, + }; - try { - const roleData = { - code: validatedFields.data.code, - description: validatedFields.data.description, - name: validatedFields.data.name, - isActive: validatedFields.data.isActive, - }; + // Database operation + if (isInsert) { + if (await prisma.role.findFirst({ + where: { + code: roleData.code + } + })){ + throw new DashboardError({ + errorCode: "INVALID_FORM_DATA", + formErrors: { + code: "The code is already exists" + } + }) + } + await prisma.role.create({ data: roleData }); + } else { + await prisma.role.update({ + where: { id: validatedFields.data.id! }, + data: roleData, + }); + } - // Database operation - if (isInsert) { - await prisma.role.create({ data: roleData }); - } else { - await prisma.role.update({ - where: { id: validatedFields.data.id! }, - data: roleData, - }); - } + // Revalidate the cache + revalidatePath("."); - // Revalidate the cache - revalidatePath("."); - - // Return success message - return { - success: true, - message: `Role ${validatedFields.data.name} has been successfully ${isInsert ? "created" : "updated"}.`, - }; - } catch (error) { - console.error('Error updating role data', error); - return { - success: false, - message: "Error updating role data.", - }; - } + // Return success message + return { + success: true, + message: `Role ${validatedFields.data.name} has been successfully ${ + isInsert ? "created" : "updated" + }.`, + }; + } catch (error) { + return handleCatch(error) + } } diff --git a/src/features/dashboard/roles/formSchemas/RoleFormData.ts b/src/features/dashboard/roles/formSchemas/RoleFormData.ts index 858776d..68aeba2 100644 --- a/src/features/dashboard/roles/formSchemas/RoleFormData.ts +++ b/src/features/dashboard/roles/formSchemas/RoleFormData.ts @@ -10,8 +10,8 @@ export interface RoleFormData { const roleFormDataSchema = z.object({ id: z.string().nullable(), - name: z.string(), - code: z.string(), + name: z.string().min(1), + code: z.string().min(1), description: z.string(), isActive: z.boolean(), })