diff --git a/src/components/pages/finance/FinanceTable.tsx b/src/components/pages/finance/FinanceTable.tsx index 7ed4fd03..aba80e4e 100644 --- a/src/components/pages/finance/FinanceTable.tsx +++ b/src/components/pages/finance/FinanceTable.tsx @@ -1,13 +1,9 @@ -import { - ChangeEventHandler, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { CellContext } from '@tanstack/react-table'; import { useSearchParams } from 'next/navigation'; import useSWR from 'swr'; +import { useFormik } from 'formik'; +import * as yup from 'yup'; import Button from '@/components/Button'; import Card from '@/components/Card'; @@ -40,6 +36,10 @@ import { Icon } from '@iconify/react'; import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import { useUiStore } from '@/stores/ui/ui.store'; +import { + FinanceTableFilterSchema, + FinanceTableFilterValues, +} from './FinanceTableFilter.schema'; const RowOptionsMenu = ({ type = 'dropdown', @@ -176,16 +176,6 @@ const FinanceTable = () => { // ===== State ===== const [searchParams, setSearchParams] = useSearchParams(); const deleteModal = useModal(); - const [pendingFilters, setPendingFilters] = useState({ - search: searchValue, - transactionTypes: '', - bankIds: '', - customerIds: '', - supplierIds: '', - sortBy: '', - startDate: '', - endDate: '', - }); const [selectedTransactionType, setSelectedTransactionType] = useState< OptionType | OptionType[] | null >(null); @@ -202,6 +192,33 @@ const FinanceTable = () => { const [selectedFinance, setSelectedFinance] = useState(null); const [isDeleteLoading, setIsDeleteLoading] = useState(false); + // ===== Formik for Filter ===== + const filterFormik = useFormik({ + initialValues: { + search: searchValue, + transaction_types: '', + bank_ids: '', + customer_ids: '', + supplier_ids: '', + sort_by: '', + start_date: '', + end_date: '', + }, + validationSchema: FinanceTableFilterSchema, + enableReinitialize: true, + onSubmit: (values) => { + updateFilter('search', values.search); + setSearchValue(values.search); + updateFilter('transactionTypes', values.transaction_types); + updateFilter('bankIds', values.bank_ids); + updateFilter('customerIds', values.customer_ids); + updateFilter('supplierIds', values.supplier_ids); + updateFilter('sortBy', values.sort_by); + updateFilter('startDate', values.start_date); + updateFilter('endDate', values.end_date); + }, + }); + // ===== Fetch Data ===== const { data: finances, @@ -238,83 +255,66 @@ const FinanceTable = () => { } = useSelect(BankApi.basePath, 'id', 'alias'); // ===== Handler ===== - const searchChangeHandler: ChangeEventHandler = (e) => { - setPendingFilters((prev) => ({ ...prev, search: e.target.value })); + const searchChangeHandler = (e: React.ChangeEvent) => { + filterFormik.setFieldValue('search', e.target.value); }; const transactionTypeChangeHandler = ( val: OptionType | OptionType[] | null ) => { setSelectedTransactionType(val); - setPendingFilters((prev) => ({ - ...prev, - transactionTypes: val + filterFormik.setFieldValue( + 'transaction_types', + val ? Array.isArray(val) ? val.map((item) => item.value).join(',') : (val.value as string) - : '', - })); + : '' + ); }; const bankChangeHandler = (val: OptionType | OptionType[] | null) => { setSelectedBank(val); - setPendingFilters((prev) => ({ - ...prev, - bankIds: val + filterFormik.setFieldValue( + 'bank_ids', + val ? Array.isArray(val) ? val.map((item) => item.value).join(',') : (val.value as string) - : '', - })); + : '' + ); }; const customerIdChangeHandler = (val: OptionType | OptionType[] | null) => { setSelectedCustomerId(val); - setPendingFilters((prev) => ({ - ...prev, - customerIds: val + filterFormik.setFieldValue( + 'customer_ids', + val ? Array.isArray(val) ? val.map((item) => item.value).join(',') : (val.value as string) - : '', - })); + : '' + ); }; const supplierIdChangeHandler = (val: OptionType | OptionType[] | null) => { setSelectedSupplierId(val); - setPendingFilters((prev) => ({ - ...prev, - supplierIds: val + filterFormik.setFieldValue( + 'supplier_ids', + val ? Array.isArray(val) ? val.map((item) => item.value).join(',') : (val.value as string) - : '', - })); + : '' + ); }; const sortByChangeHandler = (val: OptionType | OptionType[] | null) => { setSelectedSortBy(val as OptionType); - setPendingFilters((prev) => ({ - ...prev, - sortBy: val ? ((val as OptionType).value as string) : '', - })); - }; - const startDateChangeHandler: ChangeEventHandler = (e) => { - setPendingFilters((prev) => ({ ...prev, startDate: e.target.value })); - }; - const endDateChangeHandler: ChangeEventHandler = (e) => { - setPendingFilters((prev) => ({ ...prev, endDate: e.target.value })); + filterFormik.setFieldValue( + 'sort_by', + val ? ((val as OptionType).value as string) : '' + ); }; const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { const newVal = val as OptionType; setPageSize(newVal.value as number); }; - const submitFilterHandler = () => { - updateFilter('search', pendingFilters.search); - setSearchValue(pendingFilters.search); - updateFilter('transactionTypes', pendingFilters.transactionTypes); - updateFilter('bankIds', pendingFilters.bankIds); - updateFilter('customerIds', pendingFilters.customerIds); - updateFilter('supplierIds', pendingFilters.supplierIds); - updateFilter('sortBy', pendingFilters.sortBy); - updateFilter('startDate', pendingFilters.startDate); - updateFilter('endDate', pendingFilters.endDate); - }; const resetFilterHandler = () => { setSelectedTransactionType(null); setSelectedBank(null); @@ -322,17 +322,7 @@ const FinanceTable = () => { setSelectedSupplierId(null); setSelectedSortBy(null); - const emptyFilters = { - search: '', - transactionTypes: '', - bankIds: '', - customerIds: '', - supplierIds: '', - sortBy: '', - startDate: '', - endDate: '', - }; - setPendingFilters(emptyFilters); + filterFormik.resetForm(); updateFilter('search', ''); resetSearchValue(); @@ -526,7 +516,7 @@ const FinanceTable = () => { @@ -601,22 +591,34 @@ const FinanceTable = () => { isClearable /> diff --git a/src/components/pages/finance/FinanceTableFilter.schema.ts b/src/components/pages/finance/FinanceTableFilter.schema.ts new file mode 100644 index 00000000..bc1053d3 --- /dev/null +++ b/src/components/pages/finance/FinanceTableFilter.schema.ts @@ -0,0 +1,39 @@ +import { OptionType } from '@/components/input/SelectInput'; +import * as yup from 'yup'; + +export type FinanceTableFilterType = { + search: string; + transaction_types: string; + bank_ids: string; + customer_ids: string; + supplier_ids: string; + sort_by: string; + start_date: string; + end_date: string; +}; + +export const FinanceTableFilterSchema = yup.object({ + search: yup.string().optional(), + transaction_types: yup.string().optional(), + bank_ids: yup.string().optional(), + customer_ids: yup.string().optional(), + supplier_ids: yup.string().optional(), + sort_by: yup.string().optional(), + start_date: yup.string().optional(), + end_date: yup + .string() + .optional() + .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); + } + ), +}) as yup.ObjectSchema; + +export type FinanceTableFilterValues = yup.InferType< + typeof FinanceTableFilterSchema +>;