Add error handling to upsert role

This commit is contained in:
Sianida26 2024-01-28 05:14:42 +07:00
parent 2e04483343
commit bae8a2aa3e
4 changed files with 108 additions and 84 deletions

View File

@ -1,9 +1,11 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import DashboardError from "@/features/dashboard/errors/DashboardError";
import getRoleById from "@/features/dashboard/roles/actions/getRoleById"; import getRoleById from "@/features/dashboard/roles/actions/getRoleById";
import upsertRole from "@/features/dashboard/roles/actions/upsertRole"; import upsertRole from "@/features/dashboard/roles/actions/upsertRole";
import roleFormDataSchema, { import roleFormDataSchema, {
RoleFormData, RoleFormData,
} from "@/features/dashboard/roles/formSchemas/RoleFormData"; } from "@/features/dashboard/roles/formSchemas/RoleFormData";
import withErrorHandling from "@/features/dashboard/utils/withServerAction";
import { showNotification } from "@/utils/notifications"; import { showNotification } from "@/utils/notifications";
import { import {
Flex, Flex,
@ -17,6 +19,7 @@ import {
Checkbox, Checkbox,
Skeleton, Skeleton,
Fieldset, Fieldset,
Alert,
} from "@mantine/core"; } from "@mantine/core";
import { useForm, zodResolver } from "@mantine/form"; import { useForm, zodResolver } from "@mantine/form";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
@ -41,6 +44,7 @@ export default function FormModal(props: ModalProps) {
const router = useRouter(); const router = useRouter();
const [isSubmitting, setSubmitting] = useState(false); const [isSubmitting, setSubmitting] = useState(false);
const [isFetching, setFetching] = useState(false); const [isFetching, setFetching] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const form = useForm<RoleFormData>({ const form = useForm<RoleFormData>({
initialValues: { initialValues: {
@ -93,21 +97,29 @@ export default function FormModal(props: ModalProps) {
}; };
const handleSubmit = (values: RoleFormData) => { const handleSubmit = (values: RoleFormData) => {
upsertRole(values) setSubmitting(true);
withErrorHandling(() => upsertRole(values))
.then((response) => { .then((response) => {
if (response.success) { showNotification(response.message!, "success");
showNotification(response.message, "success"); closeModal();
return closeModal();
} else {
form.setErrors(response.errors ?? {});
if (!response.errors) {
showNotification(response.message, "error");
}
}
}) })
.catch((e) => { .catch((e) => {
//TODO: Handle Error if (e instanceof DashboardError) {
console.log(e); 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) {
> >
<form onSubmit={form.onSubmit(handleSubmit)}> <form onSubmit={form.onSubmit(handleSubmit)}>
<Stack mt="sm" gap="lg" px="lg"> <Stack mt="sm" gap="lg" px="lg">
{errorMessage && <Alert color="red">{errorMessage}</Alert>}
{/* ID */} {/* ID */}
{form.values.id ? ( {form.values.id ? (
<TextInput <TextInput

View File

@ -4,7 +4,7 @@ export const DashboardErrorCodes = [
"UNAUTHORIZED", "UNAUTHORIZED",
"NOT_FOUND", "NOT_FOUND",
"UNKNOWN_ERROR", "UNKNOWN_ERROR",
"INVALID_FORM_DATA" "INVALID_FORM_DATA",
] as const; ] as const;
interface ErrorOptions { interface ErrorOptions {
@ -14,7 +14,7 @@ interface ErrorOptions {
} }
export default class DashboardError extends Error { export default class DashboardError extends Error {
public readonly errorCode: string; public readonly errorCode: typeof DashboardErrorCodes[number] | string & {};
public readonly formErrors?: {[k: string]: string} public readonly formErrors?: {[k: string]: string}
// public readonly data: object; // public readonly data: object;

View File

@ -2,39 +2,40 @@
import checkPermission from "@/features/auth/tools/checkPermission"; import checkPermission from "@/features/auth/tools/checkPermission";
import roleFormDataSchema, { RoleFormData } from "../formSchemas/RoleFormData"; import roleFormDataSchema, { RoleFormData } from "../formSchemas/RoleFormData";
import { unauthorized } from "@/BaseError";
import mapObjectToFirstValue from "@/utils/mapObjectToFirstValue"; import mapObjectToFirstValue from "@/utils/mapObjectToFirstValue";
import prisma from "@/db"; import prisma from "@/db";
import { revalidatePath } from "next/cache"; import { revalidatePath } from "next/cache";
import ServerResponse from "@/types/Action";
import DashboardError, { handleCatch, unauthorized } from "../../errors/DashboardError";
/** /**
* Upserts a role based on the provided RoleFormData. * Upserts a role based on the provided RoleFormData.
* If the role already exists (determined by `id`), it updates the role; otherwise, it creates a new role. * If the role already exists (determined by `id`), it updates the role; otherwise, it creates a new role.
* Authorization checks are performed based on whether it's a create or update operation. * Authorization checks are performed based on whether it's a create or update operation.
* *
* @param {RoleFormData} data - The data for creating or updating the role. * @param data - The data for creating or updating the role.
* @returns {Promise<object>} An object containing the success status, message, and any errors. * @returns An object containing the success status, message, and any errors.
*/ */
export default async function upsertRole(data: RoleFormData) { export default async function upsertRole(
data: RoleFormData
): Promise<ServerResponse> {
try {
const isInsert = !data.id; const isInsert = !data.id;
// Authorization check // Authorization check
const permissionType = isInsert ? "role.create" : "role.update"; const permissionType = isInsert ? "role.create" : "role.update";
if (!await checkPermission(permissionType)) { if (!(await checkPermission(permissionType))) {
return unauthorized(); return unauthorized();
} }
// Validate form data // Validate form data
const validatedFields = roleFormDataSchema.safeParse(data); const validatedFields = roleFormDataSchema.safeParse(data);
if (!validatedFields.success) { if (!validatedFields.success) {
return { throw new DashboardError({
success: false, errorCode: "INVALID_FORM_DATA",
message: "Invalid Form Data", formErrors: mapObjectToFirstValue(validatedFields.error.flatten().fieldErrors)
errors: mapObjectToFirstValue(validatedFields.error.flatten().fieldErrors), })
};
} }
try {
const roleData = { const roleData = {
code: validatedFields.data.code, code: validatedFields.data.code,
description: validatedFields.data.description, description: validatedFields.data.description,
@ -44,6 +45,18 @@ export default async function upsertRole(data: RoleFormData) {
// Database operation // Database operation
if (isInsert) { 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 }); await prisma.role.create({ data: roleData });
} else { } else {
await prisma.role.update({ await prisma.role.update({
@ -58,13 +71,11 @@ export default async function upsertRole(data: RoleFormData) {
// Return success message // Return success message
return { return {
success: true, success: true,
message: `Role ${validatedFields.data.name} has been successfully ${isInsert ? "created" : "updated"}.`, message: `Role ${validatedFields.data.name} has been successfully ${
isInsert ? "created" : "updated"
}.`,
}; };
} catch (error) { } catch (error) {
console.error('Error updating role data', error); return handleCatch(error)
return {
success: false,
message: "Error updating role data.",
};
} }
} }

View File

@ -10,8 +10,8 @@ export interface RoleFormData {
const roleFormDataSchema = z.object({ const roleFormDataSchema = z.object({
id: z.string().nullable(), id: z.string().nullable(),
name: z.string(), name: z.string().min(1),
code: z.string(), code: z.string().min(1),
description: z.string(), description: z.string(),
isActive: z.boolean(), isActive: z.boolean(),
}) })