diff --git a/src/components/pages/inventory/adjustment/InventoryAdjustmentTable.tsx b/src/components/pages/inventory/adjustment/InventoryAdjustmentTable.tsx index c04dabec..e96ee62b 100644 --- a/src/components/pages/inventory/adjustment/InventoryAdjustmentTable.tsx +++ b/src/components/pages/inventory/adjustment/InventoryAdjustmentTable.tsx @@ -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; + warehouseFilter?: OptionType; + transactionTypeFilter?: OptionType; + }>({ 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({ 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 = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const inventoryAdjustmentsColumns: ColumnDef[] = 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' }} diff --git a/src/components/pages/inventory/adjustment/filter/AdjustmentFilter.ts b/src/components/pages/inventory/adjustment/filter/AdjustmentFilter.ts index 4568618f..7cd97ecb 100644 --- a/src/components/pages/inventory/adjustment/filter/AdjustmentFilter.ts +++ b/src/components/pages/inventory/adjustment/filter/AdjustmentFilter.ts @@ -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; + warehouse?: OptionType; + transaction_type?: OptionType; }; diff --git a/src/components/pages/inventory/movement/MovementTable.tsx b/src/components/pages/inventory/movement/MovementTable.tsx index 58bddf08..0efd4a9f 100644 --- a/src/components/pages/inventory/movement/MovementTable.tsx +++ b/src/components/pages/inventory/movement/MovementTable.tsx @@ -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; + warehouseFilter?: OptionType; + }>({ 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({ 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 = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const movementColumns: ColumnDef[] = useMemo( @@ -419,13 +347,7 @@ const MovementTable = () => { @@ -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} diff --git a/src/components/pages/inventory/movement/filter/MovementFilter.ts b/src/components/pages/inventory/movement/filter/MovementFilter.ts index fc27b898..24eedb9d 100644 --- a/src/components/pages/inventory/movement/filter/MovementFilter.ts +++ b/src/components/pages/inventory/movement/filter/MovementFilter.ts @@ -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; + warehouse?: OptionType; }; diff --git a/src/components/pages/inventory/product/InventoryProductTable.tsx b/src/components/pages/inventory/product/InventoryProductTable.tsx index ae1a5093..21f457e0 100644 --- a/src/components/pages/inventory/product/InventoryProductTable.tsx +++ b/src/components/pages/inventory/product/InventoryProductTable.tsx @@ -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; + }>({ 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 }>({ + 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([]); @@ -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 = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const columns: ColumnDef[] = useMemo( @@ -309,7 +274,7 @@ const InventoryProductTable = () => { /> @@ -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}