Merge branch 'fix/purchase-form' into 'development'

[FIX/FE] Purchase Form & Expense Filter

See merge request mbugroup/lti-web-client!421
This commit is contained in:
Rivaldi A N S
2026-04-22 07:01:46 +00:00
4 changed files with 86 additions and 39 deletions
+46 -8
View File
@@ -51,7 +51,9 @@ type ExpenseTableFilters = {
transactionDate: string;
realizationDate: string;
locationId: string;
locationName: string;
vendorId: string;
vendorName: string;
userId: string;
};
@@ -235,6 +237,7 @@ const ExpensesTable = () => {
setPage,
setPageSize,
toQueryString: getTableFilterQueryString,
reset: resetFilter,
} = useTableFilter<ExpenseTableFilters>({
initial: {
page: 1,
@@ -244,7 +247,9 @@ const ExpensesTable = () => {
transactionDate: '',
realizationDate: '',
locationId: '',
locationName: '',
vendorId: '',
vendorName: '',
userId: '',
},
paramMap: {
@@ -254,7 +259,9 @@ const ExpensesTable = () => {
transactionDate: 'transaction_date',
realizationDate: 'realization_date',
locationId: 'location_id',
locationName: 'location_name',
vendorId: 'vendor_id',
vendorName: 'vendor_name',
userId: 'user_id',
},
@@ -740,20 +747,31 @@ const ExpensesTable = () => {
const handleFilterSubmit = (values: {
transaction_date?: string | null;
realization_date?: string | null;
location_id?: string | null;
vendor_id?: string | null;
location?: { value: number; label: string } | null;
vendor?: { value: number; label: string } | null;
}) => {
updateFilter('transactionDate', values.transaction_date || '');
updateFilter('realizationDate', values.realization_date || '');
updateFilter('locationId', values.location_id || '');
updateFilter('vendorId', values.vendor_id || '');
updateFilter(
'locationId',
values.location?.value ? String(values.location?.value) : ''
);
updateFilter(
'locationName',
values.location?.label ? String(values.location?.label) : ''
);
updateFilter(
'vendorId',
values.vendor?.value ? String(values.vendor?.value) : ''
);
updateFilter(
'vendorName',
values.vendor?.label ? String(values.vendor?.label) : ''
);
};
const handleFilterReset = () => {
updateFilter('transactionDate', '');
updateFilter('realizationDate', '');
updateFilter('locationId', '');
updateFilter('vendorId', '');
resetFilter();
};
// track sorting
@@ -927,6 +945,8 @@ const ExpensesTable = () => {
'search',
'nameSort',
'userId',
'locationName',
'vendorName',
]}
onClick={handleFilterModalOpen}
className='px-3 py-2.5'
@@ -1245,6 +1265,24 @@ const ExpensesTable = () => {
ref={filterModal.ref}
onSubmit={handleFilterSubmit}
onReset={handleFilterReset}
initialValues={{
location:
tableFilterState.locationId && tableFilterState.locationName
? {
value: Number(tableFilterState.locationId),
label: tableFilterState.locationName,
}
: null,
vendor:
tableFilterState.vendorId && tableFilterState.vendorName
? {
value: Number(tableFilterState.vendorId),
label: tableFilterState.vendorName,
}
: null,
realization_date: tableFilterState.realizationDate,
transaction_date: tableFilterState.transactionDate,
}}
/>
</>
);
@@ -3,8 +3,8 @@ import * as yup from 'yup';
export type ExpensesFilterType = {
transaction_date: string | null;
realization_date: string | null;
location_id: string | null;
vendor_id: string | null;
location: { value: number; label: string } | null;
vendor: { value: number; label: string } | null;
};
export const ExpensesFilterSchema = yup.object({
@@ -21,8 +21,18 @@ export const ExpensesFilterSchema = yup.object({
return new Date(value) >= new Date(transaction_date);
}
),
location_id: yup.string().nullable(),
vendor_id: yup.string().nullable(),
location: yup
.object({
value: yup.number().required(),
label: yup.string().required(),
})
.nullable(),
vendor: yup
.object({
value: yup.number().required(),
label: yup.string().required(),
})
.nullable(),
});
export type ExpensesFilterValues = yup.InferType<typeof ExpensesFilterSchema>;
@@ -1,6 +1,6 @@
'use client';
import { RefObject } from 'react';
import { RefObject, useCallback } from 'react';
import { useFormik } from 'formik';
import { Icon } from '@iconify/react';
@@ -39,54 +39,51 @@ const ExpensesFilterModal = ({
setInputValue: setLocationInputValue,
options: locationOptions,
isLoadingOptions: isLoadingLocationOptions,
loadMore: loadMoreLocations,
} = useSelect<Location>(LocationApi.basePath, 'id', 'name');
const {
setInputValue: setVendorInputValue,
options: vendorOptions,
isLoadingOptions: isLoadingVendorOptions,
loadMore: loadMoreVendors,
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name');
const formik = useFormik<ExpensesFilterValues>({
initialValues: initialValues || {
transaction_date: null,
realization_date: null,
location_id: null,
vendor_id: null,
location: null,
vendor: null,
},
validationSchema: ExpensesFilterSchema,
onSubmit: async (values) => {
onSubmit?.(values);
closeModalHandler();
},
onReset: () => {
onReset?.();
closeModalHandler();
},
});
const locationValue = formik.values.location_id
? locationOptions.find(
(opt) => String(opt.value) === formik.values.location_id
) || null
: null;
const { resetForm } = formik;
const vendorValue = formik.values.vendor_id
? vendorOptions.find(
(opt) => String(opt.value) === formik.values.vendor_id
) || null
: null;
const formikResetHandler = useCallback(() => {
resetForm({
values: {
transaction_date: null,
realization_date: null,
location: null,
vendor: null,
},
});
onReset?.();
closeModalHandler();
}, [resetForm, onReset, closeModalHandler]);
const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
const locationId =
val && !Array.isArray(val) ? (String(val.value) as string) : null;
formik.setFieldValue('location_id', locationId);
formik.setFieldValue('location', val as OptionType | null);
};
const vendorChangeHandler = (val: OptionType | OptionType[] | null) => {
const vendorId =
val && !Array.isArray(val) ? (String(val.value) as string) : null;
formik.setFieldValue('vendor_id', vendorId);
formik.setFieldValue('vendor', val as OptionType | null);
};
return (
@@ -98,7 +95,7 @@ const ExpensesFilterModal = ({
>
<form
onSubmit={formik.handleSubmit}
onReset={formik.handleReset}
onReset={formikResetHandler}
className='w-full flex flex-col'
>
{/* Modal Header */}
@@ -160,10 +157,11 @@ const ExpensesFilterModal = ({
label='Lokasi'
placeholder='Pilih Lokasi'
options={locationOptions}
value={locationValue}
value={formik.values.location}
onChange={locationChangeHandler}
onInputChange={setLocationInputValue}
isLoading={isLoadingLocationOptions}
onMenuScrollToBottom={loadMoreLocations}
isClearable
isSearchable={true}
className={{ wrapper: 'w-full' }}
@@ -173,10 +171,11 @@ const ExpensesFilterModal = ({
label='Vendor'
placeholder='Pilih Vendor'
options={vendorOptions}
value={vendorValue}
value={formik.values.vendor}
onChange={vendorChangeHandler}
onInputChange={setVendorInputValue}
isLoading={isLoadingVendorOptions}
onMenuScrollToBottom={loadMoreVendors}
isClearable
isSearchable={true}
className={{ wrapper: 'w-full' }}
@@ -55,7 +55,6 @@ const PurchaseRequestForm = ({
const deleteModal = useModal();
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [, setLocationSelectInputValue] = useState('');
const [selectedPurchaseItems, setSelectedPurchaseItems] = useState<number[]>(
[]
);
@@ -163,6 +162,7 @@ const PurchaseRequestForm = ({
options: locationOptions,
isLoadingOptions: isLoadingLocations,
loadMore: loadMoreLocations,
setInputValue: setLocationSelectInputValue,
} = useSelect(LocationApi.basePath, 'id', 'name', '', {
area_id:
selectedArea != ''