From 7dbf8802284755a52318d7d9fdf7ada55965913c Mon Sep 17 00:00:00 2001 From: rstubryan Date: Fri, 10 Oct 2025 08:39:34 +0700 Subject: [PATCH] feat(FE-62): add FormActions and FormHeader components for form management --- src/components/helper/form/FormActions.tsx | 82 +++++++++++++++++++ src/components/helper/form/FormHeader.tsx | 24 ++++++ .../movement/form/useMovementFormHandlers.ts | 69 ++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 src/components/helper/form/FormActions.tsx create mode 100644 src/components/helper/form/FormHeader.tsx create mode 100644 src/components/pages/inventory/movement/form/useMovementFormHandlers.ts diff --git a/src/components/helper/form/FormActions.tsx b/src/components/helper/form/FormActions.tsx new file mode 100644 index 00000000..25b586c3 --- /dev/null +++ b/src/components/helper/form/FormActions.tsx @@ -0,0 +1,82 @@ +import { Icon } from '@iconify/react'; +import { FormikContextType } from 'formik'; +import Button from '@/components/Button'; +import { cn } from '@/lib/helper'; + +interface FormActionsProps { + type: 'add' | 'edit' | 'detail'; + formik: FormikContextType; + editUrl?: string; + onDelete?: () => void; +} + +export const FormActions = ({ + type, + formik, + editUrl, + onDelete, +}: FormActionsProps) => { + return ( +
+ {type !== 'add' && onDelete && ( +
+ + {type !== 'edit' && editUrl && ( + + )} +
+ )} + {type !== 'detail' && ( +
+ + +
+ )} +
+ ); +}; diff --git a/src/components/helper/form/FormHeader.tsx b/src/components/helper/form/FormHeader.tsx new file mode 100644 index 00000000..ebc1d7ae --- /dev/null +++ b/src/components/helper/form/FormHeader.tsx @@ -0,0 +1,24 @@ +import Button from '@/components/Button'; +import { Icon } from '@iconify/react'; + +interface FormHeaderProps { + type: 'add' | 'edit' | 'detail'; + title: string; + backUrl: string; +} + +export const FormHeader = ({ type, title, backUrl }: FormHeaderProps) => { + return ( +
+ +

+ {type === 'add' && `Tambah ${title}`} + {type === 'edit' && `Edit ${title}`} + {type === 'detail' && `Detail ${title}`} +

+
+ ); +}; diff --git a/src/components/pages/inventory/movement/form/useMovementFormHandlers.ts b/src/components/pages/inventory/movement/form/useMovementFormHandlers.ts new file mode 100644 index 00000000..f6531bf4 --- /dev/null +++ b/src/components/pages/inventory/movement/form/useMovementFormHandlers.ts @@ -0,0 +1,69 @@ +import { useCallback, useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { toast } from 'react-hot-toast'; +import { useModal } from '@/components/Modal'; +import { MovementApi } from '@/services/api/inventory'; +import { + CreateMovementPayload, + UpdateMovementPayload, +} from '@/types/api/inventory/movement'; +import { isResponseError } from '@/lib/api-helper'; + +export const useMovementFormHandlers = (initialValuesId?: number) => { + const router = useRouter(); + const deleteModal = useModal(); + const [movementFormErrorMessage, setMovementFormErrorMessage] = useState(''); + const [isDeleteLoading, setIsDeleteLoading] = useState(false); + + const createMovementHandler = useCallback( + async (payload: CreateMovementPayload) => { + const res = await MovementApi.create(payload); + if (isResponseError(res)) { + setMovementFormErrorMessage(res.message); + return; + } + toast.success(res?.message as string); + router.push('/inventory/movement'); + }, + [router] + ); + + const updateMovementHandler = useCallback( + async (movementId: number, payload: UpdateMovementPayload) => { + const res = await MovementApi.update(movementId, payload); + if (res?.status === 'error') { + setMovementFormErrorMessage(res.message); + return; + } + toast.success(res?.message as string); + router.refresh(); + router.push('/inventory/movement'); + }, + [router] + ); + + const deleteMovementClickHandler = useCallback(() => { + deleteModal.openModal(); + }, [deleteModal]); + + const confirmationModalDeleteClickHandler = useCallback(async () => { + if (!initialValuesId) return; + + setIsDeleteLoading(true); + await MovementApi.delete(initialValuesId); + deleteModal.closeModal(); + toast.success('Successfully delete Movement!'); + setIsDeleteLoading(false); + router.push('/inventory/movement'); + }, [deleteModal, initialValuesId, router]); + + return { + deleteModal, + movementFormErrorMessage, + isDeleteLoading, + createMovementHandler, + updateMovementHandler, + deleteMovementClickHandler, + confirmationModalDeleteClickHandler, + }; +};