From e53d4e22b2b073a1deea914d6e520e5f880af16d Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Sat, 4 Oct 2025 14:58:25 +0700 Subject: [PATCH] feat(FE-40,41): create LocationForm component --- .../location/form/LocationForm.tsx | 305 ++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 src/components/pages/master-data/location/form/LocationForm.tsx diff --git a/src/components/pages/master-data/location/form/LocationForm.tsx b/src/components/pages/master-data/location/form/LocationForm.tsx new file mode 100644 index 00000000..50a5f664 --- /dev/null +++ b/src/components/pages/master-data/location/form/LocationForm.tsx @@ -0,0 +1,305 @@ +'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 useSWR from 'swr'; + +import { Icon } from '@iconify/react'; +import Button from '@/components/Button'; +import TextInput from '@/components/input/TextInput'; +import SelectInput, { OptionType } from '@/components/input/SelectInput'; +import { useModal } from '@/components/Modal'; +import ConfirmationModal from '@/components/modal/ConfirmationModal'; + +import { + LocationFormSchema, + LocationFormValues, + UpdateLocationFormSchema, +} from '@/components/pages/master-data/location/form/LocationForm.schema'; +import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; +import { + Location, + CreateLocationPayload, + UpdateLocationPayload, +} from '@/types/api/master-data/location'; +import { AreaApi, LocationApi } from '@/services/api/master-data'; +import { cn } from '@/lib/helper'; + +interface LocationFormProps { + type?: 'add' | 'edit' | 'detail'; + initialValues?: Location; +} + +const LocationForm = ({ type = 'add', initialValues }: LocationFormProps) => { + const router = useRouter(); + const deleteModal = useModal(); + + const [locationFormErrorMessage, setLocationFormErrorMessage] = useState(''); + const [isDeleteLoading, setIsDeleteLoading] = useState(false); + + const createLocationHandler = useCallback( + async (payload: CreateLocationPayload) => { + const createLocationRes = await LocationApi.create(payload); + + if (isResponseError(createLocationRes)) { + setLocationFormErrorMessage(createLocationRes.message); + return; + } + + toast.success(createLocationRes?.message as string); + router.push('/master-data/location'); + }, + [router] + ); + + const updateLocationHandler = useCallback( + async (locationId: number, payload: UpdateLocationPayload) => { + const updateLocationRes = await LocationApi.update(locationId, payload); + + if (updateLocationRes?.status === 'error') { + setLocationFormErrorMessage(updateLocationRes.message); + return; + } + + toast.success(updateLocationRes?.message as string); + router.refresh(); + router.push('/master-data/location'); + }, + [router] + ); + + const formikInitialValues = useMemo(() => { + return { + name: initialValues?.name ?? '', + address: initialValues?.address ?? '', + areaId: initialValues?.area?.id ?? 0, + area: initialValues?.area + ? { + value: initialValues.area.id, + label: initialValues.area.name, + } + : null, + }; + }, [initialValues]); + + const formik = useFormik({ + initialValues: formikInitialValues, + validationSchema: + type === 'edit' ? UpdateLocationFormSchema : LocationFormSchema, + onSubmit: async (values) => { + setLocationFormErrorMessage(''); + + const locationPayload: CreateLocationPayload = { + name: values.name, + address: values.address, + area_id: values.areaId, + }; + + switch (type) { + case 'add': + await createLocationHandler(locationPayload); + break; + + case 'edit': + await updateLocationHandler( + initialValues?.id as number, + locationPayload + ); + break; + } + }, + }); + + const { setValues: formikSetValues } = formik; + + const [areaSelectInputValue, setAreaSelectInputValue] = useState(''); + + const areasUrl = `${AreaApi.basePath}?${new URLSearchParams({ + search: areaSelectInputValue ?? '', + }).toString()}`; + + const { data: areas, isLoading: isLoadingAreas } = useSWR( + areasUrl, + AreaApi.getAllFetcher + ); + + const areaOptions = isResponseSuccess(areas) + ? areas?.data.map((area) => ({ + value: area.id, + label: area.name, + })) + : []; + + const areaChangeHandler = (val: OptionType | OptionType[] | null) => { + formik.setFieldTouched('area', true); + formik.setFieldValue('area', val); + + formik.setFieldTouched('areaId', true); + formik.setFieldValue('areaId', (val as OptionType)?.value); + }; + + const deleteLocationClickHandler = () => { + deleteModal.openModal(); + }; + + const confirmationModalDeleteClickHandler = async () => { + setIsDeleteLoading(true); + + await LocationApi.delete(initialValues?.id as number); + + deleteModal.closeModal(); + toast.success('Successfully delete Location!'); + setIsDeleteLoading(false); + router.push('/master-data/location'); + }; + + useEffect(() => { + formikSetValues(formikInitialValues); + }, [formikSetValues, formikInitialValues]); + + return ( + <> +
+
+ + +

+ {type === 'add' && 'Tambah Location'} + {type === 'edit' && 'Edit Location'} + {type === 'detail' && 'Detail Location'} +

+
+ +
+
+ + + + + +
+ +
+ {type !== 'add' && ( +
+ +
+ )} + + {type !== 'detail' && ( +
+ + + +
+ )} +
+ + {locationFormErrorMessage && ( +
+ + {locationFormErrorMessage} +
+ )} +
+
+ + {type !== 'add' && ( + + )} + + ); +}; + +export default LocationForm;