diff --git a/src/app/master-data/product-category/add/page.tsx b/src/app/master-data/product-category/add/page.tsx
new file mode 100644
index 00000000..0993ba7a
--- /dev/null
+++ b/src/app/master-data/product-category/add/page.tsx
@@ -0,0 +1,11 @@
+import ProductCategoryForm from "@/components/pages/master-data/product-category/form/ProductCategoryForm";
+
+const AddProductCategory = () => {
+ return (
+
+ );
+};
+
+export default AddProductCategory;
\ No newline at end of file
diff --git a/src/app/master-data/product-category/detail/edit/page.tsx b/src/app/master-data/product-category/detail/edit/page.tsx
new file mode 100644
index 00000000..6bc10644
--- /dev/null
+++ b/src/app/master-data/product-category/detail/edit/page.tsx
@@ -0,0 +1,47 @@
+'use client';
+
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+
+import ProductCategoryForm from '@/components/pages/master-data/product-category/form/ProductCategoryForm';
+
+import { ProductCategoryApi } from '@/services/api/master-data';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+
+const ProductCategoryEdit = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const productCategoryId = searchParams.get('productCategoryId');
+
+ const { data: productCategory, isLoading: isLoadingProductCategory } = useSWR(
+ productCategoryId,
+ (id: number) => ProductCategoryApi.getSingle(id)
+ );
+
+ if (!productCategoryId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoadingProductCategory && (!productCategory || isResponseError(productCategory))) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoadingProductCategory &&
}
+ {!isLoadingProductCategory && isResponseSuccess(productCategory) && (
+
+ )}
+
+ );
+}
+
+export default ProductCategoryEdit;
\ No newline at end of file
diff --git a/src/app/master-data/product-category/detail/page.tsx b/src/app/master-data/product-category/detail/page.tsx
new file mode 100644
index 00000000..cba06fdb
--- /dev/null
+++ b/src/app/master-data/product-category/detail/page.tsx
@@ -0,0 +1,47 @@
+'use client';
+
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+
+import ProductCategoryForm from '@/components/pages/master-data/product-category/form/ProductCategoryForm';
+
+import { ProductCategoryApi } from '@/services/api/master-data';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+
+const ProductCategoryDetail = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const productCategoryId = searchParams.get('productCategoryId');
+
+ const { data: productCategory, isLoading: isLoadingProductCategory } = useSWR(
+ productCategoryId,
+ (id: number) => ProductCategoryApi.getSingle(id)
+ );
+
+ if (!productCategoryId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoadingProductCategory && (!productCategory || isResponseError(productCategory))) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoadingProductCategory &&
}
+ {!isLoadingProductCategory && isResponseSuccess(productCategory) && (
+
+ )}
+
+ );
+};
+
+export default ProductCategoryDetail;
diff --git a/src/app/master-data/product-category/page.tsx b/src/app/master-data/product-category/page.tsx
new file mode 100644
index 00000000..5ec6d555
--- /dev/null
+++ b/src/app/master-data/product-category/page.tsx
@@ -0,0 +1,11 @@
+import ProductCategoryTable from "@/components/pages/master-data/product-category/ProductCategoryTable";
+
+const ProductCategory = () => {
+ return (
+
+ );
+};
+
+export default ProductCategory;
\ No newline at end of file
diff --git a/src/components/pages/master-data/product-category/form/ProductCategoryForm.tsx b/src/components/pages/master-data/product-category/form/ProductCategoryForm.tsx
new file mode 100644
index 00000000..453670f3
--- /dev/null
+++ b/src/components/pages/master-data/product-category/form/ProductCategoryForm.tsx
@@ -0,0 +1,266 @@
+'use client';
+
+import { useCallback, useEffect, useMemo, useState } from 'react';
+import { useRouter } from 'next/navigation';
+import { useFormik } from 'formik';
+import { toast } from 'react-hot-toast';
+
+import { Icon } from '@iconify/react';
+import Button from '@/components/Button';
+import TextInput from '@/components/input/TextInput';
+import { useModal } from '@/components/Modal';
+import ConfirmationModal from '@/components/modal/ConfirmationModal';
+
+import {
+ ProductCategoryFormSchema,
+ ProductCategoryFormValues,
+ UpdateProductCategoryFormSchema,
+} from '@/components/pages/master-data/product-category/form/ProductCategoryForm.schema';
+import { isResponseError } from '@/lib/api-helper';
+import {
+ ProductCategory,
+ CreateProductCategoryPayload,
+ UpdateProductCategoryPayload,
+} from '@/types/api/master-data/product-category';
+import { ProductCategoryApi } from '@/services/api/master-data';
+import { cn } from '@/lib/helper';
+
+interface ProductCategoryFormProps {
+ type?: 'add' | 'edit' | 'detail';
+ initialValues?: ProductCategory;
+}
+
+const ProductCategoryForm = ({ type = 'add', initialValues }: ProductCategoryFormProps) => {
+ const router = useRouter();
+ const deleteModal = useModal();
+
+ const [formErrorMessage, setFormErrorMessage] = useState('');
+ const [isDeleteLoading, setIsDeleteLoading] = useState(false);
+
+ const createProductCategoryHandler = useCallback(
+ async (payload: CreateProductCategoryPayload) => {
+ const res = await ProductCategoryApi.create(payload);
+
+ if (isResponseError(res)) {
+ setFormErrorMessage(res.message);
+ return;
+ }
+
+ toast.success(res?.message as string);
+ router.push('/master-data/product-category');
+ },
+ [router]
+ );
+
+ const updateProductCategoryHandler = useCallback(
+ async (id: number, payload: UpdateProductCategoryPayload) => {
+ const res = await ProductCategoryApi.update(id, payload);
+
+ if (res?.status === 'error') {
+ setFormErrorMessage(res.message);
+ return;
+ }
+
+ toast.success(res?.message as string);
+ router.refresh();
+ router.push('/master-data/product-category');
+ },
+ [router]
+ );
+
+ const formikInitialValues = useMemo(() => {
+ return {
+ code: initialValues?.code ?? '',
+ name: initialValues?.name ?? '',
+ };
+ }, [initialValues]);
+
+ const formik = useFormik({
+ initialValues: formikInitialValues,
+ validationSchema: type === 'edit' ? UpdateProductCategoryFormSchema : ProductCategoryFormSchema,
+ onSubmit: async (values) => {
+ setFormErrorMessage('');
+
+ const payload: CreateProductCategoryPayload = {
+ code: values.code,
+ name: values.name,
+ };
+
+ switch (type) {
+ case 'add':
+ await createProductCategoryHandler(payload);
+ break;
+ case 'edit':
+ await updateProductCategoryHandler(initialValues?.id as number, payload);
+ break;
+ }
+ },
+ });
+
+ const { setValues: formikSetValues } = formik;
+
+ const deleteProductCategoryClickHandler = () => {
+ deleteModal.openModal();
+ };
+
+ const confirmationModalDeleteClickHandler = async () => {
+ setIsDeleteLoading(true);
+
+ await ProductCategoryApi.delete(initialValues?.id as number);
+
+ deleteModal.closeModal();
+ toast.success('Successfully delete Product Category!');
+ setIsDeleteLoading(false);
+ router.push('/master-data/product-category');
+ };
+
+ useEffect(() => {
+ formikSetValues(formikInitialValues);
+ }, [formikSetValues, formikInitialValues]);
+
+ return (
+ <>
+
+
+
+
+
+ {type === 'add' && 'Tambah Product Category'}
+ {type === 'edit' && 'Edit Product Category'}
+ {type === 'detail' && 'Detail Product Category'}
+
+
+
+
+
+
+ {type !== 'add' && (
+
+ )}
+ >
+ );
+};
+
+export default ProductCategoryForm;
\ No newline at end of file