mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-24 15:25:46 +00:00
feat(FE-65): enhance MovementForm with initial values handling and refactor components
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
|
import { Movement } from '@/types/api/inventory/movement';
|
||||||
|
|
||||||
export const MovementFormSchema = Yup.object({
|
export const MovementFormSchema = Yup.object({
|
||||||
alasan_transfer: Yup.string().required('Alasan transfer wajib diisi!'),
|
alasan_transfer: Yup.string().required('Alasan transfer wajib diisi!'),
|
||||||
@@ -66,3 +67,43 @@ export const MovementFormSchema = Yup.object({
|
|||||||
export const UpdateMovementFormSchema = MovementFormSchema;
|
export const UpdateMovementFormSchema = MovementFormSchema;
|
||||||
|
|
||||||
export type MovementFormValues = Yup.InferType<typeof MovementFormSchema>;
|
export type MovementFormValues = Yup.InferType<typeof MovementFormSchema>;
|
||||||
|
|
||||||
|
export const getMovementFormInitialValues = (
|
||||||
|
initialValues?: Movement
|
||||||
|
): MovementFormValues => ({
|
||||||
|
alasan_transfer: initialValues?.alasan_transfer ?? '',
|
||||||
|
tanggal_transfer: initialValues?.tanggal_transfer ?? '',
|
||||||
|
warehouse_asal: initialValues?.warehouse_asal
|
||||||
|
? {
|
||||||
|
value: initialValues.warehouse_asal.id,
|
||||||
|
label: initialValues.warehouse_asal.name,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
warehouse_asal_id: initialValues?.warehouse_asal?.id ?? 0,
|
||||||
|
warehouse_tujuan: initialValues?.warehouse_tujuan
|
||||||
|
? {
|
||||||
|
value: initialValues.warehouse_tujuan.id,
|
||||||
|
label: initialValues.warehouse_tujuan.name,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
warehouse_tujuan_id: initialValues?.warehouse_tujuan?.id ?? 0,
|
||||||
|
product:
|
||||||
|
initialValues?.product?.map((p) => ({
|
||||||
|
product: { value: p.product.id, label: p.product.name },
|
||||||
|
product_id: p.product.id,
|
||||||
|
qty_product: p.qty_product,
|
||||||
|
})) ?? [],
|
||||||
|
ekspedisi:
|
||||||
|
initialValues?.ekspedisi?.map((e) => ({
|
||||||
|
product: { value: e.product_id, label: '' },
|
||||||
|
product_id: e.product_id,
|
||||||
|
qty: e.qty,
|
||||||
|
supplier: { value: e.supplier.id, label: e.supplier.name },
|
||||||
|
supplier_id: e.supplier.id,
|
||||||
|
plat_nomor: e.plat_nomor,
|
||||||
|
no_surat_jalan: e.no_surat_jalan,
|
||||||
|
dokumen: e.dokumen,
|
||||||
|
biaya_ekspedisi: e.biaya_ekspedisi,
|
||||||
|
nama_sopir: e.nama_sopir,
|
||||||
|
})) ?? [],
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,36 +1,33 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
import { FieldArray, FormikProvider, useFormik } from 'formik';
|
import { FieldArray, FormikProvider, useFormik } from 'formik';
|
||||||
import { toast } from 'react-hot-toast';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import TextInput from '@/components/input/TextInput';
|
import TextInput from '@/components/input/TextInput';
|
||||||
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
||||||
import { useModal } from '@/components/Modal';
|
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
|
import { FormHeader } from '@/components/helper/form/FormHeader';
|
||||||
|
import { FormActions } from '@/components/helper/form/FormActions';
|
||||||
|
import {
|
||||||
|
CreateMovementPayload,
|
||||||
|
Movement,
|
||||||
|
} from '@/types/api/inventory/movement';
|
||||||
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
import {
|
import {
|
||||||
MovementFormSchema,
|
MovementFormSchema,
|
||||||
MovementFormValues,
|
MovementFormValues,
|
||||||
UpdateMovementFormSchema,
|
UpdateMovementFormSchema,
|
||||||
|
getMovementFormInitialValues,
|
||||||
} from '@/components/pages/inventory/movement/form/MovementForm.schema';
|
} from '@/components/pages/inventory/movement/form/MovementForm.schema';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { useMovementFormHandlers } from './useMovementFormHandlers';
|
||||||
import {
|
|
||||||
Movement,
|
|
||||||
CreateMovementPayload,
|
|
||||||
UpdateMovementPayload,
|
|
||||||
} from '@/types/api/inventory/movement';
|
|
||||||
import {
|
import {
|
||||||
ProductApi,
|
ProductApi,
|
||||||
WarehouseApi,
|
|
||||||
SupplierApi,
|
SupplierApi,
|
||||||
|
WarehouseApi,
|
||||||
} from '@/services/api/master-data';
|
} from '@/services/api/master-data';
|
||||||
import { MovementApi } from '@/services/api/inventory';
|
|
||||||
import { cn } from '@/lib/helper';
|
|
||||||
|
|
||||||
interface MovementFormProps {
|
interface MovementFormProps {
|
||||||
type?: 'add' | 'edit' | 'detail';
|
type?: 'add' | 'edit' | 'detail';
|
||||||
@@ -38,77 +35,20 @@ interface MovementFormProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||||
const router = useRouter();
|
const [, setMovementFormErrorMessage] = useState('');
|
||||||
const deleteModal = useModal();
|
|
||||||
|
|
||||||
const [movementFormErrorMessage, setMovementFormErrorMessage] = useState('');
|
const {
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
deleteModal,
|
||||||
|
movementFormErrorMessage,
|
||||||
const createMovementHandler = useCallback(
|
isDeleteLoading,
|
||||||
async (payload: CreateMovementPayload) => {
|
createMovementHandler,
|
||||||
const res = await MovementApi.create(payload);
|
updateMovementHandler,
|
||||||
if (isResponseError(res)) {
|
deleteMovementClickHandler,
|
||||||
setMovementFormErrorMessage(res.message);
|
confirmationModalDeleteClickHandler,
|
||||||
return;
|
} = useMovementFormHandlers(initialValues?.id);
|
||||||
}
|
|
||||||
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 formikInitialValues = useMemo<MovementFormValues>(
|
const formikInitialValues = useMemo<MovementFormValues>(
|
||||||
() => ({
|
() => getMovementFormInitialValues(initialValues),
|
||||||
alasan_transfer: initialValues?.alasan_transfer ?? '',
|
|
||||||
tanggal_transfer: initialValues?.tanggal_transfer ?? '',
|
|
||||||
warehouse_asal: initialValues?.warehouse_asal
|
|
||||||
? {
|
|
||||||
value: initialValues.warehouse_asal.id,
|
|
||||||
label: initialValues.warehouse_asal.name,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
warehouse_asal_id: initialValues?.warehouse_asal?.id ?? 0,
|
|
||||||
warehouse_tujuan: initialValues?.warehouse_tujuan
|
|
||||||
? {
|
|
||||||
value: initialValues.warehouse_tujuan.id,
|
|
||||||
label: initialValues.warehouse_tujuan.name,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
warehouse_tujuan_id: initialValues?.warehouse_tujuan?.id ?? 0,
|
|
||||||
product:
|
|
||||||
initialValues?.product?.map((p) => ({
|
|
||||||
product: { value: p.product.id, label: p.product.name },
|
|
||||||
product_id: p.product.id,
|
|
||||||
qty_product: p.qty_product,
|
|
||||||
})) ?? [],
|
|
||||||
ekspedisi:
|
|
||||||
initialValues?.ekspedisi?.map((e) => ({
|
|
||||||
product: { value: e.product_id, label: '' }, // Need to fetch product details
|
|
||||||
product_id: e.product_id,
|
|
||||||
qty: e.qty,
|
|
||||||
supplier: { value: e.supplier.id, label: e.supplier.name },
|
|
||||||
supplier_id: e.supplier.id,
|
|
||||||
plat_nomor: e.plat_nomor,
|
|
||||||
no_surat_jalan: e.no_surat_jalan,
|
|
||||||
dokumen: e.dokumen,
|
|
||||||
biaya_ekspedisi: e.biaya_ekspedisi,
|
|
||||||
nama_sopir: e.nama_sopir,
|
|
||||||
})) ?? [],
|
|
||||||
}),
|
|
||||||
[initialValues]
|
[initialValues]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -185,19 +125,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
|||||||
? suppliers?.data.map((s) => ({ value: s.id, label: s.name }))
|
? suppliers?.data.map((s) => ({ value: s.id, label: s.name }))
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const deleteMovementClickHandler = () => {
|
|
||||||
deleteModal.openModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
|
||||||
setIsDeleteLoading(true);
|
|
||||||
await MovementApi.delete(initialValues?.id as number);
|
|
||||||
deleteModal.closeModal();
|
|
||||||
toast.success('Successfully delete Movement!');
|
|
||||||
setIsDeleteLoading(false);
|
|
||||||
router.push('/inventory/movement');
|
|
||||||
};
|
|
||||||
|
|
||||||
const { setValues: formikSetValues } = formik;
|
const { setValues: formikSetValues } = formik;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -207,22 +134,11 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className='w-full max-w-xl'>
|
<section className='w-full max-w-xl'>
|
||||||
<header className='flex flex-col gap-4'>
|
<FormHeader
|
||||||
<Button
|
type={type}
|
||||||
href='/inventory/movement'
|
title='Movement'
|
||||||
variant='link'
|
backUrl='/inventory/movement'
|
||||||
className='w-fit p-0 text-primary'
|
/>
|
||||||
>
|
|
||||||
<Icon icon='uil:arrow-left' width={24} height={24} />
|
|
||||||
Kembali
|
|
||||||
</Button>
|
|
||||||
<h1 className='text-2xl font-bold text-center'>
|
|
||||||
{type === 'add' && 'Tambah Movement'}
|
|
||||||
{type === 'edit' && 'Edit Movement'}
|
|
||||||
{type === 'detail' && 'Detail Movement'}
|
|
||||||
</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<FormikProvider value={formik}>
|
<FormikProvider value={formik}>
|
||||||
<form
|
<form
|
||||||
onSubmit={formik.handleSubmit}
|
onSubmit={formik.handleSubmit}
|
||||||
@@ -330,11 +246,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
|||||||
<FieldArray name='product'>
|
<FieldArray name='product'>
|
||||||
{({ push, remove }) => (
|
{({ push, remove }) => (
|
||||||
<>
|
<>
|
||||||
{typeof formik.errors.product === 'string' && (
|
|
||||||
<div className='text-error'>
|
|
||||||
{formik.errors.product}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<table className='table'>
|
<table className='table'>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -433,11 +344,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
|||||||
<FieldArray name='ekspedisi'>
|
<FieldArray name='ekspedisi'>
|
||||||
{({ push, remove }) => (
|
{({ push, remove }) => (
|
||||||
<>
|
<>
|
||||||
{typeof formik.errors.ekspedisi === 'string' && (
|
|
||||||
<div className='text-error'>
|
|
||||||
{formik.errors.ekspedisi}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<table className='table'>
|
<table className='table'>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -652,67 +558,16 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action buttons */}
|
{/* Action buttons */}
|
||||||
<div className='flex flex-row justify-between gap-2 flex-wrap'>
|
<FormActions<MovementFormValues>
|
||||||
{type !== 'add' && (
|
type={type}
|
||||||
<div className='flex flex-row justify-start gap-2'>
|
formik={formik}
|
||||||
<Button
|
editUrl={
|
||||||
type='button'
|
initialValues
|
||||||
color='error'
|
? `/inventory/movement/detail/edit/?movementId=${initialValues.id}`
|
||||||
onClick={deleteMovementClickHandler}
|
: undefined
|
||||||
className='px-4'
|
}
|
||||||
>
|
onDelete={deleteMovementClickHandler}
|
||||||
<Icon
|
/>
|
||||||
icon='material-symbols:delete-outline-rounded'
|
|
||||||
width={24}
|
|
||||||
height={24}
|
|
||||||
className='justify-start text-sm'
|
|
||||||
/>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
{type !== 'edit' && (
|
|
||||||
<Button
|
|
||||||
type='button'
|
|
||||||
color='warning'
|
|
||||||
href={`/inventory/movement/detail/edit/?movementId=${initialValues?.id}`}
|
|
||||||
className='px-4'
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
icon='material-symbols:edit-outline'
|
|
||||||
width={24}
|
|
||||||
height={24}
|
|
||||||
className='justify-start text-sm'
|
|
||||||
/>
|
|
||||||
Edit
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{type !== 'detail' && (
|
|
||||||
<div
|
|
||||||
className={cn('flex flex-row justify-end gap-2', {
|
|
||||||
'w-full': type === 'add',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
type='reset'
|
|
||||||
color='warning'
|
|
||||||
className='px-4'
|
|
||||||
onClick={formik.handleReset}
|
|
||||||
>
|
|
||||||
Reset
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type='submit'
|
|
||||||
color='primary'
|
|
||||||
className='px-4'
|
|
||||||
isLoading={formik.isSubmitting}
|
|
||||||
disabled={!formik.isValid || formik.isSubmitting}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{movementFormErrorMessage && (
|
{movementFormErrorMessage && (
|
||||||
<div role='alert' className='alert alert-error'>
|
<div role='alert' className='alert alert-error'>
|
||||||
|
|||||||
Reference in New Issue
Block a user