diff --git a/src/components/pages/master-data/area/AreasTable.tsx b/src/components/pages/master-data/area/AreasTable.tsx index 95f91ee9..af730c9c 100644 --- a/src/components/pages/master-data/area/AreasTable.tsx +++ b/src/components/pages/master-data/area/AreasTable.tsx @@ -1,6 +1,6 @@ 'use client'; -import { ChangeEventHandler, useEffect, useMemo, useState } from 'react'; +import { ChangeEventHandler, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -20,8 +20,6 @@ import { Area } from '@/types/api/master-data/area'; import { AreaApi } from '@/services/api/master-data'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { usePathname } from 'next/navigation'; -import { useUiStore } from '@/stores/ui/ui.store'; const RowOptionsMenu = ({ popoverPosition = 'bottom', @@ -103,9 +101,6 @@ const RowOptionsMenu = ({ }; const AreasTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, @@ -114,12 +109,14 @@ const AreasTable = () => { toQueryString: getTableFilterQueryString, } = useTableFilter({ initial: { - search: searchValue, + search: '', }, paramMap: { page: 'page', pageSize: 'limit', }, + persist: true, + storeName: 'areas-table', }); const [sorting, setSorting] = useState([]); @@ -137,17 +134,8 @@ const AreasTable = () => { const [selectedArea, setSelectedArea] = useState(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - - useEffect(() => { - setTableState('areas-table', pathname); - }, [pathname, setTableState]); - const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { diff --git a/src/components/pages/master-data/bank/BanksTable.tsx b/src/components/pages/master-data/bank/BanksTable.tsx index cc62cf70..0284c907 100644 --- a/src/components/pages/master-data/bank/BanksTable.tsx +++ b/src/components/pages/master-data/bank/BanksTable.tsx @@ -1,6 +1,6 @@ 'use client'; -import { ChangeEventHandler, useEffect, useMemo, useState } from 'react'; +import { ChangeEventHandler, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -20,8 +20,6 @@ import { Bank } from '@/types/api/master-data/bank'; import { BankApi } from '@/services/api/master-data'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { usePathname } from 'next/navigation'; -import { useUiStore } from '@/stores/ui/ui.store'; const RowOptionsMenu = ({ popoverPosition = 'bottom', @@ -103,9 +101,6 @@ const RowOptionsMenu = ({ }; const BanksTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, @@ -114,12 +109,14 @@ const BanksTable = () => { toQueryString: getTableFilterQueryString, } = useTableFilter({ initial: { - search: searchValue, + search: '', }, paramMap: { page: 'page', pageSize: 'limit', }, + persist: true, + storeName: 'banks-table', }); const [sorting, setSorting] = useState([]); @@ -137,17 +134,8 @@ const BanksTable = () => { const [selectedBank, setSelectedBank] = useState(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - - useEffect(() => { - setTableState('banks-table', pathname); - }, [pathname, setTableState]); - const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { diff --git a/src/components/pages/master-data/customer/CustomersTable.tsx b/src/components/pages/master-data/customer/CustomersTable.tsx index 1f02428a..b1fb8051 100644 --- a/src/components/pages/master-data/customer/CustomersTable.tsx +++ b/src/components/pages/master-data/customer/CustomersTable.tsx @@ -1,6 +1,6 @@ 'use client'; -import { ChangeEventHandler, useEffect, useMemo, useState } from 'react'; +import { ChangeEventHandler, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -20,8 +20,6 @@ import { Customer } from '@/types/api/master-data/customer'; import { CustomerApi } from '@/services/api/master-data'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { usePathname } from 'next/navigation'; -import { useUiStore } from '@/stores/ui/ui.store'; const RowOptionsMenu = ({ popoverPosition = 'bottom', @@ -103,9 +101,6 @@ const RowOptionsMenu = ({ }; const CustomersTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, @@ -114,12 +109,14 @@ const CustomersTable = () => { toQueryString: getTableFilterQueryString, } = useTableFilter({ initial: { - search: searchValue, + search: '', }, paramMap: { page: 'page', pageSize: 'limit', }, + persist: true, + storeName: 'customers-table', }); const [sorting, setSorting] = useState([]); @@ -139,17 +136,8 @@ const CustomersTable = () => { >(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - - useEffect(() => { - setTableState('customers-table', pathname); - }, [pathname, setTableState]); - const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { diff --git a/src/components/pages/master-data/flock/FlocksTable.tsx b/src/components/pages/master-data/flock/FlocksTable.tsx index ed9f4007..76b3cae8 100644 --- a/src/components/pages/master-data/flock/FlocksTable.tsx +++ b/src/components/pages/master-data/flock/FlocksTable.tsx @@ -1,6 +1,6 @@ 'use client'; -import { ChangeEventHandler, useEffect, useMemo, useState } from 'react'; +import { ChangeEventHandler, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -20,8 +20,6 @@ import { Flock } from '@/types/api/master-data/flock'; import { FlockApi } from '@/services/api/master-data'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { usePathname } from 'next/navigation'; -import { useUiStore } from '@/stores/ui/ui.store'; const RowOptionsMenu = ({ popoverPosition = 'bottom', @@ -103,9 +101,6 @@ const RowOptionsMenu = ({ }; const FlockTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, @@ -114,12 +109,14 @@ const FlockTable = () => { toQueryString: getTableFilterQueryString, } = useTableFilter({ initial: { - search: searchValue, + search: '', }, paramMap: { page: 'page', pageSize: 'limit', }, + persist: true, + storeName: 'flock-table', }); const [sorting, setSorting] = useState([]); @@ -139,17 +136,8 @@ const FlockTable = () => { ); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - - useEffect(() => { - setTableState('flocks-table', pathname); - }, [pathname, setTableState]); - const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { diff --git a/src/components/pages/master-data/kandang/KandangsTable.tsx b/src/components/pages/master-data/kandang/KandangsTable.tsx index 698d3a96..b12192d8 100644 --- a/src/components/pages/master-data/kandang/KandangsTable.tsx +++ b/src/components/pages/master-data/kandang/KandangsTable.tsx @@ -1,13 +1,6 @@ 'use client'; -import { - ChangeEventHandler, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; -import { usePathname } from 'next/navigation'; +import { ChangeEventHandler, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -35,7 +28,6 @@ import { User } from '@/types/api/api-general'; import { formatNumber } from '@/lib/helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { useUiStore } from '@/stores/ui/ui.store'; import { KandangFilterSchema, KandangFilterType, @@ -122,20 +114,21 @@ const RowOptionsMenu = ({ }; const KandangsTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, setPage, setPageSize, toQueryString: getTableFilterQueryString, - } = useTableFilter({ + } = useTableFilter<{ + search: string; + locationFilter?: OptionType; + picFilter?: OptionType; + }>({ initial: { search: '', - locationFilter: '', - picFilter: '', + locationFilter: undefined, + picFilter: undefined, }, paramMap: { page: 'page', @@ -143,6 +136,8 @@ const KandangsTable = () => { locationFilter: 'location_id', picFilter: 'pic_id', }, + persist: true, + storeName: 'kandangs-table', }); // ===== FILTER MODAL STATE ===== @@ -151,22 +146,34 @@ const KandangsTable = () => { // ===== FORMIK SETUP ===== const formik = useFormik({ initialValues: { - location_id: null, - pic_id: null, + location: tableFilterState.locationFilter, + pic: tableFilterState.picFilter, }, validationSchema: KandangFilterSchema, onSubmit: (values, { setSubmitting }) => { - updateFilter('locationFilter', values.location_id || ''); - updateFilter('picFilter', values.pic_id || ''); + updateFilter('locationFilter', values.location || undefined, true); + updateFilter('picFilter', values.pic || undefined, true); filterModal.closeModal(); setSubmitting(false); }, - onReset: () => { - updateFilter('locationFilter', ''); - updateFilter('picFilter', ''); - }, }); + const formikResetHandler = () => { + updateFilter('locationFilter', undefined, true); + updateFilter('picFilter', undefined, true); + + formik.resetForm({ + values: { + location: undefined, + pic: undefined, + }, + }); + + filterModal.closeModal(); + }; + + const { setFieldValue } = formik; + // ===== LOCATION OPTIONS ===== const { setInputValue: setLocationInputValue, @@ -194,43 +201,15 @@ const KandangsTable = () => { ); // ===== FILTER HANDLERS ===== - const handleFilterLocationChange = useCallback( - (val: OptionType | OptionType[] | null) => { - const location = val as OptionType | null; - const locationId = location?.value ? String(location.value) : null; + const handleFilterLocationChange = ( + val: OptionType | OptionType[] | null + ) => { + setFieldValue('location', val); + }; - formik.setFieldValue('location_id', locationId); - }, - [formik] - ); - - const handleFilterPicChange = useCallback( - (val: OptionType | OptionType[] | null) => { - const pic = val as OptionType | null; - const picId = pic?.value ? String(pic.value) : null; - - formik.setFieldValue('pic_id', picId); - }, - [formik] - ); - - // ===== FILTER HELPERS ===== - const locationIdValue = useMemo(() => { - if (!formik.values.location_id) return null; - return ( - locationOptions.find( - (opt) => String(opt.value) === formik.values.location_id - ) || null - ); - }, [formik.values.location_id, locationOptions]); - - const picIdValue = useMemo(() => { - if (!formik.values.pic_id) return null; - return ( - picOptions.find((opt) => String(opt.value) === formik.values.pic_id) || - null - ); - }, [formik.values.pic_id, picOptions]); + const handleFilterPicChange = (val: OptionType | OptionType[] | null) => { + setFieldValue('pic', val); + }; // ===== HANDLE FILTER MODAL OPEN ===== const handleFilterModalOpen = () => { @@ -255,17 +234,8 @@ const KandangsTable = () => { ); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - - useEffect(() => { - setTableState('kandangs-table', pathname); - }, [pathname, setTableState]); - const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { @@ -475,13 +445,13 @@ const KandangsTable = () => { -
+
{ label='PIC' placeholder='Pilih PIC' options={picOptions} - value={picIdValue} + value={formik.values.pic} onChange={handleFilterPicChange} onInputChange={setPicInputValue} isLoading={isLoadingPicOptions} @@ -510,17 +480,14 @@ const KandangsTable = () => { type='button' 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={() => { - formik.resetForm(); - filterModal.closeModal(); - }} + onClick={formikResetHandler} > Reset Filter diff --git a/src/components/pages/master-data/kandang/filter/KandangFilter.ts b/src/components/pages/master-data/kandang/filter/KandangFilter.ts index 30132611..6c945f8d 100644 --- a/src/components/pages/master-data/kandang/filter/KandangFilter.ts +++ b/src/components/pages/master-data/kandang/filter/KandangFilter.ts @@ -1,11 +1,19 @@ -import { string, object } from 'yup'; +import { OptionType } from '@/components/input/SelectInput'; +import * as Yup from 'yup'; -export const KandangFilterSchema = object().shape({ - location_id: string().nullable(), - pic_id: string().nullable(), +export const KandangFilterSchema = Yup.object().shape({ + location: Yup.object({ + value: Yup.string().nullable(), + label: Yup.string().nullable(), + }).nullable(), + + pic: Yup.object({ + value: Yup.string().nullable(), + label: Yup.string().nullable(), + }).nullable(), }); export type KandangFilterType = { - location_id: string | null; - pic_id: string | null; + location?: OptionType; + pic?: OptionType; }; diff --git a/src/components/pages/master-data/location/LocationsTable.tsx b/src/components/pages/master-data/location/LocationsTable.tsx index 89a00539..c396b1c5 100644 --- a/src/components/pages/master-data/location/LocationsTable.tsx +++ b/src/components/pages/master-data/location/LocationsTable.tsx @@ -1,13 +1,6 @@ 'use client'; -import { - ChangeEventHandler, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; -import { usePathname } from 'next/navigation'; +import { ChangeEventHandler, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -32,7 +25,6 @@ import { Area } from '@/types/api/master-data/area'; import { LocationApi, AreaApi } from '@/services/api/master-data'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { useUiStore } from '@/stores/ui/ui.store'; import { LocationFilterSchema, LocationFilterType, @@ -118,25 +110,27 @@ const RowOptionsMenu = ({ }; const LocationsTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, setPage, setPageSize, toQueryString: getTableFilterQueryString, - } = useTableFilter({ + } = useTableFilter<{ + search: string; + areaFilter?: OptionType; + }>({ initial: { search: '', - areaFilter: '', + areaFilter: undefined, }, paramMap: { page: 'page', pageSize: 'limit', areaFilter: 'area_id', }, + persist: true, + storeName: 'locations-table', }); // ===== FILTER MODAL STATE ===== @@ -145,19 +139,28 @@ const LocationsTable = () => { // ===== FORMIK SETUP ===== const formik = useFormik({ initialValues: { - area_id: null, + area: tableFilterState.areaFilter, }, validationSchema: LocationFilterSchema, onSubmit: (values, { setSubmitting }) => { - updateFilter('areaFilter', values.area_id || ''); + updateFilter('areaFilter', values.area || undefined, true); filterModal.closeModal(); setSubmitting(false); }, - onReset: () => { - updateFilter('areaFilter', ''); - }, }); + const formikResetHandler = () => { + updateFilter('areaFilter', undefined, true); + + formik.resetForm({ + values: { + area: undefined, + }, + }); + + filterModal.closeModal(); + }; + // ===== AREA OPTIONS ===== const { setInputValue: setAreaInputValue, @@ -172,24 +175,9 @@ const LocationsTable = () => { ); // ===== FILTER HANDLERS ===== - const handleFilterAreaChange = useCallback( - (val: OptionType | OptionType[] | null) => { - const area = val as OptionType | null; - const areaId = area?.value ? String(area.value) : null; - - formik.setFieldValue('area_id', areaId); - }, - [formik] - ); - - // ===== FILTER HELPERS ===== - const areaIdValue = useMemo(() => { - if (!formik.values.area_id) return null; - return ( - areaOptions.find((opt) => String(opt.value) === formik.values.area_id) || - null - ); - }, [formik.values.area_id, areaOptions]); + const handleFilterAreaChange = (val: OptionType | OptionType[] | null) => { + formik.setFieldValue('area', val); + }; // ===== HANDLE FILTER MODAL OPEN ===== const handleFilterModalOpen = () => { @@ -212,19 +200,10 @@ const LocationsTable = () => { >(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - - useEffect(() => { - setTableState('locations-table', pathname); - }, [pathname, setTableState]); - const [sorting, setSorting] = useState([]); const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { @@ -425,13 +404,13 @@ const LocationsTable = () => {
- +
{ type='button' 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={() => { - formik.resetForm(); - filterModal.closeModal(); - }} + onClick={formikResetHandler} > Reset Filter diff --git a/src/components/pages/master-data/location/filter/LocationFilter.ts b/src/components/pages/master-data/location/filter/LocationFilter.ts index 1235d782..51d58c7c 100644 --- a/src/components/pages/master-data/location/filter/LocationFilter.ts +++ b/src/components/pages/master-data/location/filter/LocationFilter.ts @@ -1,9 +1,13 @@ -import { string, object } from 'yup'; +import { OptionType } from '@/components/input/SelectInput'; +import * as Yup from 'yup'; -export const LocationFilterSchema = object().shape({ - area_id: string().nullable(), +export const LocationFilterSchema = Yup.object().shape({ + area: Yup.object({ + value: Yup.string().nullable(), + label: Yup.string().nullable(), + }).nullable(), }); export type LocationFilterType = { - area_id: string | null; + area?: OptionType; }; diff --git a/src/components/pages/master-data/nonstock/NonstocksTable.tsx b/src/components/pages/master-data/nonstock/NonstocksTable.tsx index cb1b4a17..76e51bb3 100644 --- a/src/components/pages/master-data/nonstock/NonstocksTable.tsx +++ b/src/components/pages/master-data/nonstock/NonstocksTable.tsx @@ -20,8 +20,6 @@ import { Nonstock } from '@/types/api/master-data/nonstock'; import { NonstockApi } from '@/services/api/master-data'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { usePathname } from 'next/navigation'; -import { useUiStore } from '@/stores/ui/ui.store'; const RowOptionsMenu = ({ popoverPosition = 'bottom', @@ -103,9 +101,6 @@ const RowOptionsMenu = ({ }; const NonstocksTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, @@ -114,22 +109,16 @@ const NonstocksTable = () => { toQueryString: getTableFilterQueryString, } = useTableFilter({ initial: { - search: searchValue, + search: '', }, paramMap: { page: 'page', pageSize: 'limit', }, + persist: true, + storeName: 'nonstock-table', }); - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - - useEffect(() => { - setTableState('nonstocks-table', pathname); - }, [pathname, setTableState]); - const [sorting, setSorting] = useState([]); const { @@ -148,8 +137,7 @@ const NonstocksTable = () => { const [isDeleteLoading, setIsDeleteLoading] = useState(false); const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { diff --git a/src/components/pages/master-data/product-category/ProductCategoryTable.tsx b/src/components/pages/master-data/product-category/ProductCategoryTable.tsx index 331f87f7..2b5b99ab 100644 --- a/src/components/pages/master-data/product-category/ProductCategoryTable.tsx +++ b/src/components/pages/master-data/product-category/ProductCategoryTable.tsx @@ -1,7 +1,6 @@ 'use client'; -import { ChangeEventHandler, useEffect, useMemo, useState } from 'react'; -import { usePathname } from 'next/navigation'; +import { ChangeEventHandler, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -21,7 +20,6 @@ import { ProductCategory } from '@/types/api/master-data/product-category'; import { ProductCategoryApi } from '@/services/api/master-data'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { useUiStore } from '@/stores/ui/ui.store'; const RowOptionsMenu = ({ popoverPosition = 'bottom', @@ -103,9 +101,6 @@ const RowOptionsMenu = ({ }; const ProductCategoryTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, @@ -120,12 +115,10 @@ const ProductCategoryTable = () => { page: 'page', pageSize: 'limit', }, + persist: true, + storeName: 'product-category-table', }); - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - const [sorting, setSorting] = useState([]); const { @@ -144,8 +137,7 @@ const ProductCategoryTable = () => { const [isDeleteLoading, setIsDeleteLoading] = useState(false); const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { @@ -214,10 +206,6 @@ const ProductCategoryTable = () => { [tableFilterState.pageSize, tableFilterState.page, deleteModal] ); - useEffect(() => { - setTableState('product-category-table', pathname); - }, [pathname, setTableState]); - return ( <>
diff --git a/src/components/pages/master-data/product/ProductTable.tsx b/src/components/pages/master-data/product/ProductTable.tsx index c58caeed..e422330a 100644 --- a/src/components/pages/master-data/product/ProductTable.tsx +++ b/src/components/pages/master-data/product/ProductTable.tsx @@ -1,13 +1,6 @@ 'use client'; -import { - ChangeEventHandler, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; -import { usePathname } from 'next/navigation'; +import { ChangeEventHandler, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -33,7 +26,6 @@ import { ProductApi, ProductCategoryApi } from '@/services/api/master-data'; import { formatCurrency } from '@/lib/helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { useUiStore } from '@/stores/ui/ui.store'; import { ProductFilterSchema, ProductFilterType, @@ -119,25 +111,27 @@ const RowOptionsMenu = ({ }; const ProductsTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, setPage, setPageSize, toQueryString: getTableFilterQueryString, - } = useTableFilter({ + } = useTableFilter<{ + search: string; + productCategoryFilter?: OptionType; + }>({ initial: { search: '', - productCategoryFilter: '', + productCategoryFilter: undefined, }, paramMap: { page: 'page', pageSize: 'limit', productCategoryFilter: 'product_category_id', }, + persist: true, + storeName: 'product-table', }); // ===== FILTER MODAL STATE ===== @@ -146,19 +140,32 @@ const ProductsTable = () => { // ===== FORMIK SETUP ===== const formik = useFormik({ initialValues: { - product_category_id: null, + product_category: tableFilterState.productCategoryFilter, }, validationSchema: ProductFilterSchema, onSubmit: (values, { setSubmitting }) => { - updateFilter('productCategoryFilter', values.product_category_id || ''); + updateFilter( + 'productCategoryFilter', + values.product_category || undefined, + true + ); filterModal.closeModal(); setSubmitting(false); }, - onReset: () => { - updateFilter('productCategoryFilter', ''); - }, }); + const formikResetHandler = () => { + updateFilter('productCategoryFilter', undefined, true); + + formik.resetForm({ + values: { + product_category: undefined, + }, + }); + + filterModal.closeModal(); + }; + // ===== PRODUCT CATEGORY OPTIONS ===== const { setInputValue: setProductCategoryInputValue, @@ -173,25 +180,11 @@ const ProductsTable = () => { ); // ===== FILTER HANDLERS ===== - const handleFilterProductCategoryChange = useCallback( - (val: OptionType | OptionType[] | null) => { - const category = val as OptionType | null; - const categoryId = category?.value ? String(category.value) : null; - - formik.setFieldValue('product_category_id', categoryId); - }, - [formik] - ); - - // ===== FILTER HELPERS ===== - const productCategoryIdValue = useMemo(() => { - if (!formik.values.product_category_id) return null; - return ( - productCategoryOptions.find( - (opt) => String(opt.value) === formik.values.product_category_id - ) || null - ); - }, [formik.values.product_category_id, productCategoryOptions]); + const handleFilterProductCategoryChange = ( + val: OptionType | OptionType[] | null + ) => { + formik.setFieldValue('product_category', val); + }; // ===== HANDLE FILTER MODAL OPEN ===== const handleFilterModalOpen = () => { @@ -199,10 +192,6 @@ const ProductsTable = () => { formik.validateForm(); }; - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - const [sorting, setSorting] = useState([]); const { @@ -220,13 +209,8 @@ const ProductsTable = () => { ); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - useEffect(() => { - setTableState('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 confirmationModalDeleteClickHandler = async () => { @@ -477,13 +461,13 @@ const ProductsTable = () => {
- +
{ type='button' 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={() => { - formik.resetForm(); - filterModal.closeModal(); - }} + onClick={formikResetHandler} > Reset Filter diff --git a/src/components/pages/master-data/product/filter/ProductFilter.ts b/src/components/pages/master-data/product/filter/ProductFilter.ts index 365dc5de..a2afd7ee 100644 --- a/src/components/pages/master-data/product/filter/ProductFilter.ts +++ b/src/components/pages/master-data/product/filter/ProductFilter.ts @@ -1,9 +1,13 @@ -import { string, object } from 'yup'; +import { OptionType } from '@/components/input/SelectInput'; +import * as Yup from 'yup'; -export const ProductFilterSchema = object().shape({ - product_category_id: string().nullable(), +export const ProductFilterSchema = Yup.object().shape({ + product_category: Yup.object({ + value: Yup.string().nullable(), + label: Yup.string().nullable(), + }).nullable(), }); export type ProductFilterType = { - product_category_id: string | null; + product_category?: OptionType; }; diff --git a/src/components/pages/master-data/production-standard/ProductionStandardTable.tsx b/src/components/pages/master-data/production-standard/ProductionStandardTable.tsx index d843a929..9bb6e432 100644 --- a/src/components/pages/master-data/production-standard/ProductionStandardTable.tsx +++ b/src/components/pages/master-data/production-standard/ProductionStandardTable.tsx @@ -128,27 +128,44 @@ const ProductionStandardTable = () => { pageSize: 'limit', projectCategoryFilter: 'project_category', }, + persist: true, + storeName: 'production-standard-table', }); // ===== FILTER MODAL STATE ===== const filterModal = useModal(); + // ===== FILTER INITIAL VALUES (derived from persisted state) ===== + const filterInitialValues = useMemo( + () => ({ + project_category: tableFilterState.projectCategoryFilter || null, + }), + [tableFilterState.projectCategoryFilter] + ); + // ===== FORMIK SETUP ===== const formik = useFormik({ - initialValues: { - project_category: null, - }, + initialValues: filterInitialValues, validationSchema: ProductionStandardFilterSchema, onSubmit: (values, { setSubmitting }) => { updateFilter('projectCategoryFilter', values.project_category || ''); filterModal.closeModal(); setSubmitting(false); }, - onReset: () => { - updateFilter('projectCategoryFilter', ''); - }, }); + const formikResetHandler = () => { + updateFilter('projectCategoryFilter', '', true); + + formik.resetForm({ + values: { + project_category: null, + }, + }); + + filterModal.closeModal(); + }; + // ===== PROJECT CATEGORY OPTIONS (GROWING or LAYING) ===== const projectCategoryOptions = useMemo( () => [ @@ -381,7 +398,7 @@ const ProductionStandardTable = () => {
- +
{ {/* Modal Footer */}
diff --git a/src/components/pages/master-data/supplier/SupplierTable.tsx b/src/components/pages/master-data/supplier/SupplierTable.tsx index ad2c4ca3..763c18bf 100644 --- a/src/components/pages/master-data/supplier/SupplierTable.tsx +++ b/src/components/pages/master-data/supplier/SupplierTable.tsx @@ -7,7 +7,6 @@ import { useMemo, useState, } from 'react'; -import { usePathname } from 'next/navigation'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -30,7 +29,7 @@ import { Supplier } from '@/types/api/master-data/supplier'; import { SupplierApi } from '@/services/api/master-data'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { useUiStore } from '@/stores/ui/ui.store'; + import { SupplierFilterSchema, SupplierFilterType, @@ -117,20 +116,21 @@ const RowOptionsMenu = ({ }; const SuppliersTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, setPage, setPageSize, toQueryString: getTableFilterQueryString, - } = useTableFilter({ + } = useTableFilter<{ + search: string; + categoryFilter?: OptionType; + flagFilter?: string; + }>({ initial: { search: '', - categoryFilter: '', - flagFilter: '', + categoryFilter: undefined, + flagFilter: undefined, }, paramMap: { page: 'page', @@ -138,6 +138,8 @@ const SuppliersTable = () => { categoryFilter: 'category_id', flagFilter: 'flag', }, + persist: true, + storeName: 'supplier-table', }); // ===== FILTER MODAL STATE ===== @@ -146,26 +148,33 @@ const SuppliersTable = () => { // ===== FORMIK SETUP ===== const formik = useFormik({ initialValues: { - category_id: null, - flag: false, + category: tableFilterState.categoryFilter, + flag: tableFilterState.flagFilter === 'EKSPEDISI', }, validationSchema: SupplierFilterSchema, onSubmit: (values, { setSubmitting }) => { - updateFilter('categoryFilter', values.category_id || ''); - updateFilter( - 'flagFilter', - values.flag === true ? 'EKSPEDISI' : values.flag === false ? '' : '' - ); + updateFilter('categoryFilter', values.category || undefined, true); + updateFilter('flagFilter', values.flag === true ? 'EKSPEDISI' : '', true); filterModal.closeModal(); + setSubmitting(false); }, - onReset: () => { - updateFilter('categoryFilter', ''); - updateFilter('flagFilter', ''); - formik.setFieldValue('flag', false); - }, }); + const formikResetHandler = () => { + updateFilter('categoryFilter', undefined, true); + updateFilter('flagFilter', '', true); + + formik.resetForm({ + values: { + category: undefined, + flag: false, + }, + }); + + filterModal.closeModal(); + }; + const { setFieldValue } = formik; // ===== CATEGORY OPTIONS (SAPRONAK or BOP) ===== @@ -187,15 +196,11 @@ const SuppliersTable = () => { ); // ===== FILTER HANDLERS ===== - const handleFilterCategoryChange = useCallback( - (val: OptionType | OptionType[] | null) => { - const option = val as OptionType | null; - const categoryId = option?.value ? String(option.value) : null; - - setFieldValue('category_id', categoryId); - }, - [setFieldValue] - ); + const handleFilterCategoryChange = ( + val: OptionType | OptionType[] | null + ) => { + setFieldValue('category', val); + }; const handleFilterFlagChange = useCallback( (val: OptionType | OptionType[] | null) => { @@ -213,13 +218,13 @@ const SuppliersTable = () => { ); // ===== FILTER HELPERS ===== - const categoryIdValue = useMemo(() => { - if (!formik.values.category_id) return null; - return ( - categoryOptions.find((opt) => opt.value === formik.values.category_id) || - null - ); - }, [formik.values.category_id, categoryOptions]); + // const categoryIdValue = useMemo(() => { + // if (!formik.values.category_id) return null; + // return ( + // categoryOptions.find((opt) => opt.value === formik.values.category_id) || + // null + // ); + // }, [formik.values.category_id, categoryOptions]); const flagValue = useMemo(() => { if (formik.values.flag === null) return null; @@ -243,14 +248,6 @@ const SuppliersTable = () => { } }, [filterModal.open, tableFilterState.flagFilter, setFieldValue]); - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - - useEffect(() => { - setTableState('suppliers-table', pathname); - }, [pathname, setTableState]); - const [sorting, setSorting] = useState([]); const { @@ -269,8 +266,7 @@ const SuppliersTable = () => { const [isDeleteLoading, setIsDeleteLoading] = useState(false); const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { @@ -491,13 +487,13 @@ const SuppliersTable = () => {
- +
{ {/* Modal Footer */}
diff --git a/src/components/pages/master-data/supplier/filter/SupplierFilter.ts b/src/components/pages/master-data/supplier/filter/SupplierFilter.ts index 4eaa3a1f..ad5dfbe4 100644 --- a/src/components/pages/master-data/supplier/filter/SupplierFilter.ts +++ b/src/components/pages/master-data/supplier/filter/SupplierFilter.ts @@ -1,11 +1,16 @@ -import { string, boolean, object } from 'yup'; +import { OptionType } from '@/components/input/SelectInput'; +import * as Yup from 'yup'; -export const SupplierFilterSchema = object().shape({ - category_id: string().nullable(), - flag: boolean().nullable(), +export const SupplierFilterSchema = Yup.object().shape({ + category: Yup.object({ + value: Yup.string().required(), + label: Yup.string().required(), + }).nullable(), + + flag: Yup.boolean().nullable(), }); export type SupplierFilterType = { - category_id: string | null; + category?: OptionType; flag: boolean | null; }; diff --git a/src/components/pages/master-data/uom/UomsTable.tsx b/src/components/pages/master-data/uom/UomsTable.tsx index a39fdb1a..1334714d 100644 --- a/src/components/pages/master-data/uom/UomsTable.tsx +++ b/src/components/pages/master-data/uom/UomsTable.tsx @@ -1,6 +1,6 @@ 'use client'; -import { ChangeEventHandler, useEffect, useMemo, useState } from 'react'; +import { ChangeEventHandler, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -20,8 +20,6 @@ import { Uom } from '@/types/api/master-data/uom'; import { UomApi } from '@/services/api/master-data'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { usePathname } from 'next/navigation'; -import { useUiStore } from '@/stores/ui/ui.store'; const RowOptionsMenu = ({ popoverPosition = 'bottom', @@ -103,9 +101,6 @@ const RowOptionsMenu = ({ }; const UomsTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, @@ -114,22 +109,16 @@ const UomsTable = () => { toQueryString: getTableFilterQueryString, } = useTableFilter({ initial: { - search: searchValue, + search: '', }, paramMap: { page: 'page', pageSize: 'limit', }, + persist: true, + storeName: 'uom-table', }); - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - - useEffect(() => { - setTableState('uoms-table', pathname); - }, [pathname, setTableState]); - const [sorting, setSorting] = useState([]); const { @@ -146,8 +135,7 @@ const UomsTable = () => { const [isDeleteLoading, setIsDeleteLoading] = useState(false); const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { diff --git a/src/components/pages/master-data/warehouse/WarehousesTable.tsx b/src/components/pages/master-data/warehouse/WarehousesTable.tsx index 25642411..5789605d 100644 --- a/src/components/pages/master-data/warehouse/WarehousesTable.tsx +++ b/src/components/pages/master-data/warehouse/WarehousesTable.tsx @@ -1,13 +1,6 @@ 'use client'; -import { - ChangeEventHandler, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; -import { usePathname } from 'next/navigation'; +import { ChangeEventHandler, useCallback, useMemo, useState } from 'react'; import useSWR from 'swr'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import toast from 'react-hot-toast'; @@ -31,7 +24,6 @@ import { Warehouse } from '@/types/api/master-data/warehouse'; import { WarehouseApi, AreaApi } from '@/services/api/master-data'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { useUiStore } from '@/stores/ui/ui.store'; import { WarehouseFilterSchema, WarehouseFilterType, @@ -120,9 +112,6 @@ const RowOptionsMenu = ({ }; const WarehousesTable = () => { - const { searchValue, setSearchValue, setTableState } = useUiStore(); - const pathname = usePathname(); - const { state: tableFilterState, updateFilter, @@ -141,6 +130,8 @@ const WarehousesTable = () => { areaFilter: 'area_id', activeProjectFlockFilter: 'active_project_flock', }, + persist: true, + storeName: 'warehouses-table', }); // ===== FILTER MODAL STATE ===== @@ -149,27 +140,36 @@ const WarehousesTable = () => { // ===== FORMIK SETUP ===== const formik = useFormik({ initialValues: { - area_id: null, - active_project_flock: false, + area_id: tableFilterState.areaFilter || null, + active_project_flock: + tableFilterState.activeProjectFlockFilter === 'true', }, validationSchema: WarehouseFilterSchema, onSubmit: (values, { setSubmitting }) => { - updateFilter('areaFilter', values.area_id || ''); + updateFilter('areaFilter', values.area_id || '', true); updateFilter( 'activeProjectFlockFilter', - values.active_project_flock === true ? 'true' : '' + values.active_project_flock === true ? 'true' : '', + true ); filterModal.closeModal(); setSubmitting(false); }, - onReset: () => { - updateFilter('areaFilter', ''); - updateFilter('activeProjectFlockFilter', ''); - formik.setFieldValue('active_project_flock', false); - }, }); - const { setFieldValue } = formik; + const formikResetHandler = () => { + updateFilter('areaFilter', '', true); + updateFilter('activeProjectFlockFilter', '', true); + + formik.resetForm({ + values: { + area_id: null, + active_project_flock: false, + }, + }); + + filterModal.closeModal(); + }; // ===== AREA OPTIONS ===== const { @@ -243,26 +243,6 @@ const WarehousesTable = () => { formik.validateForm(); }; - useEffect(() => { - if (filterModal.open) { - const activeProjectFlockValue = - tableFilterState.activeProjectFlockFilter === 'true' ? true : false; // Default ke false (Semua Kandang) - setFieldValue('active_project_flock', activeProjectFlockValue); - } - }, [ - filterModal.open, - tableFilterState.activeProjectFlockFilter, - setFieldValue, - ]); - - useEffect(() => { - updateFilter('search', searchValue); - }, [searchValue, updateFilter]); - - useEffect(() => { - setTableState('warehouses-table', pathname); - }, [pathname, setTableState]); - const [sorting, setSorting] = useState([]); const { @@ -281,8 +261,7 @@ const WarehousesTable = () => { const [isDeleteLoading, setIsDeleteLoading] = useState(false); const searchChangeHandler: ChangeEventHandler = (e) => { - setSearchValue(e.target.value); - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; const confirmationModalDeleteClickHandler = async () => { @@ -507,7 +486,7 @@ const WarehousesTable = () => {
- +
{ type='button' 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={() => { - formik.resetForm(); - filterModal.closeModal(); - }} + onClick={formikResetHandler} > Reset Filter