refactor(FE): Refactor filter schema and form handling for

PurchasesPerSupplier
This commit is contained in:
rstubryan
2026-02-12 10:55:40 +07:00
parent e23b53d797
commit 322b519def
2 changed files with 106 additions and 141 deletions
@@ -1,88 +1,38 @@
import { OptionType } from '@/components/input/SelectInput';
import * as yup from 'yup';
export type PurchasesPerSupplierFilterType = {
start_date: string | null | undefined;
end_date: string | null | undefined;
area_ids: OptionType[] | null | undefined;
supplier_ids: OptionType[] | null | undefined;
product_ids: OptionType[] | null | undefined;
product_category_ids: OptionType[] | null | undefined;
filter_by: OptionType | null | undefined;
sort_by: OptionType | null | undefined;
start_date: string | null;
end_date: string | null;
area_ids: string | null;
supplier_ids: string | null;
product_ids: string | null;
product_category_ids: string | null;
filter_by: string | null;
sort_by: string | null;
};
export const PurchasesPerSupplierFilterSchema: yup.ObjectSchema<PurchasesPerSupplierFilterType> =
yup.object({
start_date: yup.string().optional().nullable(),
end_date: yup
.string()
.optional()
.nullable()
.test(
'is-greater-than-start',
'Tanggal akhir tidak boleh masa lampau',
function (value) {
const { start_date } = this.parent;
if (!start_date || !value) return true;
return new Date(value) >= new Date(start_date);
}
),
area_ids: yup
.array()
.of(
yup.object({
value: yup.mixed<string | number>().required(),
label: yup.string().required(),
})
)
.optional()
.nullable(),
supplier_ids: yup
.array()
.of(
yup.object({
value: yup.mixed<string | number>().required(),
label: yup.string().required(),
})
)
.optional()
.nullable(),
product_ids: yup
.array()
.of(
yup.object({
value: yup.mixed<string | number>().required(),
label: yup.string().required(),
})
)
.optional()
.nullable(),
product_category_ids: yup
.array()
.of(
yup.object({
value: yup.mixed<string | number>().required(),
label: yup.string().required(),
})
)
.optional()
.nullable(),
filter_by: yup
.object({
value: yup.mixed<string | number>().required(),
label: yup.string().required(),
})
.optional()
.nullable(),
sort_by: yup
.object({
value: yup.mixed<string | number>().required(),
label: yup.string().required(),
})
.optional()
.nullable(),
});
export const PurchasesPerSupplierFilterSchema = yup.object({
start_date: yup.string().optional().nullable(),
end_date: yup
.string()
.optional()
.nullable()
.test(
'is-greater-than-start',
'Tanggal akhir tidak boleh masa lampau',
function (value) {
const { start_date } = this.parent;
if (!start_date || !value) return true;
return new Date(value) >= new Date(start_date);
}
),
area_ids: yup.string().nullable(),
supplier_ids: yup.string().nullable(),
product_ids: yup.string().nullable(),
product_category_ids: yup.string().nullable(),
filter_by: yup.string().nullable(),
sort_by: yup.string().nullable(),
});
export type PurchasesPerSupplierFilterValues = yup.InferType<
typeof PurchasesPerSupplierFilterSchema
@@ -125,21 +125,14 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
validationSchema: PurchasesPerSupplierFilterSchema,
onSubmit: (values, { setSubmitting }) => {
setFilterParams({
start_date: values.start_date?.toString() || undefined,
end_date: values.end_date?.toString() || undefined,
area_id:
values.area_ids?.map((v) => String(v.value)).join(',') || undefined,
supplier_id:
values.supplier_ids?.map((v) => String(v.value)).join(',') ||
undefined,
product_id:
values.product_ids?.map((v) => String(v.value)).join(',') ||
undefined,
product_category_id:
values.product_category_ids?.map((v) => String(v.value)).join(',') ||
undefined,
filter_by: values.filter_by?.value?.toString() || undefined,
sort_by: values.sort_by?.value?.toString() || undefined,
start_date: values.start_date || undefined,
end_date: values.end_date || undefined,
area_id: values.area_ids || undefined,
supplier_id: values.supplier_ids || undefined,
product_id: values.product_ids || undefined,
product_category_id: values.product_category_ids || undefined,
filter_by: values.filter_by || undefined,
sort_by: values.sort_by || undefined,
});
filterModal.closeModal();
setIsSubmitted(true);
@@ -220,6 +213,48 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
[formik, dateErrorShown]
);
// ===== DERIVED VALUES =====
const areaIdsValue = useMemo(() => {
if (!formik.values.area_ids) return [];
const ids = formik.values.area_ids.split(',');
return areaOptions.filter((opt) => ids.includes(String(opt.value)));
}, [formik.values.area_ids, areaOptions]);
const supplierIdsValue = useMemo(() => {
if (!formik.values.supplier_ids) return [];
const ids = formik.values.supplier_ids.split(',');
return supplierOptions.filter((opt) => ids.includes(String(opt.value)));
}, [formik.values.supplier_ids, supplierOptions]);
const productIdsValue = useMemo(() => {
if (!formik.values.product_ids) return [];
const ids = formik.values.product_ids.split(',');
return productOptions.filter((opt) => ids.includes(String(opt.value)));
}, [formik.values.product_ids, productOptions]);
const productCategoryIdsValue = useMemo(() => {
if (!formik.values.product_category_ids) return [];
const ids = formik.values.product_category_ids.split(',');
return productCategoryOptions.filter((opt) =>
ids.includes(String(opt.value))
);
}, [formik.values.product_category_ids, productCategoryOptions]);
const filterByValue = useMemo(() => {
if (!formik.values.filter_by) return null;
return (
dataTypeOptions.find((opt) => opt.value === formik.values.filter_by) ||
null
);
}, [formik.values.filter_by, dataTypeOptions]);
const sortByValue = useMemo(() => {
if (!formik.values.sort_by) return null;
return (
sortByOptions.find((opt) => opt.value === formik.values.sort_by) || null
);
}, [formik.values.sort_by, sortByOptions]);
// ===== ACTIVE FILTERS COUNT =====
const activeFiltersCount = useMemo(() => {
let count = 0;
@@ -875,17 +910,13 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Area'
placeholder='Pilih Area'
options={areaOptions}
value={
(formik.values.area_ids as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined) || []
}
value={areaIdsValue}
onChange={(val) => {
formik.setFieldValue(
'area_ids',
Array.isArray(val) ? val : val ? [val] : null
Array.isArray(val) && val.length > 0
? val.map((v) => String(v.value)).join(',')
: null
);
}}
isLoading={isLoadingAreas}
@@ -898,17 +929,13 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Supplier'
placeholder='Pilih Supplier'
options={supplierOptions}
value={
(formik.values.supplier_ids as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined) || []
}
value={supplierIdsValue}
onChange={(val) => {
formik.setFieldValue(
'supplier_ids',
Array.isArray(val) ? val : val ? [val] : null
Array.isArray(val) && val.length > 0
? val.map((v) => String(v.value)).join(',')
: null
);
}}
isLoading={isLoadingSuppliers}
@@ -921,17 +948,13 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Produk'
placeholder='Pilih Produk'
options={productOptions}
value={
(formik.values.product_ids as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined) || []
}
value={productIdsValue}
onChange={(val) => {
formik.setFieldValue(
'product_ids',
Array.isArray(val) ? val : val ? [val] : null
Array.isArray(val) && val.length > 0
? val.map((v) => String(v.value)).join(',')
: null
);
}}
isLoading={isLoadingProducts}
@@ -944,17 +967,13 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Kategori Produk'
placeholder='Pilih Kategori Produk'
options={productCategoryOptions}
value={
(formik.values.product_category_ids as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined) || []
}
value={productCategoryIdsValue}
onChange={(val) => {
formik.setFieldValue(
'product_category_ids',
Array.isArray(val) ? val : val ? [val] : null
Array.isArray(val) && val.length > 0
? val.map((v) => String(v.value)).join(',')
: null
);
}}
isLoading={isLoadingProductCategories}
@@ -967,15 +986,13 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Filter Berdasarkan'
placeholder='Pilih Filter Berdasarkan'
options={dataTypeOptions}
value={
(formik.values.filter_by as
| { value: string; label: string }
| null
| undefined) || null
}
value={filterByValue}
onChange={(val) => {
if (!Array.isArray(val)) {
formik.setFieldValue('filter_by', val);
formik.setFieldValue(
'filter_by',
val?.value?.toString() || null
);
}
}}
className={{ wrapper: 'w-full' }}
@@ -987,15 +1004,13 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Urutkan Berdasarkan'
placeholder='Pilih Urutkan Berdasarkan'
options={sortByOptions}
value={
(formik.values.sort_by as
| { value: string; label: string }
| null
| undefined) || null
}
value={sortByValue}
onChange={(val) => {
if (!Array.isArray(val)) {
formik.setFieldValue('sort_by', val);
formik.setFieldValue(
'sort_by',
val?.value?.toString() || null
);
}
}}
className={{ wrapper: 'w-full' }}