mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
fix: implement table filter persist state
This commit is contained in:
@@ -7,8 +7,7 @@ import {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { usePathname } from 'next/navigation';
|
import useSWR from 'swr';
|
||||||
import useSWR, { mutate } from 'swr';
|
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { ColumnDef, ColumnSort, SortingState } from '@tanstack/react-table';
|
import { ColumnDef, ColumnSort, SortingState } from '@tanstack/react-table';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
@@ -25,7 +24,6 @@ import { cn, formatNumber, formatDate, formatCurrency } from '@/lib/helper';
|
|||||||
import { InventoryAdjustmentApi } from '@/services/api/inventory';
|
import { InventoryAdjustmentApi } from '@/services/api/inventory';
|
||||||
import { WarehouseApi, ProductApi } from '@/services/api/master-data';
|
import { WarehouseApi, ProductApi } from '@/services/api/master-data';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
import PopoverButton from '@/components/popover/PopoverButton';
|
import PopoverButton from '@/components/popover/PopoverButton';
|
||||||
import PopoverContent from '@/components/popover/PopoverContent';
|
import PopoverContent from '@/components/popover/PopoverContent';
|
||||||
@@ -100,27 +98,31 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const InventoryAdjustmentTable = () => {
|
const InventoryAdjustmentTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
setPage,
|
setPage,
|
||||||
setPageSize,
|
setPageSize,
|
||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter<{
|
||||||
|
search: string;
|
||||||
|
productCategorySort: string;
|
||||||
|
productSort: string;
|
||||||
|
warehouseSort: string;
|
||||||
|
stockSort: string;
|
||||||
|
productFilter?: OptionType<string>;
|
||||||
|
warehouseFilter?: OptionType<string>;
|
||||||
|
transactionTypeFilter?: OptionType<string>;
|
||||||
|
}>({
|
||||||
initial: {
|
initial: {
|
||||||
search: '',
|
search: '',
|
||||||
productCategorySort: '',
|
productCategorySort: '',
|
||||||
productSort: '',
|
productSort: '',
|
||||||
warehouseSort: '',
|
warehouseSort: '',
|
||||||
stockSort: '',
|
stockSort: '',
|
||||||
productFilter: '',
|
productFilter: undefined,
|
||||||
warehouseFilter: '',
|
warehouseFilter: undefined,
|
||||||
transactionTypeFilter: '',
|
transactionTypeFilter: undefined,
|
||||||
productName: '',
|
|
||||||
warehouseName: '',
|
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
@@ -133,7 +135,6 @@ const InventoryAdjustmentTable = () => {
|
|||||||
warehouseFilter: 'warehouse_id',
|
warehouseFilter: 'warehouse_id',
|
||||||
transactionTypeFilter: 'transaction_type',
|
transactionTypeFilter: 'transaction_type',
|
||||||
},
|
},
|
||||||
excludeKeysFromUrl: ['productName', 'warehouseName'],
|
|
||||||
persist: true,
|
persist: true,
|
||||||
storeName: 'inventory-adjustment-table',
|
storeName: 'inventory-adjustment-table',
|
||||||
});
|
});
|
||||||
@@ -144,32 +145,26 @@ const InventoryAdjustmentTable = () => {
|
|||||||
// ===== FORMIK SETUP =====
|
// ===== FORMIK SETUP =====
|
||||||
const formik = useFormik<AdjustmentFilterType>({
|
const formik = useFormik<AdjustmentFilterType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
product_id: null,
|
product: tableFilterState.productFilter,
|
||||||
warehouse_id: null,
|
warehouse: tableFilterState.warehouseFilter,
|
||||||
transaction_type: null,
|
transaction_type: tableFilterState.transactionTypeFilter,
|
||||||
},
|
},
|
||||||
validationSchema: AdjustmentFilterSchema,
|
validationSchema: AdjustmentFilterSchema,
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
updateFilter('productFilter', values.product_id || '');
|
updateFilter('productFilter', values.product || undefined, true);
|
||||||
updateFilter('warehouseFilter', values.warehouse_id || '');
|
updateFilter('warehouseFilter', values.warehouse || undefined, true);
|
||||||
updateFilter('transactionTypeFilter', values.transaction_type || '');
|
|
||||||
updateFilter(
|
updateFilter(
|
||||||
'productName',
|
'transactionTypeFilter',
|
||||||
productIdValue?.label ? String(productIdValue.label) : ''
|
values.transaction_type || undefined,
|
||||||
);
|
true
|
||||||
updateFilter(
|
|
||||||
'warehouseName',
|
|
||||||
warehouseIdValue?.label ? String(warehouseIdValue.label) : ''
|
|
||||||
);
|
);
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
onReset: () => {
|
onReset: () => {
|
||||||
updateFilter('productFilter', '');
|
updateFilter('productFilter', undefined, true);
|
||||||
updateFilter('warehouseFilter', '');
|
updateFilter('warehouseFilter', undefined, true);
|
||||||
updateFilter('transactionTypeFilter', '');
|
updateFilter('transactionTypeFilter', undefined, true);
|
||||||
updateFilter('productName', '');
|
|
||||||
updateFilter('warehouseName', '');
|
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -209,84 +204,28 @@ const InventoryAdjustmentTable = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// ===== FILTER HANDLERS =====
|
// ===== FILTER HANDLERS =====
|
||||||
const handleFilterProductChange = useCallback(
|
const handleFilterProductChange = (val: OptionType | OptionType[] | null) => {
|
||||||
(val: OptionType | OptionType[] | null) => {
|
formik.setFieldValue('product', val);
|
||||||
const product = val as OptionType | null;
|
};
|
||||||
const productId = product?.value ? String(product.value) : null;
|
|
||||||
formik.setFieldValue('product_id', productId);
|
|
||||||
},
|
|
||||||
[formik]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFilterWarehouseChange = (
|
const handleFilterWarehouseChange = (
|
||||||
val: OptionType | OptionType[] | null
|
val: OptionType | OptionType[] | null
|
||||||
) => {
|
) => {
|
||||||
const warehouse = val as OptionType | null;
|
formik.setFieldValue('warehouse', val);
|
||||||
formik.setFieldValue(
|
|
||||||
'warehouse_id',
|
|
||||||
warehouse?.value ? String(warehouse.value) : null
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFilterTransactionTypeChange = useCallback(
|
const handleFilterTransactionTypeChange = (
|
||||||
(val: OptionType | OptionType[] | null) => {
|
val: OptionType | OptionType[] | null
|
||||||
const type = val as OptionType | null;
|
) => {
|
||||||
const typeValue = type?.value ? String(type.value) : null;
|
formik.setFieldValue('transaction_type', val);
|
||||||
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]);
|
|
||||||
|
|
||||||
// ===== HANDLE FILTER MODAL OPEN =====
|
// ===== HANDLE FILTER MODAL OPEN =====
|
||||||
const handleFilterModalOpen = () => {
|
const handleFilterModalOpen = () => {
|
||||||
formik.setValues({
|
formik.setValues({
|
||||||
product_id: tableFilterState.productFilter || null,
|
product: tableFilterState.productFilter ?? undefined,
|
||||||
warehouse_id: tableFilterState.warehouseFilter || null,
|
warehouse: tableFilterState.warehouseFilter ?? undefined,
|
||||||
transaction_type: tableFilterState.transactionTypeFilter || null,
|
transaction_type: tableFilterState.transactionTypeFilter ?? undefined,
|
||||||
});
|
});
|
||||||
filterModal.openModal();
|
filterModal.openModal();
|
||||||
};
|
};
|
||||||
@@ -325,17 +264,8 @@ const InventoryAdjustmentTable = () => {
|
|||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
const singleDeleteModal = useModal();
|
const singleDeleteModal = useModal();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('inventory-adjustment-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const inventoryAdjustmentsColumns: ColumnDef<InventoryAdjustment>[] = useMemo(
|
const inventoryAdjustmentsColumns: ColumnDef<InventoryAdjustment>[] = useMemo(
|
||||||
@@ -647,7 +577,7 @@ const InventoryAdjustmentTable = () => {
|
|||||||
label='Produk'
|
label='Produk'
|
||||||
placeholder='Pilih Produk'
|
placeholder='Pilih Produk'
|
||||||
options={productOptions}
|
options={productOptions}
|
||||||
value={productIdValue}
|
value={formik.values.product}
|
||||||
onChange={handleFilterProductChange}
|
onChange={handleFilterProductChange}
|
||||||
onInputChange={setProductInputValue}
|
onInputChange={setProductInputValue}
|
||||||
isLoading={isLoadingProductOptions}
|
isLoading={isLoadingProductOptions}
|
||||||
@@ -659,7 +589,7 @@ const InventoryAdjustmentTable = () => {
|
|||||||
label='Gudang'
|
label='Gudang'
|
||||||
placeholder='Pilih Gudang'
|
placeholder='Pilih Gudang'
|
||||||
options={warehouseOptions}
|
options={warehouseOptions}
|
||||||
value={warehouseIdValue}
|
value={formik.values.warehouse}
|
||||||
onChange={handleFilterWarehouseChange}
|
onChange={handleFilterWarehouseChange}
|
||||||
onInputChange={setWarehouseInputValue}
|
onInputChange={setWarehouseInputValue}
|
||||||
isLoading={isLoadingWarehouseOptions}
|
isLoading={isLoadingWarehouseOptions}
|
||||||
@@ -671,7 +601,7 @@ const InventoryAdjustmentTable = () => {
|
|||||||
label='Tipe Transaksi'
|
label='Tipe Transaksi'
|
||||||
placeholder='Pilih Tipe Transaksi'
|
placeholder='Pilih Tipe Transaksi'
|
||||||
options={transactionTypeOptions}
|
options={transactionTypeOptions}
|
||||||
value={transactionTypeValue}
|
value={formik.values.transaction_type}
|
||||||
onChange={handleFilterTransactionTypeChange}
|
onChange={handleFilterTransactionTypeChange}
|
||||||
isClearable
|
isClearable
|
||||||
className={{ wrapper: 'w-full' }}
|
className={{ wrapper: 'w-full' }}
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
import { string, object } from 'yup';
|
import { OptionType } from '@/components/input/SelectInput';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
export const AdjustmentFilterSchema = object().shape({
|
export const AdjustmentFilterSchema = Yup.object().shape({
|
||||||
product_id: string().nullable(),
|
product: Yup.object({
|
||||||
warehouse_id: string().nullable(),
|
value: Yup.string().nullable(),
|
||||||
transaction_type: 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 = {
|
export type AdjustmentFilterType = {
|
||||||
product_id: string | null;
|
product?: OptionType<string>;
|
||||||
warehouse_id: string | null;
|
warehouse?: OptionType<string>;
|
||||||
transaction_type: string | null;
|
transaction_type?: OptionType<string>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import {
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
ChangeEventHandler,
|
import useSWR from 'swr';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import useSWR, { mutate } from 'swr';
|
|
||||||
import { SortingState, CellContext, ColumnDef } from '@tanstack/react-table';
|
import { SortingState, CellContext, ColumnDef } from '@tanstack/react-table';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
|
|
||||||
@@ -20,7 +13,6 @@ import { WarehouseApi, ProductApi } from '@/services/api/master-data';
|
|||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
@@ -108,22 +100,21 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const MovementTable = () => {
|
const MovementTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
setPage,
|
setPage,
|
||||||
setPageSize,
|
setPageSize,
|
||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter<{
|
||||||
|
search: string;
|
||||||
|
productFilter?: OptionType<string>;
|
||||||
|
warehouseFilter?: OptionType<string>;
|
||||||
|
}>({
|
||||||
initial: {
|
initial: {
|
||||||
search: '',
|
search: '',
|
||||||
productFilter: '',
|
productFilter: undefined,
|
||||||
warehouseFilter: '',
|
warehouseFilter: undefined,
|
||||||
productName: '',
|
|
||||||
warehouseName: '',
|
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
@@ -131,7 +122,6 @@ const MovementTable = () => {
|
|||||||
productFilter: 'product_id',
|
productFilter: 'product_id',
|
||||||
warehouseFilter: 'warehouse_id',
|
warehouseFilter: 'warehouse_id',
|
||||||
},
|
},
|
||||||
excludeKeysFromUrl: ['productName', 'warehouseName'],
|
|
||||||
persist: true,
|
persist: true,
|
||||||
storeName: 'movement-table',
|
storeName: 'movement-table',
|
||||||
});
|
});
|
||||||
@@ -142,29 +132,19 @@ const MovementTable = () => {
|
|||||||
// ===== FORMIK SETUP =====
|
// ===== FORMIK SETUP =====
|
||||||
const formik = useFormik<MovementFilterType>({
|
const formik = useFormik<MovementFilterType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
product_id: null,
|
product: tableFilterState.productFilter,
|
||||||
warehouse_id: null,
|
warehouse: tableFilterState.warehouseFilter,
|
||||||
},
|
},
|
||||||
validationSchema: MovementFilterSchema,
|
validationSchema: MovementFilterSchema,
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
updateFilter('productFilter', values.product_id || '');
|
updateFilter('productFilter', values.product || undefined, true);
|
||||||
updateFilter('warehouseFilter', values.warehouse_id || '');
|
updateFilter('warehouseFilter', values.warehouse || undefined, true);
|
||||||
updateFilter(
|
|
||||||
'productName',
|
|
||||||
productIdValue?.label ? String(productIdValue.label) : ''
|
|
||||||
);
|
|
||||||
updateFilter(
|
|
||||||
'warehouseName',
|
|
||||||
warehouseIdValue?.label ? String(warehouseIdValue.label) : ''
|
|
||||||
);
|
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
onReset: () => {
|
onReset: () => {
|
||||||
updateFilter('productFilter', '');
|
updateFilter('productFilter', undefined, true);
|
||||||
updateFilter('warehouseFilter', '');
|
updateFilter('warehouseFilter', undefined, true);
|
||||||
updateFilter('productName', '');
|
|
||||||
updateFilter('warehouseName', '');
|
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -196,64 +176,21 @@ const MovementTable = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ===== FILTER HANDLERS =====
|
// ===== FILTER HANDLERS =====
|
||||||
const handleFilterProductChange = useCallback(
|
const handleFilterProductChange = (val: OptionType | OptionType[] | null) => {
|
||||||
(val: OptionType | OptionType[] | null) => {
|
formik.setFieldValue('product', val);
|
||||||
const product = val as OptionType | null;
|
|
||||||
const productId = product?.value ? String(product.value) : null;
|
|
||||||
formik.setFieldValue('product_id', productId);
|
|
||||||
},
|
|
||||||
[formik]
|
|
||||||
);
|
|
||||||
|
|
||||||
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(() => {
|
const handleFilterWarehouseChange = (
|
||||||
if (!formik.values.warehouse_id) return null;
|
val: OptionType | OptionType[] | null
|
||||||
const found = warehouseOptions.find(
|
) => {
|
||||||
(opt) => String(opt.value) === formik.values.warehouse_id
|
formik.setFieldValue('warehouse', val);
|
||||||
);
|
|
||||||
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,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// ===== HANDLE FILTER MODAL OPEN =====
|
// ===== HANDLE FILTER MODAL OPEN =====
|
||||||
const handleFilterModalOpen = () => {
|
const handleFilterModalOpen = () => {
|
||||||
formik.setValues({
|
formik.setValues({
|
||||||
product_id: tableFilterState.productFilter || null,
|
product: tableFilterState.productFilter ?? undefined,
|
||||||
warehouse_id: tableFilterState.warehouseFilter || null,
|
warehouse: tableFilterState.warehouseFilter ?? undefined,
|
||||||
});
|
});
|
||||||
filterModal.openModal();
|
filterModal.openModal();
|
||||||
};
|
};
|
||||||
@@ -290,17 +227,8 @@ const MovementTable = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('movement-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const movementColumns: ColumnDef<Movement>[] = useMemo(
|
const movementColumns: ColumnDef<Movement>[] = useMemo(
|
||||||
@@ -419,13 +347,7 @@ const MovementTable = () => {
|
|||||||
|
|
||||||
<ButtonFilter
|
<ButtonFilter
|
||||||
values={tableFilterState}
|
values={tableFilterState}
|
||||||
excludeFields={[
|
excludeFields={['page', 'pageSize', 'search']}
|
||||||
'page',
|
|
||||||
'pageSize',
|
|
||||||
'search',
|
|
||||||
'productName',
|
|
||||||
'warehouseName',
|
|
||||||
]}
|
|
||||||
onClick={handleFilterModalOpen}
|
onClick={handleFilterModalOpen}
|
||||||
className='px-3 py-2.5'
|
className='px-3 py-2.5'
|
||||||
/>
|
/>
|
||||||
@@ -505,7 +427,7 @@ const MovementTable = () => {
|
|||||||
label='Produk'
|
label='Produk'
|
||||||
placeholder='Pilih Produk'
|
placeholder='Pilih Produk'
|
||||||
options={productOptions}
|
options={productOptions}
|
||||||
value={productIdValue}
|
value={formik.values.product}
|
||||||
onChange={handleFilterProductChange}
|
onChange={handleFilterProductChange}
|
||||||
onInputChange={setProductInputValue}
|
onInputChange={setProductInputValue}
|
||||||
isLoading={isLoadingProductOptions}
|
isLoading={isLoadingProductOptions}
|
||||||
@@ -517,7 +439,7 @@ const MovementTable = () => {
|
|||||||
label='Gudang'
|
label='Gudang'
|
||||||
placeholder='Pilih Gudang'
|
placeholder='Pilih Gudang'
|
||||||
options={warehouseOptions}
|
options={warehouseOptions}
|
||||||
value={warehouseIdValue}
|
value={formik.values.warehouse}
|
||||||
onChange={handleFilterWarehouseChange}
|
onChange={handleFilterWarehouseChange}
|
||||||
onInputChange={setWarehouseInputValue}
|
onInputChange={setWarehouseInputValue}
|
||||||
isLoading={isLoadingWarehouseOptions}
|
isLoading={isLoadingWarehouseOptions}
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
import { string, object } from 'yup';
|
import { OptionType } from '@/components/input/SelectInput';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
export const MovementFilterSchema = object().shape({
|
export const MovementFilterSchema = Yup.object().shape({
|
||||||
product_id: string().nullable(),
|
product: Yup.object({
|
||||||
warehouse_id: string().nullable(),
|
value: Yup.string().nullable(),
|
||||||
|
label: Yup.string().nullable(),
|
||||||
|
}).nullable(),
|
||||||
|
warehouse: Yup.object({
|
||||||
|
value: Yup.string().nullable(),
|
||||||
|
label: Yup.string().nullable(),
|
||||||
|
}).nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type MovementFilterType = {
|
export type MovementFilterType = {
|
||||||
product_id: string | null;
|
product?: OptionType<string>;
|
||||||
warehouse_id: string | null;
|
warehouse?: OptionType<string>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,16 +13,14 @@ import { cn, formatCurrency, formatNumber } from '@/lib/helper';
|
|||||||
import { InventoryProductApi } from '@/services/api/inventory';
|
import { InventoryProductApi } from '@/services/api/inventory';
|
||||||
import { ProductCategoryApi } from '@/services/api/master-data';
|
import { ProductCategoryApi } from '@/services/api/master-data';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
|
||||||
import { InventoryProduct } from '@/types/api/inventory/product';
|
import { InventoryProduct } from '@/types/api/inventory/product';
|
||||||
import { ProductCategory } from '@/types/api/master-data/product-category';
|
import { ProductCategory } from '@/types/api/master-data/product-category';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
import { usePathname } from 'next/navigation';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import { object, string } from 'yup';
|
import * as Yup from 'yup';
|
||||||
import PopoverButton from '@/components/popover/PopoverButton';
|
import PopoverButton from '@/components/popover/PopoverButton';
|
||||||
import PopoverContent from '@/components/popover/PopoverContent';
|
import PopoverContent from '@/components/popover/PopoverContent';
|
||||||
import InventoryProductTableSkeleton from '@/components/pages/inventory/product/skeleton/InventoryProductTableSkeleton';
|
import InventoryProductTableSkeleton from '@/components/pages/inventory/product/skeleton/InventoryProductTableSkeleton';
|
||||||
@@ -79,27 +77,25 @@ const RowOptionsMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const InventoryProductTable = () => {
|
const InventoryProductTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
setPage,
|
setPage,
|
||||||
setPageSize,
|
setPageSize,
|
||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter<{
|
||||||
|
search: string;
|
||||||
|
categoryFilter?: OptionType<string>;
|
||||||
|
}>({
|
||||||
initial: {
|
initial: {
|
||||||
search: '',
|
search: '',
|
||||||
categoryFilter: '',
|
categoryFilter: undefined,
|
||||||
categoryName: '',
|
|
||||||
},
|
},
|
||||||
paramMap: {
|
paramMap: {
|
||||||
page: 'page',
|
page: 'page',
|
||||||
pageSize: 'limit',
|
pageSize: 'limit',
|
||||||
categoryFilter: 'product_category_id',
|
categoryFilter: 'product_category_id',
|
||||||
},
|
},
|
||||||
excludeKeysFromUrl: ['categoryName'],
|
|
||||||
persist: true,
|
persist: true,
|
||||||
storeName: 'inventory-product-table',
|
storeName: 'inventory-product-table',
|
||||||
});
|
});
|
||||||
@@ -108,21 +104,21 @@ const InventoryProductTable = () => {
|
|||||||
const filterModal = useModal();
|
const filterModal = useModal();
|
||||||
|
|
||||||
// ===== FORMIK SETUP =====
|
// ===== FORMIK SETUP =====
|
||||||
const formik = useFormik<{ category_id: string | null }>({
|
const formik = useFormik<{ category?: OptionType<string> }>({
|
||||||
initialValues: { category_id: null },
|
initialValues: { category: tableFilterState.categoryFilter },
|
||||||
validationSchema: object().shape({ category_id: string().nullable() }),
|
validationSchema: Yup.object().shape({
|
||||||
|
category: Yup.object({
|
||||||
|
value: Yup.string().nullable(),
|
||||||
|
label: Yup.string().nullable(),
|
||||||
|
}).nullable(),
|
||||||
|
}),
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
updateFilter('categoryFilter', values.category_id || '');
|
updateFilter('categoryFilter', values.category || undefined, true);
|
||||||
updateFilter(
|
|
||||||
'categoryName',
|
|
||||||
categoryIdValue?.label ? String(categoryIdValue.label) : ''
|
|
||||||
);
|
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
onReset: () => {
|
onReset: () => {
|
||||||
updateFilter('categoryFilter', '');
|
updateFilter('categoryFilter', undefined, true);
|
||||||
updateFilter('categoryName', '');
|
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -140,40 +136,18 @@ const InventoryProductTable = () => {
|
|||||||
'search'
|
'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 =====
|
// ===== HANDLE FILTER MODAL OPEN =====
|
||||||
const handleFilterModalOpen = () => {
|
const handleFilterModalOpen = () => {
|
||||||
formik.setValues({ category_id: tableFilterState.categoryFilter || null });
|
formik.setValues({
|
||||||
|
category: tableFilterState.categoryFilter ?? undefined,
|
||||||
|
});
|
||||||
filterModal.openModal();
|
filterModal.openModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFilterCategoryChange = (
|
const handleFilterCategoryChange = (
|
||||||
val: OptionType | OptionType[] | null
|
val: OptionType | OptionType[] | null
|
||||||
) => {
|
) => {
|
||||||
const category = val as OptionType | null;
|
formik.setFieldValue('category', val);
|
||||||
formik.setFieldValue(
|
|
||||||
'category_id',
|
|
||||||
category?.value ? String(category.value) : null
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
@@ -183,17 +157,8 @@ const InventoryProductTable = () => {
|
|||||||
InventoryProductApi.getAllFetcher
|
InventoryProductApi.getAllFetcher
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTableState('inventory-product-table', pathname);
|
|
||||||
}, [pathname, setTableState]);
|
|
||||||
|
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
setSearchValue(e.target.value);
|
updateFilter('search', e.target.value, true);
|
||||||
updateFilter('search', e.target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns: ColumnDef<InventoryProduct>[] = useMemo(
|
const columns: ColumnDef<InventoryProduct>[] = useMemo(
|
||||||
@@ -309,7 +274,7 @@ const InventoryProductTable = () => {
|
|||||||
/>
|
/>
|
||||||
<ButtonFilter
|
<ButtonFilter
|
||||||
values={tableFilterState}
|
values={tableFilterState}
|
||||||
excludeFields={['page', 'pageSize', 'search', 'categoryName']}
|
excludeFields={['page', 'pageSize', 'search']}
|
||||||
onClick={handleFilterModalOpen}
|
onClick={handleFilterModalOpen}
|
||||||
className='px-3 py-2.5'
|
className='px-3 py-2.5'
|
||||||
/>
|
/>
|
||||||
@@ -397,7 +362,7 @@ const InventoryProductTable = () => {
|
|||||||
label='Kategori Produk'
|
label='Kategori Produk'
|
||||||
placeholder='Pilih Kategori'
|
placeholder='Pilih Kategori'
|
||||||
options={categoryOptions}
|
options={categoryOptions}
|
||||||
value={categoryIdValue}
|
value={formik.values.category}
|
||||||
onChange={handleFilterCategoryChange}
|
onChange={handleFilterCategoryChange}
|
||||||
onInputChange={setCategoryInputValue}
|
onInputChange={setCategoryInputValue}
|
||||||
isLoading={isLoadingCategoryOptions}
|
isLoading={isLoadingCategoryOptions}
|
||||||
|
|||||||
Reference in New Issue
Block a user