Merge branch 'fix/finance' into 'development'

[FIX/FE] Finance

See merge request mbugroup/lti-web-client!452
This commit is contained in:
Rivaldi A N S
2026-04-30 08:02:36 +00:00
6 changed files with 156 additions and 359 deletions
+31 -68
View File
@@ -1,12 +1,6 @@
'use client';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { CellContext, ColumnDef } from '@tanstack/react-table';
import useSWR from 'swr';
import { Icon } from '@iconify/react';
@@ -40,7 +34,6 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import toast from 'react-hot-toast';
import RequirePermission from '@/components/helper/RequirePermission';
import ButtonFilter from '@/components/helper/ButtonFilter';
import { useUiStore } from '@/stores/ui/ui.store';
import {
FinanceTableFilterSchema,
FinanceTableFilterValues,
@@ -177,9 +170,6 @@ const RowOptionsMenu = ({
};
const FinanceTable = () => {
const { searchValue, setSearchValue, resetSearchValue } = useUiStore();
const previousPathRef = useRef<string | null>(null);
const {
state: tableFilterState,
updateFilter,
@@ -188,7 +178,7 @@ const FinanceTable = () => {
toQueryString: getTableFilterQueryString,
} = useTableFilter({
initial: {
search: searchValue,
search: '',
transactionTypes: '',
bankIds: '',
customerIds: '',
@@ -242,7 +232,7 @@ const FinanceTable = () => {
// ===== Formik for Filter =====
const filterFormik = useFormik<FinanceTableFilterValues>({
initialValues: {
search: searchValue,
search: tableFilterState.search || '',
transaction_types: '',
bank_ids: '',
customer_ids: '',
@@ -252,17 +242,15 @@ const FinanceTable = () => {
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);
onSubmit: (values, { setSubmitting }) => {
updateFilter('search', values.search, true);
updateFilter('transactionTypes', values.transaction_types, true);
updateFilter('bankIds', values.bank_ids, true);
updateFilter('customerIds', values.customer_ids, true);
updateFilter('supplierIds', values.supplier_ids, true);
updateFilter('sortBy', values.sort_by, true);
updateFilter('startDate', values.start_date, true);
updateFilter('endDate', values.end_date, true);
// Save display names for restoration on modal reopen
const toNames = (val: OptionType | OptionType[] | null) =>
val
@@ -270,10 +258,12 @@ const FinanceTable = () => {
.map((o) => String(o.label))
.join(',')
: '';
updateFilter('bankNames', toNames(selectedBank));
updateFilter('customerNames', toNames(selectedCustomerId));
updateFilter('supplierNames', toNames(selectedSupplierId));
updateFilter('bankNames', toNames(selectedBank), true);
updateFilter('customerNames', toNames(selectedCustomerId), true);
updateFilter('supplierNames', toNames(selectedSupplierId), true);
filterModal.closeModal();
setSubmitting(false);
},
onReset: () => {
setSelectedTransactionType(null);
@@ -281,18 +271,17 @@ const FinanceTable = () => {
setSelectedCustomerId(null);
setSelectedSupplierId(null);
setSelectedSortBy(null);
updateFilter('search', '');
resetSearchValue();
updateFilter('transactionTypes', '');
updateFilter('bankIds', '');
updateFilter('customerIds', '');
updateFilter('supplierIds', '');
updateFilter('sortBy', '');
updateFilter('startDate', '');
updateFilter('endDate', '');
updateFilter('bankNames', '');
updateFilter('customerNames', '');
updateFilter('supplierNames', '');
updateFilter('search', '', true);
updateFilter('transactionTypes', '', true);
updateFilter('bankIds', '', true);
updateFilter('customerIds', '', true);
updateFilter('supplierIds', '', true);
updateFilter('sortBy', '', true);
updateFilter('startDate', '', true);
updateFilter('endDate', '', true);
updateFilter('bankNames', '', true);
updateFilter('customerNames', '', true);
updateFilter('supplierNames', '', true);
filterModal.closeModal();
},
});
@@ -347,14 +336,9 @@ const FinanceTable = () => {
}, [bankOptions, bankRawData]);
// ===== Handler =====
const searchChangeHandler = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
updateFilter('search', e.target.value);
setSearchValue(e.target.value);
setPage(1);
},
[updateFilter, setSearchValue, setPage]
);
const searchChangeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
updateFilter('search', e.target.value, true);
};
const transactionTypeChangeHandler = (
val: OptionType | OptionType[] | null
@@ -656,27 +640,6 @@ const FinanceTable = () => {
};
}, [dateErrorShown]);
useEffect(() => {
previousPathRef.current = window.location.pathname;
return () => {
const currentPath = window.location.pathname;
const isCurrentPathFinance = currentPath.includes('/finance');
const isPreviousPathFinance =
previousPathRef.current?.includes('/finance');
if (isPreviousPathFinance && !isCurrentPathFinance) {
resetSearchValue();
}
if (dateErrorShown) {
toast.dismiss();
setDateErrorShown(false);
}
};
}, [resetSearchValue, dateErrorShown]);
return (
<>
<div className='w-full'>
@@ -7,8 +7,7 @@ import {
useMemo,
useState,
} from 'react';
import { usePathname } from 'next/navigation';
import useSWR, { mutate } from 'swr';
import useSWR from 'swr';
import { Icon } from '@iconify/react';
import { ColumnDef, ColumnSort, SortingState } from '@tanstack/react-table';
import { useFormik } from 'formik';
@@ -25,7 +24,6 @@ import { cn, formatNumber, formatDate, formatCurrency } from '@/lib/helper';
import { InventoryAdjustmentApi } from '@/services/api/inventory';
import { WarehouseApi, ProductApi } from '@/services/api/master-data';
import { useTableFilter } from '@/services/hooks/useTableFilter';
import { useUiStore } from '@/stores/ui/ui.store';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
@@ -100,27 +98,31 @@ const RowOptionsMenu = ({
};
const InventoryAdjustmentTable = () => {
const { searchValue, setSearchValue, setTableState } = useUiStore();
const pathname = usePathname();
const {
state: tableFilterState,
updateFilter,
setPage,
setPageSize,
toQueryString: getTableFilterQueryString,
} = useTableFilter({
} = useTableFilter<{
search: string;
productCategorySort: string;
productSort: string;
warehouseSort: string;
stockSort: string;
productFilter?: OptionType<string>;
warehouseFilter?: OptionType<string>;
transactionTypeFilter?: OptionType<string>;
}>({
initial: {
search: '',
productCategorySort: '',
productSort: '',
warehouseSort: '',
stockSort: '',
productFilter: '',
warehouseFilter: '',
transactionTypeFilter: '',
productName: '',
warehouseName: '',
productFilter: undefined,
warehouseFilter: undefined,
transactionTypeFilter: undefined,
},
paramMap: {
page: 'page',
@@ -133,7 +135,6 @@ const InventoryAdjustmentTable = () => {
warehouseFilter: 'warehouse_id',
transactionTypeFilter: 'transaction_type',
},
excludeKeysFromUrl: ['productName', 'warehouseName'],
persist: true,
storeName: 'inventory-adjustment-table',
});
@@ -144,32 +145,26 @@ const InventoryAdjustmentTable = () => {
// ===== FORMIK SETUP =====
const formik = useFormik<AdjustmentFilterType>({
initialValues: {
product_id: null,
warehouse_id: null,
transaction_type: null,
product: tableFilterState.productFilter,
warehouse: tableFilterState.warehouseFilter,
transaction_type: tableFilterState.transactionTypeFilter,
},
validationSchema: AdjustmentFilterSchema,
onSubmit: (values, { setSubmitting }) => {
updateFilter('productFilter', values.product_id || '');
updateFilter('warehouseFilter', values.warehouse_id || '');
updateFilter('transactionTypeFilter', values.transaction_type || '');
updateFilter('productFilter', values.product || undefined, true);
updateFilter('warehouseFilter', values.warehouse || undefined, true);
updateFilter(
'productName',
productIdValue?.label ? String(productIdValue.label) : ''
);
updateFilter(
'warehouseName',
warehouseIdValue?.label ? String(warehouseIdValue.label) : ''
'transactionTypeFilter',
values.transaction_type || undefined,
true
);
filterModal.closeModal();
setSubmitting(false);
},
onReset: () => {
updateFilter('productFilter', '');
updateFilter('warehouseFilter', '');
updateFilter('transactionTypeFilter', '');
updateFilter('productName', '');
updateFilter('warehouseName', '');
updateFilter('productFilter', undefined, true);
updateFilter('warehouseFilter', undefined, true);
updateFilter('transactionTypeFilter', undefined, true);
filterModal.closeModal();
},
});
@@ -209,84 +204,28 @@ const InventoryAdjustmentTable = () => {
}, []);
// ===== FILTER HANDLERS =====
const handleFilterProductChange = useCallback(
(val: OptionType | OptionType[] | null) => {
const product = val as OptionType | null;
const productId = product?.value ? String(product.value) : null;
formik.setFieldValue('product_id', productId);
},
[formik]
);
const handleFilterProductChange = (val: OptionType | OptionType[] | null) => {
formik.setFieldValue('product', val);
};
const handleFilterWarehouseChange = (
val: OptionType | OptionType[] | null
) => {
const warehouse = val as OptionType | null;
formik.setFieldValue(
'warehouse_id',
warehouse?.value ? String(warehouse.value) : null
);
formik.setFieldValue('warehouse', val);
};
const handleFilterTransactionTypeChange = useCallback(
(val: OptionType | OptionType[] | null) => {
const type = val as OptionType | null;
const typeValue = type?.value ? String(type.value) : null;
formik.setFieldValue('transaction_type', typeValue);
},
[formik]
);
// ===== FILTER HELPERS =====
const productIdValue = useMemo(() => {
if (!formik.values.product_id) return null;
const found = productOptions.find(
(opt) => String(opt.value) === formik.values.product_id
);
if (found) return found;
if (tableFilterState.productName) {
return {
value: formik.values.product_id,
label: tableFilterState.productName,
};
}
return null;
}, [formik.values.product_id, productOptions, tableFilterState.productName]);
const warehouseIdValue = useMemo(() => {
if (!formik.values.warehouse_id) return null;
const found = warehouseOptions.find(
(opt) => String(opt.value) === formik.values.warehouse_id
);
if (found) return found;
if (tableFilterState.warehouseName) {
return {
value: formik.values.warehouse_id,
label: tableFilterState.warehouseName,
};
}
return null;
}, [
formik.values.warehouse_id,
warehouseOptions,
tableFilterState.warehouseName,
]);
const transactionTypeValue = useMemo(() => {
if (!formik.values.transaction_type) return null;
return (
transactionTypeOptions.find(
(opt) => String(opt.value) === formik.values.transaction_type
) || null
);
}, [formik.values.transaction_type, transactionTypeOptions]);
const handleFilterTransactionTypeChange = (
val: OptionType | OptionType[] | null
) => {
formik.setFieldValue('transaction_type', val);
};
// ===== HANDLE FILTER MODAL OPEN =====
const handleFilterModalOpen = () => {
formik.setValues({
product_id: tableFilterState.productFilter || null,
warehouse_id: tableFilterState.warehouseFilter || null,
transaction_type: tableFilterState.transactionTypeFilter || null,
product: tableFilterState.productFilter ?? undefined,
warehouse: tableFilterState.warehouseFilter ?? undefined,
transaction_type: tableFilterState.transactionTypeFilter ?? undefined,
});
filterModal.openModal();
};
@@ -325,17 +264,8 @@ const InventoryAdjustmentTable = () => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const singleDeleteModal = useModal();
useEffect(() => {
updateFilter('search', searchValue);
}, [searchValue, updateFilter]);
useEffect(() => {
setTableState('inventory-adjustment-table', pathname);
}, [pathname, setTableState]);
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
setSearchValue(e.target.value);
updateFilter('search', e.target.value);
updateFilter('search', e.target.value, true);
};
const inventoryAdjustmentsColumns: ColumnDef<InventoryAdjustment>[] = useMemo(
@@ -647,7 +577,7 @@ const InventoryAdjustmentTable = () => {
label='Produk'
placeholder='Pilih Produk'
options={productOptions}
value={productIdValue}
value={formik.values.product}
onChange={handleFilterProductChange}
onInputChange={setProductInputValue}
isLoading={isLoadingProductOptions}
@@ -659,7 +589,7 @@ const InventoryAdjustmentTable = () => {
label='Gudang'
placeholder='Pilih Gudang'
options={warehouseOptions}
value={warehouseIdValue}
value={formik.values.warehouse}
onChange={handleFilterWarehouseChange}
onInputChange={setWarehouseInputValue}
isLoading={isLoadingWarehouseOptions}
@@ -671,7 +601,7 @@ const InventoryAdjustmentTable = () => {
label='Tipe Transaksi'
placeholder='Pilih Tipe Transaksi'
options={transactionTypeOptions}
value={transactionTypeValue}
value={formik.values.transaction_type}
onChange={handleFilterTransactionTypeChange}
isClearable
className={{ wrapper: 'w-full' }}
@@ -1,13 +1,23 @@
import { string, object } from 'yup';
import { OptionType } from '@/components/input/SelectInput';
import * as Yup from 'yup';
export const AdjustmentFilterSchema = object().shape({
product_id: string().nullable(),
warehouse_id: string().nullable(),
transaction_type: string().nullable(),
export const AdjustmentFilterSchema = Yup.object().shape({
product: Yup.object({
value: Yup.string().nullable(),
label: Yup.string().nullable(),
}).nullable(),
warehouse: Yup.object({
value: Yup.string().nullable(),
label: Yup.string().nullable(),
}).nullable(),
transaction_type: Yup.object({
value: Yup.string().nullable(),
label: Yup.string().nullable(),
}).nullable(),
});
export type AdjustmentFilterType = {
product_id: string | null;
warehouse_id: string | null;
transaction_type: string | null;
product?: OptionType<string>;
warehouse?: OptionType<string>;
transaction_type?: OptionType<string>;
};
@@ -1,14 +1,7 @@
'use client';
import {
ChangeEventHandler,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { usePathname } from 'next/navigation';
import useSWR, { mutate } from 'swr';
import { ChangeEventHandler, useMemo, useState } from 'react';
import useSWR from 'swr';
import { SortingState, CellContext, ColumnDef } from '@tanstack/react-table';
import { useFormik } from 'formik';
@@ -20,7 +13,6 @@ import { WarehouseApi, ProductApi } from '@/services/api/master-data';
import { cn } from '@/lib/helper';
import { isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
import { useUiStore } from '@/stores/ui/ui.store';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import toast from 'react-hot-toast';
import Button from '@/components/Button';
@@ -108,22 +100,21 @@ const RowOptionsMenu = ({
};
const MovementTable = () => {
const { searchValue, setSearchValue, setTableState } = useUiStore();
const pathname = usePathname();
const {
state: tableFilterState,
updateFilter,
setPage,
setPageSize,
toQueryString: getTableFilterQueryString,
} = useTableFilter({
} = useTableFilter<{
search: string;
productFilter?: OptionType<string>;
warehouseFilter?: OptionType<string>;
}>({
initial: {
search: '',
productFilter: '',
warehouseFilter: '',
productName: '',
warehouseName: '',
productFilter: undefined,
warehouseFilter: undefined,
},
paramMap: {
page: 'page',
@@ -131,7 +122,6 @@ const MovementTable = () => {
productFilter: 'product_id',
warehouseFilter: 'warehouse_id',
},
excludeKeysFromUrl: ['productName', 'warehouseName'],
persist: true,
storeName: 'movement-table',
});
@@ -142,29 +132,19 @@ const MovementTable = () => {
// ===== FORMIK SETUP =====
const formik = useFormik<MovementFilterType>({
initialValues: {
product_id: null,
warehouse_id: null,
product: tableFilterState.productFilter,
warehouse: tableFilterState.warehouseFilter,
},
validationSchema: MovementFilterSchema,
onSubmit: (values, { setSubmitting }) => {
updateFilter('productFilter', values.product_id || '');
updateFilter('warehouseFilter', values.warehouse_id || '');
updateFilter(
'productName',
productIdValue?.label ? String(productIdValue.label) : ''
);
updateFilter(
'warehouseName',
warehouseIdValue?.label ? String(warehouseIdValue.label) : ''
);
updateFilter('productFilter', values.product || undefined, true);
updateFilter('warehouseFilter', values.warehouse || undefined, true);
filterModal.closeModal();
setSubmitting(false);
},
onReset: () => {
updateFilter('productFilter', '');
updateFilter('warehouseFilter', '');
updateFilter('productName', '');
updateFilter('warehouseName', '');
updateFilter('productFilter', undefined, true);
updateFilter('warehouseFilter', undefined, true);
filterModal.closeModal();
},
});
@@ -196,64 +176,21 @@ const MovementTable = () => {
);
// ===== FILTER HANDLERS =====
const handleFilterProductChange = useCallback(
(val: OptionType | OptionType[] | null) => {
const product = val as OptionType | null;
const productId = product?.value ? String(product.value) : null;
formik.setFieldValue('product_id', productId);
},
[formik]
);
const handleFilterProductChange = (val: OptionType | OptionType[] | null) => {
formik.setFieldValue('product', val);
};
const handleFilterWarehouseChange = useCallback(
(val: OptionType | OptionType[] | null) => {
const warehouse = val as OptionType | null;
const warehouseId = warehouse?.value ? String(warehouse.value) : null;
formik.setFieldValue('warehouse_id', warehouseId);
},
[formik]
);
// ===== FILTER HELPERS =====
const productIdValue = useMemo(() => {
if (!formik.values.product_id) return null;
const found = productOptions.find(
(opt) => String(opt.value) === formik.values.product_id
);
if (found) return found;
if (tableFilterState.productName) {
return {
value: formik.values.product_id,
label: tableFilterState.productName,
};
}
return null;
}, [formik.values.product_id, productOptions, tableFilterState.productName]);
const warehouseIdValue = useMemo(() => {
if (!formik.values.warehouse_id) return null;
const found = warehouseOptions.find(
(opt) => String(opt.value) === formik.values.warehouse_id
);
if (found) return found;
if (tableFilterState.warehouseName) {
return {
value: formik.values.warehouse_id,
label: tableFilterState.warehouseName,
};
}
return null;
}, [
formik.values.warehouse_id,
warehouseOptions,
tableFilterState.warehouseName,
]);
const handleFilterWarehouseChange = (
val: OptionType | OptionType[] | null
) => {
formik.setFieldValue('warehouse', val);
};
// ===== HANDLE FILTER MODAL OPEN =====
const handleFilterModalOpen = () => {
formik.setValues({
product_id: tableFilterState.productFilter || null,
warehouse_id: tableFilterState.warehouseFilter || null,
product: tableFilterState.productFilter ?? undefined,
warehouse: tableFilterState.warehouseFilter ?? undefined,
});
filterModal.openModal();
};
@@ -290,17 +227,8 @@ const MovementTable = () => {
}
};
useEffect(() => {
updateFilter('search', searchValue);
}, [searchValue, updateFilter]);
useEffect(() => {
setTableState('movement-table', pathname);
}, [pathname, setTableState]);
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
setSearchValue(e.target.value);
updateFilter('search', e.target.value);
updateFilter('search', e.target.value, true);
};
const movementColumns: ColumnDef<Movement>[] = useMemo(
@@ -419,13 +347,7 @@ const MovementTable = () => {
<ButtonFilter
values={tableFilterState}
excludeFields={[
'page',
'pageSize',
'search',
'productName',
'warehouseName',
]}
excludeFields={['page', 'pageSize', 'search']}
onClick={handleFilterModalOpen}
className='px-3 py-2.5'
/>
@@ -505,7 +427,7 @@ const MovementTable = () => {
label='Produk'
placeholder='Pilih Produk'
options={productOptions}
value={productIdValue}
value={formik.values.product}
onChange={handleFilterProductChange}
onInputChange={setProductInputValue}
isLoading={isLoadingProductOptions}
@@ -517,7 +439,7 @@ const MovementTable = () => {
label='Gudang'
placeholder='Pilih Gudang'
options={warehouseOptions}
value={warehouseIdValue}
value={formik.values.warehouse}
onChange={handleFilterWarehouseChange}
onInputChange={setWarehouseInputValue}
isLoading={isLoadingWarehouseOptions}
@@ -1,11 +1,18 @@
import { string, object } from 'yup';
import { OptionType } from '@/components/input/SelectInput';
import * as Yup from 'yup';
export const MovementFilterSchema = object().shape({
product_id: string().nullable(),
warehouse_id: string().nullable(),
export const MovementFilterSchema = Yup.object().shape({
product: Yup.object({
value: Yup.string().nullable(),
label: Yup.string().nullable(),
}).nullable(),
warehouse: Yup.object({
value: Yup.string().nullable(),
label: Yup.string().nullable(),
}).nullable(),
});
export type MovementFilterType = {
product_id: string | null;
warehouse_id: string | null;
product?: OptionType<string>;
warehouse?: OptionType<string>;
};
@@ -13,16 +13,14 @@ import { cn, formatCurrency, formatNumber } from '@/lib/helper';
import { InventoryProductApi } from '@/services/api/inventory';
import { ProductCategoryApi } from '@/services/api/master-data';
import { useTableFilter } from '@/services/hooks/useTableFilter';
import { useUiStore } from '@/stores/ui/ui.store';
import { InventoryProduct } from '@/types/api/inventory/product';
import { ProductCategory } from '@/types/api/master-data/product-category';
import { Icon } from '@iconify/react';
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
import { usePathname } from 'next/navigation';
import { ChangeEventHandler, useMemo, useState } from 'react';
import useSWR from 'swr';
import { useFormik } from 'formik';
import { object, string } from 'yup';
import * as Yup from 'yup';
import PopoverButton from '@/components/popover/PopoverButton';
import PopoverContent from '@/components/popover/PopoverContent';
import InventoryProductTableSkeleton from '@/components/pages/inventory/product/skeleton/InventoryProductTableSkeleton';
@@ -79,27 +77,25 @@ const RowOptionsMenu = ({
};
const InventoryProductTable = () => {
const { searchValue, setSearchValue, setTableState } = useUiStore();
const pathname = usePathname();
const {
state: tableFilterState,
updateFilter,
setPage,
setPageSize,
toQueryString: getTableFilterQueryString,
} = useTableFilter({
} = useTableFilter<{
search: string;
categoryFilter?: OptionType<string>;
}>({
initial: {
search: '',
categoryFilter: '',
categoryName: '',
categoryFilter: undefined,
},
paramMap: {
page: 'page',
pageSize: 'limit',
categoryFilter: 'product_category_id',
},
excludeKeysFromUrl: ['categoryName'],
persist: true,
storeName: 'inventory-product-table',
});
@@ -108,21 +104,21 @@ const InventoryProductTable = () => {
const filterModal = useModal();
// ===== FORMIK SETUP =====
const formik = useFormik<{ category_id: string | null }>({
initialValues: { category_id: null },
validationSchema: object().shape({ category_id: string().nullable() }),
const formik = useFormik<{ category?: OptionType<string> }>({
initialValues: { category: tableFilterState.categoryFilter },
validationSchema: Yup.object().shape({
category: Yup.object({
value: Yup.string().nullable(),
label: Yup.string().nullable(),
}).nullable(),
}),
onSubmit: (values, { setSubmitting }) => {
updateFilter('categoryFilter', values.category_id || '');
updateFilter(
'categoryName',
categoryIdValue?.label ? String(categoryIdValue.label) : ''
);
updateFilter('categoryFilter', values.category || undefined, true);
filterModal.closeModal();
setSubmitting(false);
},
onReset: () => {
updateFilter('categoryFilter', '');
updateFilter('categoryName', '');
updateFilter('categoryFilter', undefined, true);
filterModal.closeModal();
},
});
@@ -140,40 +136,18 @@ const InventoryProductTable = () => {
'search'
);
// ===== FILTER HELPERS =====
const categoryIdValue = useMemo(() => {
if (!formik.values.category_id) return null;
const found = categoryOptions.find(
(opt) => String(opt.value) === formik.values.category_id
);
if (found) return found;
if (tableFilterState.categoryName) {
return {
value: formik.values.category_id,
label: tableFilterState.categoryName,
};
}
return null;
}, [
formik.values.category_id,
categoryOptions,
tableFilterState.categoryName,
]);
// ===== HANDLE FILTER MODAL OPEN =====
const handleFilterModalOpen = () => {
formik.setValues({ category_id: tableFilterState.categoryFilter || null });
formik.setValues({
category: tableFilterState.categoryFilter ?? undefined,
});
filterModal.openModal();
};
const handleFilterCategoryChange = (
val: OptionType | OptionType[] | null
) => {
const category = val as OptionType | null;
formik.setFieldValue(
'category_id',
category?.value ? String(category.value) : null
);
formik.setFieldValue('category', val);
};
const [sorting, setSorting] = useState<SortingState>([]);
@@ -183,17 +157,8 @@ const InventoryProductTable = () => {
InventoryProductApi.getAllFetcher
);
useEffect(() => {
updateFilter('search', searchValue);
}, [searchValue, updateFilter]);
useEffect(() => {
setTableState('inventory-product-table', pathname);
}, [pathname, setTableState]);
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
setSearchValue(e.target.value);
updateFilter('search', e.target.value);
updateFilter('search', e.target.value, true);
};
const columns: ColumnDef<InventoryProduct>[] = useMemo(
@@ -309,7 +274,7 @@ const InventoryProductTable = () => {
/>
<ButtonFilter
values={tableFilterState}
excludeFields={['page', 'pageSize', 'search', 'categoryName']}
excludeFields={['page', 'pageSize', 'search']}
onClick={handleFilterModalOpen}
className='px-3 py-2.5'
/>
@@ -397,7 +362,7 @@ const InventoryProductTable = () => {
label='Kategori Produk'
placeholder='Pilih Kategori'
options={categoryOptions}
value={categoryIdValue}
value={formik.values.category}
onChange={handleFilterCategoryChange}
onInputChange={setCategoryInputValue}
isLoading={isLoadingCategoryOptions}