mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
feat: filter improvement
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
npm run format
|
||||
npm run lint
|
||||
npm run typecheck
|
||||
git add .
|
||||
|
||||
@@ -54,6 +54,13 @@ type ExpenseTableFilters = {
|
||||
locationName: string;
|
||||
vendorId: string;
|
||||
vendorName: string;
|
||||
category: string;
|
||||
approvalStatus: string;
|
||||
realizationStatus: string;
|
||||
projectFlockId: string;
|
||||
projectFlockName: string;
|
||||
projectFlockKandangId: string;
|
||||
projectFlockKandangName: string;
|
||||
userId: string;
|
||||
};
|
||||
|
||||
@@ -250,6 +257,13 @@ const ExpensesTable = () => {
|
||||
locationName: '',
|
||||
vendorId: '',
|
||||
vendorName: '',
|
||||
category: '',
|
||||
approvalStatus: '',
|
||||
realizationStatus: '',
|
||||
projectFlockId: '',
|
||||
projectFlockName: '',
|
||||
projectFlockKandangId: '',
|
||||
projectFlockKandangName: '',
|
||||
userId: '',
|
||||
},
|
||||
paramMap: {
|
||||
@@ -262,6 +276,13 @@ const ExpensesTable = () => {
|
||||
locationName: 'location_name',
|
||||
vendorId: 'vendor_id',
|
||||
vendorName: 'vendor_name',
|
||||
category: 'category',
|
||||
approvalStatus: 'approval_status',
|
||||
realizationStatus: 'realization_status',
|
||||
projectFlockId: 'project_flock_id',
|
||||
projectFlockName: 'project_flock_name',
|
||||
projectFlockKandangId: 'project_flock_kandang_id',
|
||||
projectFlockKandangName: 'project_flock_kandang_name',
|
||||
userId: 'user_id',
|
||||
},
|
||||
|
||||
@@ -749,6 +770,11 @@ const ExpensesTable = () => {
|
||||
realization_date?: string | null;
|
||||
location?: { value: number; label: string } | null;
|
||||
vendor?: { value: number; label: string } | null;
|
||||
category?: OptionType<string> | null;
|
||||
approval_status?: OptionType<string> | null;
|
||||
realization_status?: OptionType<string> | null;
|
||||
project_flock?: OptionType<number> | null;
|
||||
project_flock_kandang?: OptionType<number> | null;
|
||||
}) => {
|
||||
updateFilter('transactionDate', values.transaction_date || '');
|
||||
updateFilter('realizationDate', values.realization_date || '');
|
||||
@@ -768,6 +794,24 @@ const ExpensesTable = () => {
|
||||
'vendorName',
|
||||
values.vendor?.label ? String(values.vendor?.label) : ''
|
||||
);
|
||||
updateFilter('category', values.category?.value || '');
|
||||
updateFilter('approvalStatus', values.approval_status?.value || '');
|
||||
updateFilter('realizationStatus', values.realization_status?.value || '');
|
||||
updateFilter(
|
||||
'projectFlockId',
|
||||
values.project_flock?.value ? String(values.project_flock.value) : ''
|
||||
);
|
||||
updateFilter('projectFlockName', values.project_flock?.label || '');
|
||||
updateFilter(
|
||||
'projectFlockKandangId',
|
||||
values.project_flock_kandang?.value
|
||||
? String(values.project_flock_kandang.value)
|
||||
: ''
|
||||
);
|
||||
updateFilter(
|
||||
'projectFlockKandangName',
|
||||
values.project_flock_kandang?.label || ''
|
||||
);
|
||||
};
|
||||
|
||||
const handleFilterReset = () => {
|
||||
@@ -947,6 +991,8 @@ const ExpensesTable = () => {
|
||||
'userId',
|
||||
'locationName',
|
||||
'vendorName',
|
||||
'projectFlockName',
|
||||
'projectFlockKandangName',
|
||||
]}
|
||||
onClick={handleFilterModalOpen}
|
||||
className='px-3 py-2.5'
|
||||
@@ -1282,6 +1328,41 @@ const ExpensesTable = () => {
|
||||
: null,
|
||||
realization_date: tableFilterState.realizationDate,
|
||||
transaction_date: tableFilterState.transactionDate,
|
||||
category: tableFilterState.category
|
||||
? {
|
||||
value: tableFilterState.category,
|
||||
label: tableFilterState.category,
|
||||
}
|
||||
: null,
|
||||
approval_status: tableFilterState.approvalStatus
|
||||
? approvalStatusOptions.find(
|
||||
(item) => item.value === tableFilterState.approvalStatus
|
||||
) || null
|
||||
: null,
|
||||
realization_status: tableFilterState.realizationStatus
|
||||
? [
|
||||
{ value: 'NOT_REALIZED', label: 'Belum Realisasi' },
|
||||
{ value: 'REALIZED', label: 'Sudah Realisasi' },
|
||||
{ value: 'REJECTED', label: 'Ditolak' },
|
||||
].find(
|
||||
(item) => item.value === tableFilterState.realizationStatus
|
||||
) || null
|
||||
: null,
|
||||
project_flock:
|
||||
tableFilterState.projectFlockId && tableFilterState.projectFlockName
|
||||
? {
|
||||
value: Number(tableFilterState.projectFlockId),
|
||||
label: tableFilterState.projectFlockName,
|
||||
}
|
||||
: null,
|
||||
project_flock_kandang:
|
||||
tableFilterState.projectFlockKandangId &&
|
||||
tableFilterState.projectFlockKandangName
|
||||
? {
|
||||
value: Number(tableFilterState.projectFlockKandangId),
|
||||
label: tableFilterState.projectFlockKandangName,
|
||||
}
|
||||
: null,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -5,6 +5,11 @@ export type ExpensesFilterType = {
|
||||
realization_date: string | null;
|
||||
location: { value: number; label: string } | null;
|
||||
vendor: { value: number; label: string } | null;
|
||||
category: { value: string; label: string } | null;
|
||||
approval_status: { value: string; label: string } | null;
|
||||
realization_status: { value: string; label: string } | null;
|
||||
project_flock: { value: number; label: string } | null;
|
||||
project_flock_kandang: { value: number; label: string } | null;
|
||||
};
|
||||
|
||||
export const ExpensesFilterSchema = yup.object({
|
||||
@@ -33,6 +38,36 @@ export const ExpensesFilterSchema = yup.object({
|
||||
label: yup.string().required(),
|
||||
})
|
||||
.nullable(),
|
||||
category: yup
|
||||
.object({
|
||||
value: yup.string().required(),
|
||||
label: yup.string().required(),
|
||||
})
|
||||
.nullable(),
|
||||
approval_status: yup
|
||||
.object({
|
||||
value: yup.string().required(),
|
||||
label: yup.string().required(),
|
||||
})
|
||||
.nullable(),
|
||||
realization_status: yup
|
||||
.object({
|
||||
value: yup.string().required(),
|
||||
label: yup.string().required(),
|
||||
})
|
||||
.nullable(),
|
||||
project_flock: yup
|
||||
.object({
|
||||
value: yup.number().required(),
|
||||
label: yup.string().required(),
|
||||
})
|
||||
.nullable(),
|
||||
project_flock_kandang: 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, useCallback } from 'react';
|
||||
import { RefObject, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
|
||||
import { Icon } from '@iconify/react';
|
||||
@@ -11,8 +11,11 @@ import SelectInput from '@/components/input/SelectInput';
|
||||
|
||||
import { OptionType, useSelect } from '@/components/input/SelectInput';
|
||||
import { LocationApi, SupplierApi } from '@/services/api/master-data';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { Location } from '@/types/api/master-data/location';
|
||||
import { Supplier } from '@/types/api/master-data/supplier';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import {
|
||||
ExpensesFilterSchema,
|
||||
ExpensesFilterValues,
|
||||
@@ -31,10 +34,33 @@ const ExpensesFilterModal = ({
|
||||
onSubmit,
|
||||
onReset,
|
||||
}: ExpensesFilterModalProps) => {
|
||||
const [selectedLocationId, setSelectedLocationId] = useState<string>(
|
||||
initialValues?.location?.value ? String(initialValues.location.value) : ''
|
||||
);
|
||||
const closeModalHandler = () => {
|
||||
ref.current?.close();
|
||||
};
|
||||
|
||||
const categoryOptions = [
|
||||
{ value: 'BOP', label: 'BOP' },
|
||||
{ value: 'NON-BOP', label: 'NON-BOP' },
|
||||
];
|
||||
|
||||
const approvalStatusOptions = [
|
||||
{ value: 'HEAD_AREA', label: 'Approval Head Area' },
|
||||
{ value: 'UNIT_VICE_PRESIDENT', label: 'Approval Unit Vice President' },
|
||||
{ value: 'FINANCE', label: 'Approval Finance' },
|
||||
{ value: 'REALISASI', label: 'Realisasi' },
|
||||
{ value: 'SELESAI', label: 'Selesai' },
|
||||
{ value: 'DITOLAK', label: 'Ditolak' },
|
||||
];
|
||||
|
||||
const realizationStatusOptions = [
|
||||
{ value: 'NOT_REALIZED', label: 'Belum Realisasi' },
|
||||
{ value: 'REALIZED', label: 'Sudah Realisasi' },
|
||||
{ value: 'REJECTED', label: 'Ditolak' },
|
||||
];
|
||||
|
||||
const {
|
||||
setInputValue: setLocationInputValue,
|
||||
options: locationOptions,
|
||||
@@ -49,12 +75,34 @@ const ExpensesFilterModal = ({
|
||||
loadMore: loadMoreVendors,
|
||||
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name');
|
||||
|
||||
const {
|
||||
setInputValue: setProjectFlockInputValue,
|
||||
rawData: projectFlocksRawData,
|
||||
options: projectFlockOptions,
|
||||
isLoadingOptions: isLoadingProjectFlockOptions,
|
||||
loadMore: loadMoreProjectFlocks,
|
||||
} = useSelect<ProjectFlock>(
|
||||
ProjectFlockApi.basePath,
|
||||
'id',
|
||||
'flock_name',
|
||||
'search',
|
||||
{
|
||||
location_id: selectedLocationId || '',
|
||||
}
|
||||
);
|
||||
|
||||
const formik = useFormik<ExpensesFilterValues>({
|
||||
enableReinitialize: true,
|
||||
initialValues: initialValues || {
|
||||
transaction_date: null,
|
||||
realization_date: null,
|
||||
location: null,
|
||||
vendor: null,
|
||||
category: null,
|
||||
approval_status: null,
|
||||
realization_status: null,
|
||||
project_flock: null,
|
||||
project_flock_kandang: null,
|
||||
},
|
||||
validationSchema: ExpensesFilterSchema,
|
||||
onSubmit: async (values) => {
|
||||
@@ -63,6 +111,12 @@ const ExpensesFilterModal = ({
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedLocationId(
|
||||
initialValues?.location?.value ? String(initialValues.location.value) : ''
|
||||
);
|
||||
}, [initialValues?.location]);
|
||||
|
||||
const { resetForm } = formik;
|
||||
|
||||
const formikResetHandler = useCallback(() => {
|
||||
@@ -72,20 +126,51 @@ const ExpensesFilterModal = ({
|
||||
realization_date: null,
|
||||
location: null,
|
||||
vendor: null,
|
||||
category: null,
|
||||
approval_status: null,
|
||||
realization_status: null,
|
||||
project_flock: null,
|
||||
project_flock_kandang: null,
|
||||
},
|
||||
});
|
||||
setSelectedLocationId('');
|
||||
onReset?.();
|
||||
closeModalHandler();
|
||||
}, [resetForm, onReset, closeModalHandler]);
|
||||
|
||||
const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
formik.setFieldValue('location', val as OptionType | null);
|
||||
const value = val as OptionType | null;
|
||||
formik.setFieldValue('location', value);
|
||||
formik.setFieldValue('project_flock', null);
|
||||
formik.setFieldValue('project_flock_kandang', null);
|
||||
setSelectedLocationId(value?.value ? String(value.value) : '');
|
||||
};
|
||||
|
||||
const vendorChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
formik.setFieldValue('vendor', val as OptionType | null);
|
||||
};
|
||||
|
||||
const projectFlockKandangOptions = useMemo(() => {
|
||||
if (
|
||||
!formik.values.project_flock ||
|
||||
!projectFlocksRawData ||
|
||||
!isResponseSuccess(projectFlocksRawData)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const selectedProjectFlock = projectFlocksRawData.data.find(
|
||||
(item) => item.id === formik.values.project_flock?.value
|
||||
);
|
||||
|
||||
return (
|
||||
selectedProjectFlock?.kandangs?.map((item) => ({
|
||||
value: item.project_flock_kandang_id,
|
||||
label: item.name,
|
||||
})) || []
|
||||
);
|
||||
}, [formik.values.project_flock, projectFlocksRawData]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
ref={ref}
|
||||
@@ -180,6 +265,78 @@ const ExpensesFilterModal = ({
|
||||
isSearchable={true}
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Kategori'
|
||||
placeholder='Pilih Kategori'
|
||||
options={categoryOptions}
|
||||
value={formik.values.category}
|
||||
onChange={(val) =>
|
||||
formik.setFieldValue('category', val as OptionType | null)
|
||||
}
|
||||
isClearable
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Status BOP'
|
||||
placeholder='Pilih Status BOP'
|
||||
options={approvalStatusOptions}
|
||||
value={formik.values.approval_status}
|
||||
onChange={(val) =>
|
||||
formik.setFieldValue('approval_status', val as OptionType | null)
|
||||
}
|
||||
isClearable
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Status Pencairan'
|
||||
placeholder='Pilih Status Pencairan'
|
||||
options={realizationStatusOptions}
|
||||
value={formik.values.realization_status}
|
||||
onChange={(val) =>
|
||||
formik.setFieldValue(
|
||||
'realization_status',
|
||||
val as OptionType | null
|
||||
)
|
||||
}
|
||||
isClearable
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Project Flock'
|
||||
placeholder='Pilih Project Flock'
|
||||
options={projectFlockOptions}
|
||||
value={formik.values.project_flock}
|
||||
onChange={(val) => {
|
||||
formik.setFieldValue('project_flock', val as OptionType | null);
|
||||
formik.setFieldValue('project_flock_kandang', null);
|
||||
}}
|
||||
onInputChange={setProjectFlockInputValue}
|
||||
isLoading={isLoadingProjectFlockOptions}
|
||||
onMenuScrollToBottom={loadMoreProjectFlocks}
|
||||
isClearable
|
||||
isSearchable={true}
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Kandang'
|
||||
placeholder='Pilih Kandang'
|
||||
options={projectFlockKandangOptions}
|
||||
value={formik.values.project_flock_kandang}
|
||||
onChange={(val) =>
|
||||
formik.setFieldValue(
|
||||
'project_flock_kandang',
|
||||
val as OptionType | null
|
||||
)
|
||||
}
|
||||
isClearable
|
||||
isDisabled={!formik.values.project_flock}
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Modal Footer */}
|
||||
|
||||
@@ -20,6 +20,8 @@ import { MarketingApi } from '@/services/api/marketing/marketing';
|
||||
import { CustomerApi } from '@/services/api/master-data';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { BaseMarketing, BaseSalesOrder } from '@/types/api/marketing/marketing';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
|
||||
interface MarketingFilterModal {
|
||||
ref: RefObject<HTMLDialogElement | null>;
|
||||
@@ -78,6 +80,19 @@ const MarketingFilterModal = ({
|
||||
has_marketing: 'true',
|
||||
});
|
||||
|
||||
const {
|
||||
options: projectFlockOptions,
|
||||
rawData: projectFlocksRawData,
|
||||
isLoadingOptions: isLoadingProjectFlockOptions,
|
||||
setInputValue: setProjectFlockInputValue,
|
||||
loadMore: loadMoreProjectFlocks,
|
||||
} = useSelect<ProjectFlock>(
|
||||
ProjectFlockApi.basePath,
|
||||
'id',
|
||||
'flock_name',
|
||||
'search'
|
||||
);
|
||||
|
||||
const statusOptions = [
|
||||
...MARKETING_APPROVAL_LINE.map((item) => ({
|
||||
value: item.step_name.split(' ').join('_').toUpperCase(),
|
||||
@@ -91,6 +106,8 @@ const MarketingFilterModal = ({
|
||||
product_ids: [],
|
||||
status: null,
|
||||
customer: null,
|
||||
project_flock: null,
|
||||
project_flock_kandang: null,
|
||||
},
|
||||
validationSchema: MarketingFilterSchema,
|
||||
|
||||
@@ -99,6 +116,9 @@ const MarketingFilterModal = ({
|
||||
product_ids: values.product_ids.map((item) => Number(item.value)),
|
||||
status: values.status?.value.toString() || '',
|
||||
customer_id: Number(values.customer?.value),
|
||||
project_flock_id: Number(values.project_flock?.value) || undefined,
|
||||
project_flock_kandang_id:
|
||||
Number(values.project_flock_kandang?.value) || undefined,
|
||||
};
|
||||
|
||||
onSubmit?.(formattedValues);
|
||||
@@ -126,6 +146,27 @@ const MarketingFilterModal = ({
|
||||
formik.setFieldValue('status', val as OptionType);
|
||||
};
|
||||
|
||||
const projectFlockKandangOptions = useMemo(() => {
|
||||
if (
|
||||
!formik.values.project_flock ||
|
||||
!projectFlocksRawData ||
|
||||
!isResponseSuccess(projectFlocksRawData)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const selectedProjectFlock = projectFlocksRawData.data.find(
|
||||
(item) => item.id === formik.values.project_flock?.value
|
||||
);
|
||||
|
||||
return (
|
||||
selectedProjectFlock?.kandangs?.map((item) => ({
|
||||
value: item.project_flock_kandang_id,
|
||||
label: item.name,
|
||||
})) || []
|
||||
);
|
||||
}, [formik.values.project_flock, projectFlocksRawData]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
ref={ref}
|
||||
@@ -192,6 +233,37 @@ const MarketingFilterModal = ({
|
||||
onInputChange={setCustomersInputValue}
|
||||
onMenuScrollToBottom={loadMoreCustomers}
|
||||
/>
|
||||
<SelectInput
|
||||
label='Project Flock'
|
||||
isClearable
|
||||
placeholder='Pilih Project Flock'
|
||||
options={projectFlockOptions}
|
||||
isLoading={isLoadingProjectFlockOptions}
|
||||
value={formik.values.project_flock}
|
||||
onChange={(val) => {
|
||||
formik.setFieldValue(
|
||||
'project_flock',
|
||||
!Array.isArray(val) ? (val as OptionType<number> | null) : null
|
||||
);
|
||||
formik.setFieldValue('project_flock_kandang', null);
|
||||
}}
|
||||
onInputChange={setProjectFlockInputValue}
|
||||
onMenuScrollToBottom={loadMoreProjectFlocks}
|
||||
/>
|
||||
<SelectInput
|
||||
label='Kandang'
|
||||
isClearable
|
||||
placeholder='Pilih Kandang'
|
||||
options={projectFlockKandangOptions}
|
||||
value={formik.values.project_flock_kandang}
|
||||
onChange={(val) =>
|
||||
formik.setFieldValue(
|
||||
'project_flock_kandang',
|
||||
!Array.isArray(val) ? (val as OptionType<number> | null) : null
|
||||
)
|
||||
}
|
||||
isDisabled={!formik.values.project_flock}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Modal Footer */}
|
||||
|
||||
@@ -224,6 +224,8 @@ const MarketingTable = () => {
|
||||
product_ids: '',
|
||||
status: '',
|
||||
customer_id: '',
|
||||
project_flock_id: '',
|
||||
project_flock_kandang_id: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
@@ -231,6 +233,8 @@ const MarketingTable = () => {
|
||||
product_ids: 'product_ids',
|
||||
status: 'status',
|
||||
customer_id: 'customer_id',
|
||||
project_flock_id: 'project_flock_id',
|
||||
project_flock_kandang_id: 'project_flock_kandang_id',
|
||||
},
|
||||
|
||||
persist: true,
|
||||
@@ -260,6 +264,18 @@ const MarketingTable = () => {
|
||||
values.customer_id ? values.customer_id.toString() : '',
|
||||
true
|
||||
);
|
||||
updateFilter(
|
||||
'project_flock_id',
|
||||
values.project_flock_id ? values.project_flock_id.toString() : '',
|
||||
true
|
||||
);
|
||||
updateFilter(
|
||||
'project_flock_kandang_id',
|
||||
values.project_flock_kandang_id
|
||||
? values.project_flock_kandang_id.toString()
|
||||
: '',
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] =
|
||||
@@ -269,6 +285,8 @@ const MarketingTable = () => {
|
||||
updateFilter('product_ids', '', true);
|
||||
updateFilter('status', '', true);
|
||||
updateFilter('customer_id', '', true);
|
||||
updateFilter('project_flock_id', '', true);
|
||||
updateFilter('project_flock_kandang_id', '', true);
|
||||
};
|
||||
|
||||
const approveClickHandler = () => {
|
||||
|
||||
@@ -5,10 +5,14 @@ export const MarketingFilterSchema = object({
|
||||
product_ids: array().of(mixed<OptionType<number>>().required()).required(),
|
||||
status: mixed<OptionType<string>>().nullable(),
|
||||
customer: mixed<OptionType<number>>().nullable(),
|
||||
project_flock: mixed<OptionType<number>>().nullable(),
|
||||
project_flock_kandang: mixed<OptionType<number>>().nullable(),
|
||||
});
|
||||
|
||||
export type MarketingFilterFormValues = {
|
||||
product_ids: OptionType<number>[];
|
||||
status: OptionType<string> | null;
|
||||
customer: OptionType<number> | null;
|
||||
project_flock: OptionType<number> | null;
|
||||
project_flock_kandang: OptionType<number> | null;
|
||||
};
|
||||
|
||||
@@ -319,15 +319,23 @@ const RecordingTable = () => {
|
||||
search: '',
|
||||
areaFilter: '',
|
||||
locationFilter: '',
|
||||
projectFlockFilter: '',
|
||||
kandangFilter: '',
|
||||
projectFlockKandangFilter: '',
|
||||
approvalStatusFilter: '',
|
||||
projectFlockCategoryFilter: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
pageSize: 'limit',
|
||||
search: 'search',
|
||||
areaFilter: 'area_id',
|
||||
locationFilter: 'location_id',
|
||||
projectFlockFilter: 'project_flock_id',
|
||||
kandangFilter: 'kandang_id',
|
||||
projectFlockKandangFilter: 'project_flock_kandang_id',
|
||||
approvalStatusFilter: 'approval_status',
|
||||
projectFlockCategoryFilter: 'project_flock_category',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -356,26 +364,38 @@ const RecordingTable = () => {
|
||||
initialValues: {
|
||||
area_id: null,
|
||||
location_id: null,
|
||||
project_flock_id: null,
|
||||
kandang_id: null,
|
||||
project_flock_kandang_id: null,
|
||||
approval_status: null,
|
||||
project_flock_category: null,
|
||||
},
|
||||
validationSchema: RecordingFilterSchema,
|
||||
onSubmit: (values, { setSubmitting }) => {
|
||||
updateFilter('areaFilter', values.area_id || '');
|
||||
updateFilter('locationFilter', values.location_id || '');
|
||||
updateFilter('projectFlockFilter', values.project_flock_id || '');
|
||||
updateFilter('kandangFilter', values.kandang_id || '');
|
||||
updateFilter(
|
||||
'projectFlockKandangFilter',
|
||||
values.project_flock_kandang_id || ''
|
||||
);
|
||||
updateFilter('approvalStatusFilter', values.approval_status || '');
|
||||
updateFilter(
|
||||
'projectFlockCategoryFilter',
|
||||
values.project_flock_category || ''
|
||||
);
|
||||
filterModal.closeModal();
|
||||
setSubmitting(false);
|
||||
},
|
||||
onReset: () => {
|
||||
updateFilter('areaFilter', '');
|
||||
updateFilter('locationFilter', '');
|
||||
updateFilter('projectFlockFilter', '');
|
||||
updateFilter('kandangFilter', '');
|
||||
updateFilter('projectFlockKandangFilter', '');
|
||||
updateFilter('approvalStatusFilter', '');
|
||||
updateFilter('projectFlockCategoryFilter', '');
|
||||
},
|
||||
});
|
||||
|
||||
@@ -537,6 +557,7 @@ const RecordingTable = () => {
|
||||
|
||||
formik.setFieldValue('area_id', areaId);
|
||||
formik.setFieldValue('location_id', null);
|
||||
formik.setFieldValue('project_flock_id', null);
|
||||
formik.setFieldValue('kandang_id', null);
|
||||
formik.setFieldValue('project_flock_kandang_id', null);
|
||||
|
||||
@@ -556,6 +577,7 @@ const RecordingTable = () => {
|
||||
const locationId = location?.value ? String(location.value) : null;
|
||||
|
||||
formik.setFieldValue('location_id', locationId);
|
||||
formik.setFieldValue('project_flock_id', null);
|
||||
formik.setFieldValue('kandang_id', null);
|
||||
formik.setFieldValue('project_flock_kandang_id', null);
|
||||
|
||||
@@ -570,7 +592,11 @@ const RecordingTable = () => {
|
||||
const handleFilterProjectFlockChange = useCallback(
|
||||
(val: OptionType | OptionType[] | null) => {
|
||||
const projectFlock = val as OptionType | null;
|
||||
const projectFlockId = projectFlock?.value
|
||||
? String(projectFlock.value)
|
||||
: null;
|
||||
|
||||
formik.setFieldValue('project_flock_id', projectFlockId);
|
||||
formik.setFieldValue('kandang_id', null);
|
||||
formik.setFieldValue('project_flock_kandang_id', null);
|
||||
|
||||
@@ -625,6 +651,36 @@ const RecordingTable = () => {
|
||||
);
|
||||
}, [formik.values.kandang_id, kandangOptions]);
|
||||
|
||||
const recordingApprovalStatusOptions: OptionType<string>[] = [
|
||||
{ value: 'CREATED', label: 'Pengajuan' },
|
||||
{ value: 'UPDATED', label: 'Diperbarui' },
|
||||
{ value: 'APPROVED', label: 'Disetujui' },
|
||||
{ value: 'REJECTED', label: 'Ditolak' },
|
||||
];
|
||||
|
||||
const projectFlockCategoryOptions: OptionType<string>[] = [
|
||||
{ value: 'GROWING', label: 'Growing' },
|
||||
{ value: 'LAYING', label: 'Laying' },
|
||||
];
|
||||
|
||||
const approvalStatusValue = useMemo(() => {
|
||||
if (!formik.values.approval_status) return null;
|
||||
return (
|
||||
recordingApprovalStatusOptions.find(
|
||||
(opt) => opt.value === formik.values.approval_status
|
||||
) || null
|
||||
);
|
||||
}, [formik.values.approval_status]);
|
||||
|
||||
const projectFlockCategoryValue = useMemo(() => {
|
||||
if (!formik.values.project_flock_category) return null;
|
||||
return (
|
||||
projectFlockCategoryOptions.find(
|
||||
(opt) => opt.value === formik.values.project_flock_category
|
||||
) || null
|
||||
);
|
||||
}, [formik.values.project_flock_category]);
|
||||
|
||||
// ===== HANDLE FILTER MODAL OPEN =====
|
||||
const handleFilterModalOpen = () => {
|
||||
filterModal.openModal();
|
||||
@@ -1607,6 +1663,36 @@ const RecordingTable = () => {
|
||||
isDisabled={!filterProjectFlock}
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Kategori'
|
||||
placeholder='Pilih Kategori'
|
||||
options={projectFlockCategoryOptions}
|
||||
value={projectFlockCategoryValue}
|
||||
onChange={(val) => {
|
||||
formik.setFieldValue(
|
||||
'project_flock_category',
|
||||
!Array.isArray(val) && val ? String(val.value) : null
|
||||
);
|
||||
}}
|
||||
isClearable
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Status Approval'
|
||||
placeholder='Pilih Status Approval'
|
||||
options={recordingApprovalStatusOptions}
|
||||
value={approvalStatusValue}
|
||||
onChange={(val) => {
|
||||
formik.setFieldValue(
|
||||
'approval_status',
|
||||
!Array.isArray(val) && val ? String(val.value) : null
|
||||
);
|
||||
}}
|
||||
isClearable
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Modal Footer */}
|
||||
@@ -1631,11 +1717,7 @@ const RecordingTable = () => {
|
||||
<Button
|
||||
type='submit'
|
||||
className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold'
|
||||
disabled={
|
||||
!formik.isValid ||
|
||||
formik.isSubmitting ||
|
||||
!formik.values.kandang_id
|
||||
}
|
||||
disabled={!formik.isValid || formik.isSubmitting}
|
||||
>
|
||||
Apply Filter
|
||||
</Button>
|
||||
|
||||
@@ -3,13 +3,19 @@ import { string, object } from 'yup';
|
||||
export const RecordingFilterSchema = object().shape({
|
||||
area_id: string().nullable(),
|
||||
location_id: string().nullable(),
|
||||
project_flock_id: string().nullable(),
|
||||
kandang_id: string().nullable(),
|
||||
project_flock_kandang_id: string().nullable(),
|
||||
approval_status: string().nullable(),
|
||||
project_flock_category: string().nullable(),
|
||||
});
|
||||
|
||||
export type RecordingFilterType = {
|
||||
area_id: string | null;
|
||||
location_id: string | null;
|
||||
project_flock_id: string | null;
|
||||
kandang_id: string | null;
|
||||
project_flock_kandang_id: string | null;
|
||||
approval_status: string | null;
|
||||
project_flock_category: string | null;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { RefObject, useState, useEffect } from 'react';
|
||||
import { RefObject, useState, useEffect, useMemo } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
@@ -9,12 +9,20 @@ import Modal from '@/components/Modal';
|
||||
import Button from '@/components/Button';
|
||||
import DateInput from '@/components/input/DateInput';
|
||||
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
|
||||
import SelectInput from '@/components/input/SelectInput';
|
||||
|
||||
import { OptionType, useSelect } from '@/components/input/SelectInput';
|
||||
import { PurchaseFilter } from '@/types/api/purchase/purchase';
|
||||
import { AreaApi, LocationApi, SupplierApi } from '@/services/api/master-data';
|
||||
import { ProductCategory } from '@/types/api/master-data/product-category';
|
||||
import { ProductCategoryApi } from '@/services/api/master-data';
|
||||
import { Area } from '@/types/api/master-data/area';
|
||||
import { Location } from '@/types/api/master-data/location';
|
||||
import { Supplier } from '@/types/api/master-data/supplier';
|
||||
import { PURCHASE_ORDER_APPROVAL_LINE } from '@/config/approval-line';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
interface PurchaseFilterModalProps {
|
||||
ref: RefObject<HTMLDialogElement | null>;
|
||||
@@ -73,32 +81,112 @@ const PurchaseFilterModal = ({
|
||||
'search'
|
||||
);
|
||||
|
||||
const [selectedAreaId, setSelectedAreaId] = useState('');
|
||||
const [selectedLocationId, setSelectedLocationId] = useState('');
|
||||
|
||||
const {
|
||||
setInputValue: setSupplierInputValue,
|
||||
options: supplierOptions,
|
||||
isLoadingOptions: isLoadingSupplierOptions,
|
||||
loadMore: loadMoreSuppliers,
|
||||
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name', 'search');
|
||||
|
||||
const {
|
||||
setInputValue: setAreaInputValue,
|
||||
options: areaOptions,
|
||||
isLoadingOptions: isLoadingAreaOptions,
|
||||
loadMore: loadMoreAreas,
|
||||
} = useSelect<Area>(AreaApi.basePath, 'id', 'name', 'search');
|
||||
|
||||
const {
|
||||
setInputValue: setLocationInputValue,
|
||||
options: locationOptions,
|
||||
isLoadingOptions: isLoadingLocationOptions,
|
||||
loadMore: loadMoreLocations,
|
||||
} = useSelect<Location>(LocationApi.basePath, 'id', 'name', 'search', {
|
||||
area_id: selectedAreaId || '',
|
||||
});
|
||||
|
||||
const {
|
||||
setInputValue: setProjectFlockInputValue,
|
||||
options: projectFlockOptions,
|
||||
rawData: projectFlocksRawData,
|
||||
isLoadingOptions: isLoadingProjectFlockOptions,
|
||||
loadMore: loadMoreProjectFlocks,
|
||||
} = useSelect<ProjectFlock>(
|
||||
ProjectFlockApi.basePath,
|
||||
'id',
|
||||
'flock_name',
|
||||
'search',
|
||||
{
|
||||
location_id: selectedLocationId || '',
|
||||
}
|
||||
);
|
||||
|
||||
const formik = useFormik<{
|
||||
poDate: string;
|
||||
category: { label: string; value: number }[];
|
||||
status: { label: string; value: string }[];
|
||||
supplier: OptionType<number> | null;
|
||||
area: OptionType<number> | null;
|
||||
location: OptionType<number> | null;
|
||||
project_flock: OptionType<number> | null;
|
||||
project_flock_kandang: OptionType<number> | null;
|
||||
}>({
|
||||
initialValues: {
|
||||
poDate: '',
|
||||
category: [],
|
||||
status: [],
|
||||
supplier: null,
|
||||
area: null,
|
||||
location: null,
|
||||
project_flock: null,
|
||||
project_flock_kandang: null,
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
const formattedValues = {
|
||||
...values,
|
||||
category: values.category.map((item) => String(item.value)),
|
||||
status: values.status.map((item) => String(item.value)),
|
||||
supplier_id: values.supplier?.value,
|
||||
area_id: values.area?.value,
|
||||
location_id: values.location?.value,
|
||||
project_flock_id: values.project_flock?.value,
|
||||
project_flock_kandang_id: values.project_flock_kandang?.value,
|
||||
};
|
||||
|
||||
onSubmit?.(formattedValues);
|
||||
closeModalHandler();
|
||||
},
|
||||
onReset: () => {
|
||||
setSelectedAreaId('');
|
||||
setSelectedLocationId('');
|
||||
onReset?.();
|
||||
closeModalHandler();
|
||||
},
|
||||
});
|
||||
|
||||
const projectFlockKandangOptions = useMemo(() => {
|
||||
if (
|
||||
!formik.values.project_flock ||
|
||||
!projectFlocksRawData ||
|
||||
!isResponseSuccess(projectFlocksRawData)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const selectedProjectFlock = projectFlocksRawData.data.find(
|
||||
(item) => item.id === formik.values.project_flock?.value
|
||||
);
|
||||
|
||||
return (
|
||||
selectedProjectFlock?.kandangs?.map((item) => ({
|
||||
value: item.project_flock_kandang_id,
|
||||
label: item.name,
|
||||
})) || []
|
||||
);
|
||||
}, [formik.values.project_flock, projectFlocksRawData]);
|
||||
|
||||
const productCategoryChangeHandler = (
|
||||
val: OptionType | OptionType[] | null
|
||||
) => {
|
||||
@@ -172,6 +260,108 @@ const PurchaseFilterModal = ({
|
||||
value: item.step_name,
|
||||
}))}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Vendor'
|
||||
placeholder='Pilih Vendor'
|
||||
value={formik.values.supplier}
|
||||
onChange={(val) =>
|
||||
formik.setFieldValue(
|
||||
'supplier',
|
||||
!Array.isArray(val)
|
||||
? (val as OptionType<number> | null)
|
||||
: null
|
||||
)
|
||||
}
|
||||
options={supplierOptions}
|
||||
isLoading={isLoadingSupplierOptions}
|
||||
onInputChange={setSupplierInputValue}
|
||||
onMenuScrollToBottom={loadMoreSuppliers}
|
||||
isClearable
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Area'
|
||||
placeholder='Pilih Area'
|
||||
value={formik.values.area}
|
||||
onChange={(val) => {
|
||||
const nextValue = !Array.isArray(val)
|
||||
? (val as OptionType<number> | null)
|
||||
: null;
|
||||
formik.setFieldValue('area', nextValue);
|
||||
formik.setFieldValue('location', null);
|
||||
formik.setFieldValue('project_flock', null);
|
||||
formik.setFieldValue('project_flock_kandang', null);
|
||||
setSelectedAreaId(
|
||||
nextValue?.value ? String(nextValue.value) : ''
|
||||
);
|
||||
setSelectedLocationId('');
|
||||
}}
|
||||
options={areaOptions}
|
||||
isLoading={isLoadingAreaOptions}
|
||||
onInputChange={setAreaInputValue}
|
||||
onMenuScrollToBottom={loadMoreAreas}
|
||||
isClearable
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Lokasi'
|
||||
placeholder='Pilih Lokasi'
|
||||
value={formik.values.location}
|
||||
onChange={(val) => {
|
||||
const nextValue = !Array.isArray(val)
|
||||
? (val as OptionType<number> | null)
|
||||
: null;
|
||||
formik.setFieldValue('location', nextValue);
|
||||
formik.setFieldValue('project_flock', null);
|
||||
formik.setFieldValue('project_flock_kandang', null);
|
||||
setSelectedLocationId(
|
||||
nextValue?.value ? String(nextValue.value) : ''
|
||||
);
|
||||
}}
|
||||
options={locationOptions}
|
||||
isLoading={isLoadingLocationOptions}
|
||||
onInputChange={setLocationInputValue}
|
||||
onMenuScrollToBottom={loadMoreLocations}
|
||||
isClearable
|
||||
isDisabled={!formik.values.area}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Project Flock'
|
||||
placeholder='Pilih Project Flock'
|
||||
value={formik.values.project_flock}
|
||||
onChange={(val) => {
|
||||
const nextValue = !Array.isArray(val)
|
||||
? (val as OptionType<number> | null)
|
||||
: null;
|
||||
formik.setFieldValue('project_flock', nextValue);
|
||||
formik.setFieldValue('project_flock_kandang', null);
|
||||
}}
|
||||
options={projectFlockOptions}
|
||||
isLoading={isLoadingProjectFlockOptions}
|
||||
onInputChange={setProjectFlockInputValue}
|
||||
onMenuScrollToBottom={loadMoreProjectFlocks}
|
||||
isClearable
|
||||
isDisabled={!formik.values.location}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Kandang'
|
||||
placeholder='Pilih Kandang'
|
||||
value={formik.values.project_flock_kandang}
|
||||
onChange={(val) =>
|
||||
formik.setFieldValue(
|
||||
'project_flock_kandang',
|
||||
!Array.isArray(val)
|
||||
? (val as OptionType<number> | null)
|
||||
: null
|
||||
)
|
||||
}
|
||||
options={projectFlockKandangOptions}
|
||||
isClearable
|
||||
isDisabled={!formik.values.project_flock}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -213,6 +213,11 @@ const PurchaseTable = () => {
|
||||
po_date: '',
|
||||
approval_status: '',
|
||||
product_category_id: '',
|
||||
supplier_id: '',
|
||||
area_id: '',
|
||||
location_id: '',
|
||||
project_flock_id: '',
|
||||
project_flock_kandang_id: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
@@ -220,6 +225,11 @@ const PurchaseTable = () => {
|
||||
po_date: 'po_date',
|
||||
approval_status: 'approval_status',
|
||||
product_category_id: 'product_category_id',
|
||||
supplier_id: 'supplier_id',
|
||||
area_id: 'area_id',
|
||||
location_id: 'location_id',
|
||||
project_flock_id: 'project_flock_id',
|
||||
project_flock_kandang_id: 'project_flock_kandang_id',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -467,12 +477,36 @@ const PurchaseTable = () => {
|
||||
updateFilter('po_date', values.poDate);
|
||||
updateFilter('product_category_id', values.category.join(','));
|
||||
updateFilter('approval_status', values.status.join(','));
|
||||
updateFilter(
|
||||
'supplier_id',
|
||||
values.supplier_id ? String(values.supplier_id) : ''
|
||||
);
|
||||
updateFilter('area_id', values.area_id ? String(values.area_id) : '');
|
||||
updateFilter(
|
||||
'location_id',
|
||||
values.location_id ? String(values.location_id) : ''
|
||||
);
|
||||
updateFilter(
|
||||
'project_flock_id',
|
||||
values.project_flock_id ? String(values.project_flock_id) : ''
|
||||
);
|
||||
updateFilter(
|
||||
'project_flock_kandang_id',
|
||||
values.project_flock_kandang_id
|
||||
? String(values.project_flock_kandang_id)
|
||||
: ''
|
||||
);
|
||||
};
|
||||
|
||||
const filterResetHandler = () => {
|
||||
updateFilter('po_date', '');
|
||||
updateFilter('product_category_id', '');
|
||||
updateFilter('approval_status', '');
|
||||
updateFilter('supplier_id', '');
|
||||
updateFilter('area_id', '');
|
||||
updateFilter('location_id', '');
|
||||
updateFilter('project_flock_id', '');
|
||||
updateFilter('project_flock_kandang_id', '');
|
||||
};
|
||||
|
||||
const resetExportProgressForm = useCallback(() => {
|
||||
|
||||
+2
@@ -97,6 +97,8 @@ export type MarketingFilter = {
|
||||
product_ids: number[];
|
||||
status: string;
|
||||
customer_id: number;
|
||||
project_flock_id?: number;
|
||||
project_flock_kandang_id?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+5
@@ -149,4 +149,9 @@ export type PurchaseFilter = {
|
||||
poDate: string;
|
||||
category: string[];
|
||||
status: string[];
|
||||
supplier_id?: number;
|
||||
area_id?: number;
|
||||
location_id?: number;
|
||||
project_flock_id?: number;
|
||||
project_flock_kandang_id?: number;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user