Add error handling to upsert role
This commit is contained in:
parent
2e04483343
commit
bae8a2aa3e
|
|
@ -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";
|
||||||
|
|
@ -39,27 +42,28 @@ export interface ModalProps {
|
||||||
*/
|
*/
|
||||||
export default function FormModal(props: ModalProps) {
|
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: {
|
||||||
code: "",
|
code: "",
|
||||||
description: "",
|
description: "",
|
||||||
id: "",
|
id: "",
|
||||||
isActive: false,
|
isActive: false,
|
||||||
name: "",
|
name: "",
|
||||||
},
|
},
|
||||||
validate: zodResolver(roleFormDataSchema),
|
validate: zodResolver(roleFormDataSchema),
|
||||||
validateInputOnChange: false,
|
validateInputOnChange: false,
|
||||||
onValuesChange: (values) => {
|
onValuesChange: (values) => {
|
||||||
console.log(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(() => {
|
useEffect(() => {
|
||||||
if (!props.opened || !props.id) {
|
if (!props.opened || !props.id) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,69 +2,80 @@
|
||||||
|
|
||||||
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(
|
||||||
const isInsert = !data.id;
|
data: RoleFormData
|
||||||
|
): Promise<ServerResponse> {
|
||||||
|
try {
|
||||||
|
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),
|
})
|
||||||
};
|
}
|
||||||
}
|
const roleData = {
|
||||||
|
code: validatedFields.data.code,
|
||||||
|
description: validatedFields.data.description,
|
||||||
|
name: validatedFields.data.name,
|
||||||
|
isActive: validatedFields.data.isActive,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
// Database operation
|
||||||
const roleData = {
|
if (isInsert) {
|
||||||
code: validatedFields.data.code,
|
if (await prisma.role.findFirst({
|
||||||
description: validatedFields.data.description,
|
where: {
|
||||||
name: validatedFields.data.name,
|
code: roleData.code
|
||||||
isActive: validatedFields.data.isActive,
|
}
|
||||||
};
|
})){
|
||||||
|
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
|
// Revalidate the cache
|
||||||
if (isInsert) {
|
revalidatePath(".");
|
||||||
await prisma.role.create({ data: roleData });
|
|
||||||
} else {
|
|
||||||
await prisma.role.update({
|
|
||||||
where: { id: validatedFields.data.id! },
|
|
||||||
data: roleData,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revalidate the cache
|
// Return success message
|
||||||
revalidatePath(".");
|
return {
|
||||||
|
success: true,
|
||||||
// Return success message
|
message: `Role ${validatedFields.data.name} has been successfully ${
|
||||||
return {
|
isInsert ? "created" : "updated"
|
||||||
success: true,
|
}.`,
|
||||||
message: `Role ${validatedFields.data.name} has been successfully ${isInsert ? "created" : "updated"}.`,
|
};
|
||||||
};
|
} catch (error) {
|
||||||
} catch (error) {
|
return handleCatch(error)
|
||||||
console.error('Error updating role data', error);
|
}
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message: "Error updating role data.",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user