refactor(FE): Refactor PurchasesPerSupplierTab to use Formik for filters

This commit is contained in:
rstubryan
2026-02-11 16:44:10 +07:00
parent 14d0dc590f
commit 52d58d0921
2 changed files with 439 additions and 405 deletions
@@ -0,0 +1,89 @@
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;
};
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 type PurchasesPerSupplierFilterValues = yup.InferType<
typeof PurchasesPerSupplierFilterSchema
>;
@@ -1,40 +1,55 @@
import { useState, useMemo, useCallback, useEffect } from 'react';
import useSWR from 'swr';
import Button from '@/components/Button';
import Card from '@/components/Card';
import { useSelect, OptionType } from '@/components/input/SelectInput';
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
import SelectInputRadio from '@/components/input/SelectInputRadio';
import Dropdown from '@/components/Dropdown';
import DateInput from '@/components/input/DateInput';
import { OptionType, useSelect } from '@/components/input/SelectInput';
import Menu from '@/components/menu/Menu';
import MenuItem from '@/components/menu/MenuItem';
import Modal, { useModal } from '@/components/Modal';
import Table from '@/components/Table';
import { isResponseSuccess } from '@/lib/api-helper';
import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
import { AreaApi } from '@/services/api/master-data';
import { SupplierApi } from '@/services/api/master-data';
import { ProductApi } from '@/services/api/master-data';
import { ProductCategoryApi } from '@/services/api/master-data';
import { LogisticApi } from '@/services/api/report/logistic-stock';
import Table from '@/components/Table';
import { ColumnDef } from '@tanstack/react-table';
import { formatCurrency, formatDate, formatNumber, cn } from '@/lib/helper';
import {
LogisticPurchasePerSupplierReport,
LogisticPurchasePerSupplierSummary,
} from '@/types/api/report/logistic-stock';
import { isResponseSuccess } from '@/lib/api-helper';
import Button from '@/components/Button';
import Dropdown from '@/components/Dropdown';
import MenuItem from '@/components/menu/MenuItem';
import Menu from '@/components/menu/Menu';
import Modal from '@/components/Modal';
import { useModal } from '@/components/Modal';
import { generatePurchasesPerSupplierPDF } from '@/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportPDF';
import { generatePurchasesPerSupplierExcel } from '@/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportXLSX';
import PurchasePerSupplierSkeleton from '@/components/pages/report/logistic-stock/skeleton/PurchasePerSupplierSkeleton';
import toast from 'react-hot-toast';
import { generatePurchasesPerSupplierPDF } from '@/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportPDF';
import { Icon } from '@iconify/react';
import { ColumnDef } from '@tanstack/react-table';
import { useCallback, useEffect, useMemo, useState } from 'react';
import toast from 'react-hot-toast';
import useSWR from 'swr';
import { useFormik } from 'formik';
import {
PurchasesPerSupplierFilterSchema,
PurchasesPerSupplierFilterType,
} from '@/components/pages/report/logistic-stock/filter/PurchasesPerSupplierFilter';
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
import SelectInputRadio from '@/components/input/SelectInputRadio';
import { useLogisticStockTabStore } from '@/stores/logistic-stock-tab/logistic-stock-tab.store';
import PurchasePerSupplierSkeleton from '@/components/pages/report/logistic-stock/skeleton/PurchasePerSupplierSkeleton';
interface PurchasesPerSupplierTabProps {
tabId: string;
}
interface FilterParams {
area_ids?: string;
supplier_ids?: string;
product_ids?: string;
product_category_ids?: string;
start_date?: string;
end_date?: string;
sort_by?: string;
filter_by?: string;
}
const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
// ===== STATE MANAGEMENT =====
const [isPdfExportLoading, setIsPdfExportLoading] = useState(false);
@@ -46,11 +61,14 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
const [pageSize] = useState(10);
// ===== SUBMISSION STATE =====
const [filterParams, setFilterParams] = useState<FilterParams>({});
const [isSubmitted, setIsSubmitted] = useState(false);
const [dateErrorShown, setDateErrorShown] = useState(false);
const [hasDateError, setHasDateError] = useState(false);
const filterModal = useModal();
// ===== OPTIONS (Declare before filter state) =====
// ===== OPTIONS =====
const { options: areaOptions, isLoadingOptions: isLoadingAreas } = useSelect(
AreaApi.basePath,
'id',
@@ -87,127 +105,66 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
[]
);
// ===== APPLIED FILTER STATE (Yang sudah di-apply) =====
const [appliedFilterArea, setAppliedFilterArea] = useState<
typeof areaOptions
>([]);
const [appliedFilterSupplier, setAppliedFilterSupplier] = useState<
typeof supplierOptions
>([]);
const [appliedFilterProduct, setAppliedFilterProduct] = useState<
typeof productOptions
>([]);
const [appliedFilterProductCategory, setAppliedFilterProductCategory] =
useState<typeof productCategoryOptions>([]);
const [appliedFilterByType, setAppliedFilterByType] = useState<
(typeof dataTypeOptions)[0] | null
>(null);
const [appliedFilterSortBy, setAppliedFilterSortBy] = useState<
(typeof sortByOptions)[0] | null
>(null);
const [appliedFilterStartDate, setAppliedFilterStartDate] = useState('');
const [appliedFilterEndDate, setAppliedFilterEndDate] = useState('');
const [dateErrorShown, setDateErrorShown] = useState(false);
const [hasDateError, setHasDateError] = useState(false);
// ===== PENDING FILTER STATE (Yang ada di modal, belum di-apply) =====
const [filterArea, setFilterArea] = useState<typeof areaOptions>([]);
const [filterSupplier, setFilterSupplier] = useState<typeof supplierOptions>(
[]
);
const [filterProduct, setFilterProduct] = useState<typeof productOptions>([]);
const [filterProductCategory, setFilterProductCategory] = useState<
typeof productCategoryOptions
>([]);
const [filterByType, setFilterByType] = useState<
(typeof dataTypeOptions)[0] | null
>(null);
const [filterSortBy, setFilterSortBy] = useState<
(typeof sortByOptions)[0] | null
>(null);
const [filterStartDate, setFilterStartDate] = useState('');
const [filterEndDate, setFilterEndDate] = useState('');
// ===== FILTER HANDLERS =====
const handleFilterModalOpen = useCallback(() => {
setFilterArea(appliedFilterArea);
setFilterSupplier(appliedFilterSupplier);
setFilterProduct(appliedFilterProduct);
setFilterProductCategory(appliedFilterProductCategory);
setFilterByType(appliedFilterByType);
setFilterSortBy(appliedFilterSortBy);
setFilterStartDate(appliedFilterStartDate);
setFilterEndDate(appliedFilterEndDate);
const handleFilterModalOpen = () => {
filterModal.openModal();
}, [
filterModal,
appliedFilterArea,
appliedFilterSupplier,
appliedFilterProduct,
appliedFilterProductCategory,
appliedFilterByType,
appliedFilterSortBy,
appliedFilterStartDate,
appliedFilterEndDate,
]);
};
const handleResetFilters = useCallback(() => {
// ===== FORMIK SETUP =====
const formik = useFormik<PurchasesPerSupplierFilterType>({
initialValues: {
start_date: null,
end_date: null,
area_ids: null,
supplier_ids: null,
product_ids: null,
product_category_ids: null,
filter_by: null,
sort_by: null,
},
validationSchema: PurchasesPerSupplierFilterSchema,
onSubmit: (values) => {
setFilterParams({
start_date: values.start_date?.toString() || undefined,
end_date: values.end_date?.toString() || undefined,
area_ids:
values.area_ids?.map((v) => String(v.value)).join(',') || undefined,
supplier_ids:
values.supplier_ids?.map((v) => String(v.value)).join(',') ||
undefined,
product_ids:
values.product_ids?.map((v) => String(v.value)).join(',') ||
undefined,
product_category_ids:
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,
});
filterModal.closeModal();
setIsSubmitted(true);
setCurrentPage(1);
},
onReset: () => {
setFilterParams({});
setIsSubmitted(false);
setFilterArea([]);
setFilterSupplier([]);
setFilterProduct([]);
setFilterProductCategory([]);
setFilterByType(null);
setFilterSortBy(null);
setFilterStartDate('');
setFilterEndDate('');
setAppliedFilterArea([]);
setAppliedFilterSupplier([]);
setAppliedFilterProduct([]);
setAppliedFilterProductCategory([]);
setAppliedFilterByType(null);
setAppliedFilterSortBy(null);
setAppliedFilterStartDate('');
setAppliedFilterEndDate('');
setCurrentPage(1);
setHasDateError(false);
if (dateErrorShown) {
toast.dismiss();
setDateErrorShown(false);
}
}, [dateErrorShown]);
const handleApplyFilters = useCallback(() => {
setAppliedFilterArea(filterArea);
setAppliedFilterSupplier(filterSupplier);
setAppliedFilterProduct(filterProduct);
setAppliedFilterProductCategory(filterProductCategory);
setAppliedFilterByType(filterByType);
setAppliedFilterSortBy(filterSortBy);
setAppliedFilterStartDate(filterStartDate);
setAppliedFilterEndDate(filterEndDate);
setIsSubmitted(true);
setCurrentPage(1);
filterModal.closeModal();
}, [
filterModal,
filterArea,
filterSupplier,
filterProduct,
filterProductCategory,
filterByType,
filterSortBy,
filterStartDate,
filterEndDate,
]);
},
});
// ===== DATE CHANGE HANDLERS =====
const handleStartDateChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setFilterStartDate(value);
formik.setFieldValue('start_date', value || null);
if (value && filterEndDate) {
if (value && formik.values.end_date) {
const startDate = new Date(value);
const endDateObj = new Date(filterEndDate);
const endDateObj = new Date(formik.values.end_date);
if (endDateObj < startDate) {
setHasDateError(true);
@@ -228,16 +185,16 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
setHasDateError(false);
}
},
[filterEndDate, dateErrorShown]
[formik, dateErrorShown]
);
const handleEndDateChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setFilterEndDate(value);
formik.setFieldValue('end_date', value || null);
if (value && filterStartDate) {
const startDateObj = new Date(filterStartDate);
if (value && formik.values.start_date) {
const startDateObj = new Date(formik.values.start_date);
const endDate = new Date(value);
if (endDate < startDateObj) {
@@ -258,59 +215,43 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
setDateErrorShown(false);
}
},
[filterStartDate, dateErrorShown]
[formik, dateErrorShown]
);
// ===== ACTIVE FILTERS COUNT =====
const activeFiltersCount = useMemo(() => {
let count = 0;
// Date filter (start_date + end_date = 1 filter)
if (appliedFilterStartDate || appliedFilterEndDate) {
if (filterParams.start_date || filterParams.end_date) {
count += 1;
}
// Area filter
if (appliedFilterArea.length > 0) {
if (filterParams.area_ids) {
count += 1;
}
// Supplier filter
if (appliedFilterSupplier.length > 0) {
if (filterParams.supplier_ids) {
count += 1;
}
// Product filter
if (appliedFilterProduct.length > 0) {
if (filterParams.product_ids) {
count += 1;
}
// Product category filter
if (appliedFilterProductCategory.length > 0) {
if (filterParams.product_category_ids) {
count += 1;
}
// Filter by type filter
if (appliedFilterByType) {
if (filterParams.filter_by) {
count += 1;
}
// Sort by filter
if (appliedFilterSortBy) {
if (filterParams.sort_by) {
count += 1;
}
return count;
}, [
appliedFilterStartDate,
appliedFilterEndDate,
appliedFilterArea,
appliedFilterSupplier,
appliedFilterProduct,
appliedFilterProductCategory,
appliedFilterByType,
appliedFilterSortBy,
]);
}, [filterParams]);
const hasFilters = activeFiltersCount > 0;
@@ -319,39 +260,14 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
isSubmitted
? () => {
const params = {
area_id:
appliedFilterArea.length > 0
? appliedFilterArea.map((v) => String(v.value)).join(',')
: undefined,
supplier_id:
appliedFilterSupplier.length > 0
? appliedFilterSupplier.map((v) => String(v.value)).join(',')
: undefined,
product_id:
appliedFilterProduct.length > 0
? appliedFilterProduct.map((v) => String(v.value)).join(',')
: undefined,
product_category_id:
appliedFilterProductCategory.length > 0
? appliedFilterProductCategory
.map((v) => String(v.value))
.join(',')
: undefined,
received_date:
appliedFilterByType?.value === 'received_date'
? appliedFilterStartDate || undefined
: undefined,
po_date:
appliedFilterByType?.value === 'po_date'
? appliedFilterStartDate || undefined
: undefined,
start_date: appliedFilterStartDate || undefined,
end_date: appliedFilterEndDate || undefined,
sort_by:
(appliedFilterSortBy?.value as 'ASC' | 'DESC') || undefined,
filter_by:
(appliedFilterByType?.value as 'received_date' | 'po_date') ||
undefined,
area_ids: filterParams.area_ids,
supplier_ids: filterParams.supplier_ids,
product_ids: filterParams.product_ids,
product_category_ids: filterParams.product_category_ids,
start_date: filterParams.start_date,
end_date: filterParams.end_date,
sort_by: filterParams.sort_by,
filter_by: filterParams.filter_by,
page: currentPage,
limit: pageSize,
};
@@ -361,12 +277,12 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
: null,
([, params]) =>
LogisticApi.getLogisticPurchasePerSupplierReport(
params.area_id,
params.supplier_id,
params.product_id,
params.product_category_id,
params.received_date,
params.po_date,
params.area_ids,
params.supplier_ids,
params.product_ids,
params.product_category_ids,
params.filter_by === 'received_date' ? params.start_date : undefined,
params.filter_by === 'po_date' ? params.start_date : undefined,
params.start_date,
params.end_date,
params.sort_by,
@@ -395,47 +311,25 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
LogisticPurchasePerSupplierReport[] | null
> => {
const params = {
area_id:
appliedFilterArea.length > 0
? appliedFilterArea.map((v) => String(v.value)).join(',')
: undefined,
supplier_id:
appliedFilterSupplier.length > 0
? appliedFilterSupplier.map((v) => String(v.value)).join(',')
: undefined,
product_id:
appliedFilterProduct.length > 0
? appliedFilterProduct.map((v) => String(v.value)).join(',')
: undefined,
product_category_id:
appliedFilterProductCategory.length > 0
? appliedFilterProductCategory.map((v) => String(v.value)).join(',')
: undefined,
received_date:
appliedFilterByType?.value === 'received_date'
? appliedFilterStartDate || undefined
: undefined,
po_date:
appliedFilterByType?.value === 'po_date'
? appliedFilterStartDate || undefined
: undefined,
start_date: appliedFilterStartDate || undefined,
end_date: appliedFilterEndDate || undefined,
sort_by: (appliedFilterSortBy?.value as 'ASC' | 'DESC') || undefined,
filter_by:
(appliedFilterByType?.value as 'received_date' | 'po_date') ||
undefined,
area_ids: filterParams.area_ids,
supplier_ids: filterParams.supplier_ids,
product_ids: filterParams.product_ids,
product_category_ids: filterParams.product_category_ids,
start_date: filterParams.start_date,
end_date: filterParams.end_date,
sort_by: filterParams.sort_by,
filter_by: filterParams.filter_by,
limit: 100,
page: 1,
};
const response = await LogisticApi.getLogisticPurchasePerSupplierReport(
params.area_id,
params.supplier_id,
params.product_id,
params.product_category_id,
params.received_date,
params.po_date,
params.area_ids,
params.supplier_ids,
params.product_ids,
params.product_category_ids,
params.filter_by === 'received_date' ? params.start_date : undefined,
params.filter_by === 'po_date' ? params.start_date : undefined,
params.start_date,
params.end_date,
params.sort_by,
@@ -447,16 +341,7 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
return isResponseSuccess(response)
? (response.data as unknown as LogisticPurchasePerSupplierReport[])
: null;
}, [
appliedFilterArea,
appliedFilterSupplier,
appliedFilterProduct,
appliedFilterProductCategory,
appliedFilterStartDate,
appliedFilterEndDate,
appliedFilterByType,
appliedFilterSortBy,
]);
}, [filterParams]);
// ===== EXPORT HANDLERS =====
const handleExportExcel = useCallback(async () => {
@@ -496,27 +381,42 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
return;
}
const areaName =
appliedFilterArea.length > 0
? appliedFilterArea.map((c) => c.label).join(', ') || 'Semua Area'
const areaName = filterParams.area_ids
? areaOptions
.filter((opt) =>
filterParams.area_ids?.split(',').includes(String(opt.value))
)
.map((opt) => opt.label)
.join(', ') || 'Semua Area'
: 'Semua Area';
const supplierName =
appliedFilterSupplier.length > 0
? appliedFilterSupplier.map((c) => c.label).join(', ') ||
'Semua Supplier'
const supplierName = filterParams.supplier_ids
? supplierOptions
.filter((opt) =>
filterParams.supplier_ids?.split(',').includes(String(opt.value))
)
.map((opt) => opt.label)
.join(', ') || 'Semua Supplier'
: 'Semua Supplier';
const productName =
appliedFilterProduct.length > 0
? appliedFilterProduct.map((c) => c.label).join(', ') ||
'Semua Produk'
const productName = filterParams.product_ids
? productOptions
.filter((opt) =>
filterParams.product_ids?.split(',').includes(String(opt.value))
)
.map((opt) => opt.label)
.join(', ') || 'Semua Produk'
: 'Semua Produk';
const productCategoryName =
appliedFilterProductCategory.length > 0
? appliedFilterProductCategory.map((c) => c.label).join(', ') ||
'Semua Kategori Produk'
const productCategoryName = filterParams.product_category_ids
? productCategoryOptions
.filter((opt) =>
filterParams.product_category_ids
?.split(',')
.includes(String(opt.value))
)
.map((opt) => opt.label)
.join(', ') || 'Semua Kategori Produk'
: 'Semua Kategori Produk';
const exportParams = {
@@ -524,11 +424,9 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
supplier_name: supplierName,
product_name: productName,
product_category_name: productCategoryName,
filter_by:
(appliedFilterByType?.value as 'received_date' | 'po_date') ||
undefined,
start_date: appliedFilterStartDate || undefined,
end_date: appliedFilterEndDate || undefined,
filter_by: filterParams.filter_by,
start_date: filterParams.start_date,
end_date: filterParams.end_date,
};
await generatePurchasesPerSupplierPDF({
@@ -543,13 +441,11 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
}
}, [
logisticPurchasePerSupplierExport,
appliedFilterArea,
appliedFilterSupplier,
appliedFilterProduct,
appliedFilterProductCategory,
appliedFilterStartDate,
appliedFilterEndDate,
appliedFilterByType,
filterParams,
areaOptions,
supplierOptions,
productOptions,
productCategoryOptions,
]);
// ===== REGISTER TAB ACTIONS TO STORE =====
@@ -624,6 +520,7 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
</Dropdown>
</div>
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
tabId,
hasFilters,
@@ -633,6 +530,7 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
setTabActions,
]);
// Cleanup on unmount
useEffect(() => {
return () => {
clearTabActions(tabId);
@@ -945,6 +843,7 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
<Icon icon='heroicons:x-mark' width={20} height={20} />
</Button>
</div>
<form onSubmit={formik.handleSubmit} onReset={formik.handleReset}>
<div className='p-4 flex flex-col gap-3'>
{/* Date Filter */}
<div>
@@ -954,7 +853,7 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
<div className='flex flex-row gap-1.5 items-center justify-between'>
<DateInput
name='start_date'
value={filterStartDate}
value={formik.values.start_date || ''}
onChange={handleStartDateChange}
className={{ wrapper: 'w-full' }}
isNestedModal
@@ -962,10 +861,11 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
<hr className='w-full max-w-3 h-px border-base-content/10' />
<DateInput
name='end_date'
value={filterEndDate}
value={formik.values.end_date || ''}
onChange={handleEndDateChange}
className={{ wrapper: 'w-full' }}
isNestedModal
isError={hasDateError}
/>
</div>
</div>
@@ -975,9 +875,18 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Area'
placeholder='Pilih Area'
options={areaOptions}
value={filterArea}
value={
(formik.values.area_ids as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined) || []
}
onChange={(val) => {
setFilterArea(Array.isArray(val) ? val : val ? [val] : []);
formik.setFieldValue(
'area_ids',
Array.isArray(val) ? val : val ? [val] : null
);
}}
isLoading={isLoadingAreas}
isClearable
@@ -989,9 +898,18 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Supplier'
placeholder='Pilih Supplier'
options={supplierOptions}
value={filterSupplier}
value={
(formik.values.supplier_ids as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined) || []
}
onChange={(val) => {
setFilterSupplier(Array.isArray(val) ? val : val ? [val] : []);
formik.setFieldValue(
'supplier_ids',
Array.isArray(val) ? val : val ? [val] : null
);
}}
isLoading={isLoadingSuppliers}
isClearable
@@ -1003,9 +921,18 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Produk'
placeholder='Pilih Produk'
options={productOptions}
value={filterProduct}
value={
(formik.values.product_ids as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined) || []
}
onChange={(val) => {
setFilterProduct(Array.isArray(val) ? val : val ? [val] : []);
formik.setFieldValue(
'product_ids',
Array.isArray(val) ? val : val ? [val] : null
);
}}
isLoading={isLoadingProducts}
isClearable
@@ -1017,10 +944,17 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Kategori Produk'
placeholder='Pilih Kategori Produk'
options={productCategoryOptions}
value={filterProductCategory}
value={
(formik.values.product_category_ids as
| { value: number; label: string }
| { value: number; label: string }[]
| null
| undefined) || []
}
onChange={(val) => {
setFilterProductCategory(
Array.isArray(val) ? val : val ? [val] : []
formik.setFieldValue(
'product_category_ids',
Array.isArray(val) ? val : val ? [val] : null
);
}}
isLoading={isLoadingProductCategories}
@@ -1033,10 +967,15 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Filter Berdasarkan'
placeholder='Pilih Filter Berdasarkan'
options={dataTypeOptions}
value={filterByType}
value={
(formik.values.filter_by as
| { value: string; label: string }
| null
| undefined) || null
}
onChange={(val) => {
if (!Array.isArray(val)) {
setFilterByType(val);
formik.setFieldValue('filter_by', val);
}
}}
className={{ wrapper: 'w-full' }}
@@ -1048,10 +987,15 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
label='Urutkan Berdasarkan'
placeholder='Pilih Urutkan Berdasarkan'
options={sortByOptions}
value={filterSortBy}
value={
(formik.values.sort_by as
| { value: string; label: string }
| null
| undefined) || null
}
onChange={(val) => {
if (!Array.isArray(val)) {
setFilterSortBy(val);
formik.setFieldValue('sort_by', val);
}
}}
className={{ wrapper: 'w-full' }}
@@ -1062,20 +1006,21 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
{/* Modal Footer */}
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
<Button
type='reset'
variant='soft'
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
onClick={handleResetFilters}
>
Reset Filter
</Button>
<Button
type='submit'
className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold'
onClick={handleApplyFilters}
disabled={hasDateError}
disabled={hasDateError || !formik.isValid || formik.isSubmitting}
>
Apply Filter
</Button>
</div>
</form>
</Modal>
</>
);