From ef9009b304f8fb90e5162f751d16b0cc51f346bd Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Thu, 30 Apr 2026 11:02:09 +0700 Subject: [PATCH 1/6] fix: remove empty kandang date --- .../daily-checklist/DailyChecklistContent.tsx | 33 +------------------ .../api/daily-checklist/daily-checklist.d.ts | 2 -- 2 files changed, 1 insertion(+), 34 deletions(-) diff --git a/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx b/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx index 50119101..40a2c130 100644 --- a/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx +++ b/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx @@ -103,7 +103,6 @@ export function DailyChecklistContent() { searchParams.get('category') || '' ); const [emptyKandang, setEmptyKandang] = useState(false); - const [emptyKandangEndDate, setEmptyKandangEndDate] = useState(''); const isKandangEmpty = selectedCategory === 'empty_kandang'; @@ -239,7 +238,6 @@ export function DailyChecklistContent() { useEffect(() => { if (!emptyKandang) { - setEmptyKandangEndDate(''); setSelectedCategory(''); return; } @@ -314,16 +312,6 @@ export function DailyChecklistContent() { return; } - if (emptyKandang && !emptyKandangEndDate) { - setDailyChecklistId(null); - setChecklistStatus('DRAFT'); - setSelectedPhaseIds([]); - setActivitiesByPhase({}); - setTaskIdsByPhaseActivityId({}); - setAssignments({}); - return; - } - try { const checklist = await DailyChecklistApi.create({ date, @@ -331,7 +319,6 @@ export function DailyChecklistContent() { category: emptyKandang ? 'empty_kandang' : selectedCategory, status: 'DRAFT', empty_kandang: emptyKandang, - empty_kandang_end_date: emptyKandang ? emptyKandangEndDate : '', }); if (isResponseError(checklist)) { @@ -388,7 +375,7 @@ export function DailyChecklistContent() { }; checkAndLoadChecklist(); - }, [date, kandangId, selectedCategory, emptyKandang, emptyKandangEndDate]); + }, [date, kandangId, selectedCategory, emptyKandang]); // Load activities and tasks when phases change useEffect(() => { @@ -1176,24 +1163,6 @@ export function DailyChecklistContent() { /> Kandang Kosong - - {emptyKandang && ( -
- -
- -
-
- )} diff --git a/src/types/api/daily-checklist/daily-checklist.d.ts b/src/types/api/daily-checklist/daily-checklist.d.ts index 5c7d3b26..30244193 100644 --- a/src/types/api/daily-checklist/daily-checklist.d.ts +++ b/src/types/api/daily-checklist/daily-checklist.d.ts @@ -13,7 +13,6 @@ export type BaseDailyChecklist = { category: string; date: string; empty_kandang?: boolean; - empty_kandang_end_date?: string | null; kandang?: Pick; total_phase: number; total_activity: number; @@ -60,7 +59,6 @@ export type CreateDailyChecklistPayload = { category: string; status: string; empty_kandang: boolean; - empty_kandang_end_date: string; }; export type PerformanceOverviewItem = { From 15c883ca732215591384411c195923037509421d Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Thu, 30 Apr 2026 11:40:16 +0700 Subject: [PATCH 2/6] fix: add created at column --- src/components/pages/finance/FinanceTable.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/pages/finance/FinanceTable.tsx b/src/components/pages/finance/FinanceTable.tsx index 7bf62aba..5173a618 100644 --- a/src/components/pages/finance/FinanceTable.tsx +++ b/src/components/pages/finance/FinanceTable.tsx @@ -586,10 +586,14 @@ const FinanceTable = () => { }, }, { - header: 'Tanggal', + header: 'Tanggal Pembayaran', accessorFn: (finance: Finance) => formatDate(finance.payment_date, 'DD MMM YYYY'), }, + { + header: 'Tanggal Dibuat', + accessorFn: (finance) => formatDate(finance.created_at, 'DD MMM YYYY'), + }, { header: 'Metode Pembayaran', accessorKey: 'payment_method', From e52ba7b3940201744408b6cfa4e4603a0ad5d5fe Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Thu, 30 Apr 2026 15:01:11 +0700 Subject: [PATCH 3/6] fix: search input value and change handler --- src/components/pages/finance/FinanceTable.tsx | 99 ++++++------------- 1 file changed, 31 insertions(+), 68 deletions(-) diff --git a/src/components/pages/finance/FinanceTable.tsx b/src/components/pages/finance/FinanceTable.tsx index 5173a618..998193e7 100644 --- a/src/components/pages/finance/FinanceTable.tsx +++ b/src/components/pages/finance/FinanceTable.tsx @@ -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(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({ 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) => { - updateFilter('search', e.target.value); - setSearchValue(e.target.value); - setPage(1); - }, - [updateFilter, setSearchValue, setPage] - ); + const searchChangeHandler = (e: React.ChangeEvent) => { + 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 ( <>
From 039c926e2dca19188847d19acfe7d3fc7bc2a195 Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Thu, 30 Apr 2026 15:01:21 +0700 Subject: [PATCH 4/6] fix: implement table filter persist state --- .../adjustment/InventoryAdjustmentTable.tsx | 152 +++++------------- .../adjustment/filter/AdjustmentFilter.ts | 26 ++- .../inventory/movement/MovementTable.tsx | 136 ++++------------ .../movement/filter/MovementFilter.ts | 19 ++- .../product/InventoryProductTable.tsx | 83 +++------- 5 files changed, 125 insertions(+), 291 deletions(-) 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} From 9e402e373cb02ae8374fe04273ac4960a0d7be92 Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Thu, 30 Apr 2026 16:56:12 +0700 Subject: [PATCH 5/6] fix: adjust filter submit handler --- .../pages/expense/ExpensesTable.tsx | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/components/pages/expense/ExpensesTable.tsx b/src/components/pages/expense/ExpensesTable.tsx index 75c25da6..9a133447 100644 --- a/src/components/pages/expense/ExpensesTable.tsx +++ b/src/components/pages/expense/ExpensesTable.tsx @@ -1,6 +1,5 @@ 'use client'; -import axios from 'axios'; import { ChangeEventHandler, useCallback, @@ -722,7 +721,7 @@ const ExpensesTable = () => { }; const searchChangeHandler: ChangeEventHandler = (e) => { - updateFilter('search', e.target.value); + updateFilter('search', e.target.value, true); }; // ===== FILTER MODAL HANDLERS ===== @@ -741,41 +740,52 @@ const ExpensesTable = () => { project_flock?: OptionType | null; project_flock_kandang?: OptionType | null; }) => { - updateFilter('transactionDate', values.transaction_date || ''); - updateFilter('realizationDate', values.realization_date || ''); + updateFilter('transactionDate', values.transaction_date || '', true); + updateFilter('realizationDate', values.realization_date || '', true); updateFilter( 'locationId', - values.location?.value ? String(values.location?.value) : '' + values.location?.value ? String(values.location?.value) : '', + true ); updateFilter( 'locationName', - values.location?.label ? String(values.location?.label) : '' + values.location?.label ? String(values.location?.label) : '', + true ); updateFilter( 'vendorId', - values.vendor?.value ? String(values.vendor?.value) : '' + values.vendor?.value ? String(values.vendor?.value) : '', + true ); updateFilter( 'vendorName', - values.vendor?.label ? String(values.vendor?.label) : '' + values.vendor?.label ? String(values.vendor?.label) : '', + true + ); + updateFilter('category', values.category?.value || '', true); + updateFilter('approvalStatus', values.approval_status?.value || '', true); + updateFilter( + 'realizationStatus', + values.realization_status?.value || '', + true ); - updateFilter('category', values.category?.value || ''); - updateFilter('approvalStatus', values.approval_status?.value || ''); - updateFilter('realizationStatus', values.realization_status?.value || ''); updateFilter( 'projectFlockId', - values.project_flock?.value ? String(values.project_flock.value) : '' + values.project_flock?.value ? String(values.project_flock.value) : '', + true ); - updateFilter('projectFlockName', values.project_flock?.label || ''); + updateFilter('projectFlockName', values.project_flock?.label || '', true); updateFilter( 'projectFlockKandangId', values.project_flock_kandang?.value ? String(values.project_flock_kandang.value) - : '' + : '', + true ); updateFilter( 'projectFlockKandangName', - values.project_flock_kandang?.label || '' + values.project_flock_kandang?.label || '', + true ); }; From 6ffc2c280609dd2879a7813c642ac8d696d6642d Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Thu, 30 Apr 2026 16:56:47 +0700 Subject: [PATCH 6/6] fix: adjust date filter layout --- .../expense/filter/ExpensesFilterModal.tsx | 59 ++++++++----------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/src/components/pages/expense/filter/ExpensesFilterModal.tsx b/src/components/pages/expense/filter/ExpensesFilterModal.tsx index 67a6f431..fbde8542 100644 --- a/src/components/pages/expense/filter/ExpensesFilterModal.tsx +++ b/src/components/pages/expense/filter/ExpensesFilterModal.tsx @@ -203,40 +203,31 @@ const ExpensesFilterModal = ({ {/* Modal Body */}
-
- Tanggal -
- -
- -
- {formik.touched.realization_date && - formik.errors.realization_date && ( - - {formik.errors.realization_date} - - )} -
+ + +