mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Merge branch 'codex/filter-improment' into 'development'
feat: add more filters See merge request mbugroup/lti-web-client!430
This commit is contained in:
@@ -39,6 +39,7 @@ import PopoverContent from '@/components/popover/PopoverContent';
|
||||
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,
|
||||
@@ -195,6 +196,9 @@ const FinanceTable = () => {
|
||||
sortBy: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
bankNames: '',
|
||||
customerNames: '',
|
||||
supplierNames: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
@@ -207,6 +211,9 @@ const FinanceTable = () => {
|
||||
startDate: 'start_date',
|
||||
endDate: 'end_date',
|
||||
},
|
||||
excludeKeysFromUrl: ['bankNames', 'customerNames', 'supplierNames'],
|
||||
persist: true,
|
||||
storeName: 'finance-table',
|
||||
});
|
||||
|
||||
// ===== FILTER MODAL STATE =====
|
||||
@@ -256,9 +263,20 @@ const FinanceTable = () => {
|
||||
updateFilter('sortBy', values.sort_by);
|
||||
updateFilter('startDate', values.start_date);
|
||||
updateFilter('endDate', values.end_date);
|
||||
// Save display names for restoration on modal reopen
|
||||
const toNames = (val: OptionType | OptionType[] | null) =>
|
||||
val ? (Array.isArray(val) ? val : [val]).map((o) => String(o.label)).join(',') : '';
|
||||
updateFilter('bankNames', toNames(selectedBank));
|
||||
updateFilter('customerNames', toNames(selectedCustomerId));
|
||||
updateFilter('supplierNames', toNames(selectedSupplierId));
|
||||
filterModal.closeModal();
|
||||
},
|
||||
onReset: () => {
|
||||
setSelectedTransactionType(null);
|
||||
setSelectedBank(null);
|
||||
setSelectedCustomerId(null);
|
||||
setSelectedSupplierId(null);
|
||||
setSelectedSortBy(null);
|
||||
updateFilter('search', '');
|
||||
resetSearchValue();
|
||||
updateFilter('transactionTypes', '');
|
||||
@@ -268,6 +286,10 @@ const FinanceTable = () => {
|
||||
updateFilter('sortBy', '');
|
||||
updateFilter('startDate', '');
|
||||
updateFilter('endDate', '');
|
||||
updateFilter('bankNames', '');
|
||||
updateFilter('customerNames', '');
|
||||
updateFilter('supplierNames', '');
|
||||
filterModal.closeModal();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -320,31 +342,6 @@ const FinanceTable = () => {
|
||||
});
|
||||
}, [bankOptions, bankRawData]);
|
||||
|
||||
// ===== ACTIVE FILTERS COUNT =====
|
||||
const activeFiltersCount = useMemo(() => {
|
||||
let count = 0;
|
||||
|
||||
if (tableFilterState.transactionTypes) count += 1;
|
||||
if (tableFilterState.bankIds) count += 1;
|
||||
if (tableFilterState.customerIds) count += 1;
|
||||
if (tableFilterState.supplierIds) count += 1;
|
||||
if (tableFilterState.sortBy) count += 1;
|
||||
if (tableFilterState.startDate) count += 1;
|
||||
if (tableFilterState.endDate) count += 1;
|
||||
|
||||
return count;
|
||||
}, [
|
||||
tableFilterState.transactionTypes,
|
||||
tableFilterState.bankIds,
|
||||
tableFilterState.customerIds,
|
||||
tableFilterState.supplierIds,
|
||||
tableFilterState.sortBy,
|
||||
tableFilterState.startDate,
|
||||
tableFilterState.endDate,
|
||||
]);
|
||||
|
||||
const hasFilters = activeFiltersCount > 0;
|
||||
|
||||
// ===== Handler =====
|
||||
const searchChangeHandler = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -469,28 +466,73 @@ const FinanceTable = () => {
|
||||
};
|
||||
|
||||
const handleFilterModalOpen = () => {
|
||||
// Restore transaction types from stored comma-separated IDs
|
||||
const txIds = tableFilterState.transactionTypes
|
||||
? tableFilterState.transactionTypes.split(',')
|
||||
: [];
|
||||
const restoredTxTypes = FINANCE_TRANSACTION_TYPE_OPTIONS.filter((opt) =>
|
||||
txIds.includes(String(opt.value))
|
||||
);
|
||||
setSelectedTransactionType(restoredTxTypes.length ? restoredTxTypes : null);
|
||||
|
||||
// Restore banks from stored IDs and names
|
||||
const bankIdList = tableFilterState.bankIds
|
||||
? tableFilterState.bankIds.split(',')
|
||||
: [];
|
||||
const bankNameList = tableFilterState.bankNames
|
||||
? tableFilterState.bankNames.split(',')
|
||||
: [];
|
||||
const restoredBanks = bankIdList.map((id, i) => ({
|
||||
value: id,
|
||||
label: bankNameList[i] || id,
|
||||
}));
|
||||
setSelectedBank(restoredBanks.length ? restoredBanks : null);
|
||||
|
||||
// Restore customers from stored IDs and names
|
||||
const customerIdList = tableFilterState.customerIds
|
||||
? tableFilterState.customerIds.split(',')
|
||||
: [];
|
||||
const customerNameList = tableFilterState.customerNames
|
||||
? tableFilterState.customerNames.split(',')
|
||||
: [];
|
||||
const restoredCustomers = customerIdList.map((id, i) => ({
|
||||
value: id,
|
||||
label: customerNameList[i] || id,
|
||||
}));
|
||||
setSelectedCustomerId(restoredCustomers.length ? restoredCustomers : null);
|
||||
|
||||
// Restore suppliers from stored IDs and names
|
||||
const supplierIdList = tableFilterState.supplierIds
|
||||
? tableFilterState.supplierIds.split(',')
|
||||
: [];
|
||||
const supplierNameList = tableFilterState.supplierNames
|
||||
? tableFilterState.supplierNames.split(',')
|
||||
: [];
|
||||
const restoredSuppliers = supplierIdList.map((id, i) => ({
|
||||
value: id,
|
||||
label: supplierNameList[i] || id,
|
||||
}));
|
||||
setSelectedSupplierId(restoredSuppliers.length ? restoredSuppliers : null);
|
||||
|
||||
// Restore sort by
|
||||
const restoredSortBy =
|
||||
sortByOptions.find((opt) => String(opt.value) === tableFilterState.sortBy) ||
|
||||
null;
|
||||
setSelectedSortBy(restoredSortBy);
|
||||
|
||||
// Restore formik values
|
||||
filterFormik.setValues({
|
||||
search: tableFilterState.search || '',
|
||||
transaction_types: tableFilterState.transactionTypes || '',
|
||||
bank_ids: tableFilterState.bankIds || '',
|
||||
customer_ids: tableFilterState.customerIds || '',
|
||||
supplier_ids: tableFilterState.supplierIds || '',
|
||||
sort_by: tableFilterState.sortBy || '',
|
||||
start_date: tableFilterState.startDate || '',
|
||||
end_date: tableFilterState.endDate || '',
|
||||
});
|
||||
|
||||
filterModal.openModal();
|
||||
filterFormik.validateForm();
|
||||
};
|
||||
|
||||
const resetFilterHandler = () => {
|
||||
setSelectedTransactionType(null);
|
||||
setSelectedBank(null);
|
||||
setSelectedCustomerId(null);
|
||||
setSelectedSupplierId(null);
|
||||
setSelectedSortBy(null);
|
||||
|
||||
filterFormik.resetForm();
|
||||
|
||||
updateFilter('search', '');
|
||||
resetSearchValue();
|
||||
updateFilter('transactionTypes', '');
|
||||
updateFilter('bankIds', '');
|
||||
updateFilter('customerIds', '');
|
||||
updateFilter('supplierIds', '');
|
||||
updateFilter('sortBy', '');
|
||||
updateFilter('startDate', '');
|
||||
updateFilter('endDate', '');
|
||||
};
|
||||
|
||||
const confirmationModalDeleteClickHandler = async () => {
|
||||
@@ -687,25 +729,19 @@ const FinanceTable = () => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant='outline'
|
||||
color='none'
|
||||
<ButtonFilter
|
||||
values={tableFilterState}
|
||||
excludeFields={[
|
||||
'page',
|
||||
'pageSize',
|
||||
'search',
|
||||
'bankNames',
|
||||
'customerNames',
|
||||
'supplierNames',
|
||||
]}
|
||||
onClick={handleFilterModalOpen}
|
||||
className={cn(
|
||||
'px-3 py-2.5 gap-1.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft transition-all',
|
||||
{
|
||||
'border-primary-gradient text-primary': hasFilters,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||||
Filter
|
||||
{hasFilters && (
|
||||
<span className='w-5 h-5 text-white bg-[#FF3535] rounded-lg border border-base-300 flex items-center justify-center text-xs'>
|
||||
{activeFiltersCount}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
className='px-3 py-2.5'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -874,19 +910,9 @@ const FinanceTable = () => {
|
||||
{/* Modal Footer */}
|
||||
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
|
||||
<Button
|
||||
type='button'
|
||||
type='reset'
|
||||
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={() => {
|
||||
filterFormik.resetForm();
|
||||
setSelectedTransactionType(null);
|
||||
setSelectedBank(null);
|
||||
setSelectedCustomerId(null);
|
||||
setSelectedSupplierId(null);
|
||||
setSelectedSortBy(null);
|
||||
resetFilterHandler();
|
||||
filterModal.closeModal();
|
||||
}}
|
||||
>
|
||||
Reset Filter
|
||||
</Button>
|
||||
|
||||
@@ -119,6 +119,8 @@ const InventoryAdjustmentTable = () => {
|
||||
productFilter: '',
|
||||
warehouseFilter: '',
|
||||
transactionTypeFilter: '',
|
||||
productName: '',
|
||||
warehouseName: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
@@ -131,6 +133,9 @@ const InventoryAdjustmentTable = () => {
|
||||
warehouseFilter: 'warehouse_id',
|
||||
transactionTypeFilter: 'transaction_type',
|
||||
},
|
||||
excludeKeysFromUrl: ['productName', 'warehouseName'],
|
||||
persist: true,
|
||||
storeName: 'inventory-adjustment-table',
|
||||
});
|
||||
|
||||
// ===== FILTER MODAL STATE =====
|
||||
@@ -140,14 +145,16 @@ const InventoryAdjustmentTable = () => {
|
||||
const formik = useFormik<AdjustmentFilterType>({
|
||||
initialValues: {
|
||||
product_id: null,
|
||||
warehouse: null,
|
||||
warehouse_id: null,
|
||||
transaction_type: null,
|
||||
},
|
||||
validationSchema: AdjustmentFilterSchema,
|
||||
onSubmit: (values, { setSubmitting }) => {
|
||||
updateFilter('productFilter', values.product_id || '');
|
||||
updateFilter('warehouseFilter', String(values.warehouse?.value) || '');
|
||||
updateFilter('warehouseFilter', values.warehouse_id || '');
|
||||
updateFilter('transactionTypeFilter', values.transaction_type || '');
|
||||
updateFilter('productName', productIdValue?.label ? String(productIdValue.label) : '');
|
||||
updateFilter('warehouseName', warehouseIdValue?.label ? String(warehouseIdValue.label) : '');
|
||||
filterModal.closeModal();
|
||||
setSubmitting(false);
|
||||
},
|
||||
@@ -155,6 +162,9 @@ const InventoryAdjustmentTable = () => {
|
||||
updateFilter('productFilter', '');
|
||||
updateFilter('warehouseFilter', '');
|
||||
updateFilter('transactionTypeFilter', '');
|
||||
updateFilter('productName', '');
|
||||
updateFilter('warehouseName', '');
|
||||
filterModal.closeModal();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -205,7 +215,8 @@ const InventoryAdjustmentTable = () => {
|
||||
const handleFilterWarehouseChange = (
|
||||
val: OptionType | OptionType[] | null
|
||||
) => {
|
||||
formik.setFieldValue('warehouse', val);
|
||||
const warehouse = val as OptionType | null;
|
||||
formik.setFieldValue('warehouse_id', warehouse?.value ? String(warehouse.value) : null);
|
||||
};
|
||||
|
||||
const handleFilterTransactionTypeChange = useCallback(
|
||||
@@ -220,12 +231,27 @@ const InventoryAdjustmentTable = () => {
|
||||
// ===== FILTER HELPERS =====
|
||||
const productIdValue = useMemo(() => {
|
||||
if (!formik.values.product_id) return null;
|
||||
return (
|
||||
productOptions.find(
|
||||
(opt) => String(opt.value) === formik.values.product_id
|
||||
) || null
|
||||
const found = productOptions.find(
|
||||
(opt) => String(opt.value) === formik.values.product_id
|
||||
);
|
||||
}, [formik.values.product_id, productOptions]);
|
||||
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;
|
||||
@@ -238,8 +264,12 @@ const InventoryAdjustmentTable = () => {
|
||||
|
||||
// ===== HANDLE FILTER MODAL OPEN =====
|
||||
const handleFilterModalOpen = () => {
|
||||
formik.setValues({
|
||||
product_id: tableFilterState.productFilter || null,
|
||||
warehouse_id: tableFilterState.warehouseFilter || null,
|
||||
transaction_type: tableFilterState.transactionTypeFilter || null,
|
||||
});
|
||||
filterModal.openModal();
|
||||
formik.validateForm();
|
||||
};
|
||||
|
||||
const {
|
||||
@@ -507,6 +537,8 @@ const InventoryAdjustmentTable = () => {
|
||||
'productSort',
|
||||
'warehouseSort',
|
||||
'stockSort',
|
||||
'productName',
|
||||
'warehouseName',
|
||||
]}
|
||||
onClick={handleFilterModalOpen}
|
||||
className='px-3 py-2.5'
|
||||
@@ -608,7 +640,7 @@ const InventoryAdjustmentTable = () => {
|
||||
label='Gudang'
|
||||
placeholder='Pilih Gudang'
|
||||
options={warehouseOptions}
|
||||
value={formik.values.warehouse}
|
||||
value={warehouseIdValue}
|
||||
onChange={handleFilterWarehouseChange}
|
||||
onInputChange={setWarehouseInputValue}
|
||||
isLoading={isLoadingWarehouseOptions}
|
||||
@@ -630,13 +662,9 @@ const InventoryAdjustmentTable = () => {
|
||||
{/* Modal Footer */}
|
||||
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
|
||||
<Button
|
||||
type='button'
|
||||
type='reset'
|
||||
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();
|
||||
}}
|
||||
>
|
||||
Reset Filter
|
||||
</Button>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { string, object } from 'yup';
|
||||
import { OptionType } from '@/components/input/SelectInput';
|
||||
|
||||
export const AdjustmentFilterSchema = object().shape({
|
||||
product_id: string().nullable(),
|
||||
@@ -9,6 +8,6 @@ export const AdjustmentFilterSchema = object().shape({
|
||||
|
||||
export type AdjustmentFilterType = {
|
||||
product_id: string | null;
|
||||
warehouse_id: string | null;
|
||||
transaction_type: string | null;
|
||||
warehouse: OptionType<number> | null;
|
||||
};
|
||||
|
||||
@@ -122,6 +122,8 @@ const MovementTable = () => {
|
||||
search: '',
|
||||
productFilter: '',
|
||||
warehouseFilter: '',
|
||||
productName: '',
|
||||
warehouseName: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
@@ -129,6 +131,9 @@ const MovementTable = () => {
|
||||
productFilter: 'product_id',
|
||||
warehouseFilter: 'warehouse_id',
|
||||
},
|
||||
excludeKeysFromUrl: ['productName', 'warehouseName'],
|
||||
persist: true,
|
||||
storeName: 'movement-table',
|
||||
});
|
||||
|
||||
// ===== FILTER MODAL STATE =====
|
||||
@@ -144,12 +149,17 @@ const MovementTable = () => {
|
||||
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) : '');
|
||||
filterModal.closeModal();
|
||||
setSubmitting(false);
|
||||
},
|
||||
onReset: () => {
|
||||
updateFilter('productFilter', '');
|
||||
updateFilter('warehouseFilter', '');
|
||||
updateFilter('productName', '');
|
||||
updateFilter('warehouseName', '');
|
||||
filterModal.closeModal();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -201,26 +211,35 @@ const MovementTable = () => {
|
||||
// ===== FILTER HELPERS =====
|
||||
const productIdValue = useMemo(() => {
|
||||
if (!formik.values.product_id) return null;
|
||||
return (
|
||||
productOptions.find(
|
||||
(opt) => String(opt.value) === formik.values.product_id
|
||||
) || null
|
||||
const found = productOptions.find(
|
||||
(opt) => String(opt.value) === formik.values.product_id
|
||||
);
|
||||
}, [formik.values.product_id, productOptions]);
|
||||
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;
|
||||
return (
|
||||
warehouseOptions.find(
|
||||
(opt) => String(opt.value) === formik.values.warehouse_id
|
||||
) || null
|
||||
const found = warehouseOptions.find(
|
||||
(opt) => String(opt.value) === formik.values.warehouse_id
|
||||
);
|
||||
}, [formik.values.warehouse_id, warehouseOptions]);
|
||||
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 =====
|
||||
const handleFilterModalOpen = () => {
|
||||
formik.setValues({
|
||||
product_id: tableFilterState.productFilter || null,
|
||||
warehouse_id: tableFilterState.warehouseFilter || null,
|
||||
});
|
||||
filterModal.openModal();
|
||||
formik.validateForm();
|
||||
};
|
||||
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
@@ -384,7 +403,7 @@ const MovementTable = () => {
|
||||
|
||||
<ButtonFilter
|
||||
values={tableFilterState}
|
||||
excludeFields={['page', 'pageSize', 'search']}
|
||||
excludeFields={['page', 'pageSize', 'search', 'productName', 'warehouseName']}
|
||||
onClick={handleFilterModalOpen}
|
||||
className='px-3 py-2.5'
|
||||
/>
|
||||
@@ -489,13 +508,9 @@ const MovementTable = () => {
|
||||
{/* Modal Footer */}
|
||||
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
|
||||
<Button
|
||||
type='button'
|
||||
type='reset'
|
||||
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();
|
||||
}}
|
||||
>
|
||||
Reset Filter
|
||||
</Button>
|
||||
|
||||
@@ -4,17 +4,25 @@ import Button from '@/components/Button';
|
||||
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||
import Table from '@/components/Table';
|
||||
import RequirePermission from '@/components/helper/RequirePermission';
|
||||
import ButtonFilter from '@/components/helper/ButtonFilter';
|
||||
import Modal, { useModal } from '@/components/Modal';
|
||||
import SelectInput, { useSelect } from '@/components/input/SelectInput';
|
||||
import { OptionType } from '@/components/input/SelectInput';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
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 useSWR from 'swr';
|
||||
import { useFormik } from 'formik';
|
||||
import { object, string } from 'yup';
|
||||
import PopoverButton from '@/components/popover/PopoverButton';
|
||||
import PopoverContent from '@/components/popover/PopoverContent';
|
||||
import InventoryProductTableSkeleton from '@/components/pages/inventory/product/skeleton/InventoryProductTableSkeleton';
|
||||
@@ -83,13 +91,76 @@ const InventoryProductTable = () => {
|
||||
} = useTableFilter({
|
||||
initial: {
|
||||
search: '',
|
||||
categoryFilter: '',
|
||||
categoryName: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
pageSize: 'limit',
|
||||
categoryFilter: 'product_category_id',
|
||||
},
|
||||
excludeKeysFromUrl: ['categoryName'],
|
||||
persist: true,
|
||||
storeName: 'inventory-product-table',
|
||||
});
|
||||
|
||||
// ===== FILTER MODAL STATE =====
|
||||
const filterModal = useModal();
|
||||
|
||||
// ===== FORMIK SETUP =====
|
||||
const formik = useFormik<{ category_id: string | null }>({
|
||||
initialValues: { category_id: null },
|
||||
validationSchema: object().shape({ category_id: string().nullable() }),
|
||||
onSubmit: (values, { setSubmitting }) => {
|
||||
updateFilter('categoryFilter', values.category_id || '');
|
||||
updateFilter('categoryName', categoryIdValue?.label ? String(categoryIdValue.label) : '');
|
||||
filterModal.closeModal();
|
||||
setSubmitting(false);
|
||||
},
|
||||
onReset: () => {
|
||||
updateFilter('categoryFilter', '');
|
||||
updateFilter('categoryName', '');
|
||||
filterModal.closeModal();
|
||||
},
|
||||
});
|
||||
|
||||
// ===== CATEGORY OPTIONS =====
|
||||
const {
|
||||
setInputValue: setCategoryInputValue,
|
||||
options: categoryOptions,
|
||||
isLoadingOptions: isLoadingCategoryOptions,
|
||||
loadMore: loadMoreCategories,
|
||||
} = useSelect<ProductCategory>(
|
||||
filterModal.open ? ProductCategoryApi.basePath : null,
|
||||
'id',
|
||||
'name',
|
||||
'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 });
|
||||
filterModal.openModal();
|
||||
};
|
||||
|
||||
const handleFilterCategoryChange = (val: OptionType | OptionType[] | null) => {
|
||||
const category = val as OptionType | null;
|
||||
formik.setFieldValue('category_id', category?.value ? String(category.value) : null);
|
||||
};
|
||||
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
|
||||
const { data: inventoryProducts, isLoading } = useSWR(
|
||||
@@ -182,6 +253,7 @@ const InventoryProductTable = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='w-full'>
|
||||
{/* Header Section */}
|
||||
<div className='w-full p-3 flex flex-row justify-between gap-3 flex-wrap border-b border-base-content/10'>
|
||||
@@ -199,7 +271,7 @@ const InventoryProductTable = () => {
|
||||
</RequirePermission>
|
||||
</div>
|
||||
|
||||
{/* Search */}
|
||||
{/* Search and Filter */}
|
||||
<div className='flex flex-1 flex-row justify-start sm:justify-end items-center gap-3 flex-wrap'>
|
||||
<DebouncedTextInput
|
||||
name='search'
|
||||
@@ -216,6 +288,12 @@ const InventoryProductTable = () => {
|
||||
'placeholder:font-semibold placeholder:text-base-content/50',
|
||||
}}
|
||||
/>
|
||||
<ButtonFilter
|
||||
values={tableFilterState}
|
||||
excludeFields={['page', 'pageSize', 'search', 'categoryName']}
|
||||
onClick={handleFilterModalOpen}
|
||||
className='px-3 py-2.5'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -272,6 +350,62 @@ const InventoryProductTable = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filter Modal */}
|
||||
<Modal
|
||||
ref={filterModal.ref}
|
||||
className={{
|
||||
modal: 'p-0',
|
||||
modalBox: 'p-0 rounded-[0.875rem] xl:max-w-4/12 max-w-sm',
|
||||
}}
|
||||
>
|
||||
<div className='flex items-center justify-between gap-2 border-b border-base-content/10 p-4'>
|
||||
<div className='flex items-center gap-2 text-primary'>
|
||||
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||||
<h3 className='font-medium text-sm'>Filter Data</h3>
|
||||
</div>
|
||||
<Button
|
||||
variant='link'
|
||||
onClick={filterModal.closeModal}
|
||||
className='text-base-content/50 hover:text-base-content transition-colors cursor-pointer'
|
||||
>
|
||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||
</Button>
|
||||
</div>
|
||||
<form onSubmit={formik.handleSubmit} onReset={formik.handleReset}>
|
||||
<div className='p-4 flex flex-col gap-1.5'>
|
||||
<SelectInput
|
||||
label='Kategori Produk'
|
||||
placeholder='Pilih Kategori'
|
||||
options={categoryOptions}
|
||||
value={categoryIdValue}
|
||||
onChange={handleFilterCategoryChange}
|
||||
onInputChange={setCategoryInputValue}
|
||||
isLoading={isLoadingCategoryOptions}
|
||||
isClearable
|
||||
onMenuScrollToBottom={loadMoreCategories}
|
||||
className={{ wrapper: 'w-full' }}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
|
||||
<Button
|
||||
type='reset'
|
||||
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'
|
||||
>
|
||||
Reset Filter
|
||||
</Button>
|
||||
<Button
|
||||
type='submit'
|
||||
className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold'
|
||||
disabled={!formik.isValid || formik.isSubmitting}
|
||||
>
|
||||
Apply Filter
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -172,6 +172,9 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
kandang_id: '',
|
||||
category: '',
|
||||
period: '',
|
||||
area_name: '',
|
||||
location_name: '',
|
||||
kandang_name: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
@@ -183,7 +186,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
category: 'category',
|
||||
period: 'period',
|
||||
},
|
||||
|
||||
excludeKeysFromUrl: ['area_name', 'location_name', 'kandang_name'],
|
||||
persist: true,
|
||||
storeName: 'project-flock-table',
|
||||
});
|
||||
@@ -259,6 +262,9 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
updateFilter('kandang_id', values.kandang_id || '');
|
||||
updateFilter('category', values.category || '');
|
||||
updateFilter('period', values.period || '');
|
||||
updateFilter('area_name', areaValue?.label ? String(areaValue.label) : '');
|
||||
updateFilter('location_name', locationValue?.label ? String(locationValue.label) : '');
|
||||
updateFilter('kandang_name', kandangValue?.label ? String(kandangValue.label) : '');
|
||||
filterModal.closeModal();
|
||||
setSubmitting(false);
|
||||
},
|
||||
@@ -268,6 +274,9 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
updateFilter('kandang_id', '');
|
||||
updateFilter('category', '');
|
||||
updateFilter('period', '');
|
||||
updateFilter('area_name', '');
|
||||
updateFilter('location_name', '');
|
||||
updateFilter('kandang_name', '');
|
||||
setFilterAreaId(undefined);
|
||||
setFilterLocationId(undefined);
|
||||
filterModal.closeModal();
|
||||
@@ -320,29 +329,37 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
// ===== FILTER HELPERS =====
|
||||
const areaValue = 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 found = areaOptions.find((opt) => String(opt.value) === formik.values.area_id);
|
||||
if (found) return found;
|
||||
if (tableFilterState.area_name) {
|
||||
return { value: formik.values.area_id, label: tableFilterState.area_name };
|
||||
}
|
||||
return null;
|
||||
}, [formik.values.area_id, areaOptions, tableFilterState.area_name]);
|
||||
|
||||
const locationValue = useMemo(() => {
|
||||
if (!formik.values.location_id) return null;
|
||||
return (
|
||||
locationOptions.find(
|
||||
(opt) => String(opt.value) === formik.values.location_id
|
||||
) || null
|
||||
const found = locationOptions.find(
|
||||
(opt) => String(opt.value) === formik.values.location_id
|
||||
);
|
||||
}, [formik.values.location_id, locationOptions]);
|
||||
if (found) return found;
|
||||
if (tableFilterState.location_name) {
|
||||
return { value: formik.values.location_id, label: tableFilterState.location_name };
|
||||
}
|
||||
return null;
|
||||
}, [formik.values.location_id, locationOptions, tableFilterState.location_name]);
|
||||
|
||||
const kandangValue = useMemo(() => {
|
||||
if (!formik.values.kandang_id) return null;
|
||||
return (
|
||||
kandangOptions.find(
|
||||
(opt) => String(opt.value) === formik.values.kandang_id
|
||||
) || null
|
||||
const found = kandangOptions.find(
|
||||
(opt) => String(opt.value) === formik.values.kandang_id
|
||||
);
|
||||
}, [formik.values.kandang_id, kandangOptions]);
|
||||
if (found) return found;
|
||||
if (tableFilterState.kandang_name) {
|
||||
return { value: formik.values.kandang_id, label: tableFilterState.kandang_name };
|
||||
}
|
||||
return null;
|
||||
}, [formik.values.kandang_id, kandangOptions, tableFilterState.kandang_name]);
|
||||
|
||||
const categoryValue = useMemo(() => {
|
||||
if (!formik.values.category) return null;
|
||||
@@ -967,7 +984,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
|
||||
<ButtonFilter
|
||||
values={tableFilterState}
|
||||
excludeFields={['page', 'pageSize', 'search']}
|
||||
excludeFields={['page', 'pageSize', 'search', 'area_name', 'location_name', 'kandang_name']}
|
||||
onClick={handleFilterModalOpen}
|
||||
className='px-3 py-2.5'
|
||||
/>
|
||||
|
||||
+10
-21
@@ -13,7 +13,6 @@ import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
|
||||
import { OptionType, useSelect } from '@/components/input/SelectInput';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { Flock } from '@/types/api/master-data/flock';
|
||||
import { TransferToLayingFilter } from '@/types/api/production/transfer-to-laying';
|
||||
import {
|
||||
TransferToLayingFilterSchema,
|
||||
TransferToLayingFilterValues,
|
||||
@@ -21,12 +20,14 @@ import {
|
||||
|
||||
interface TransferToLayingFilterModal {
|
||||
ref: RefObject<HTMLDialogElement | null>;
|
||||
onSubmit?: (values: TransferToLayingFilter) => void;
|
||||
initialValues?: Partial<TransferToLayingFilterValues>;
|
||||
onSubmit?: (values: TransferToLayingFilterValues) => void;
|
||||
onReset?: () => void;
|
||||
}
|
||||
|
||||
const TransferToLayingFilterModal = ({
|
||||
ref,
|
||||
initialValues: initialValuesProp,
|
||||
onSubmit,
|
||||
onReset,
|
||||
}: TransferToLayingFilterModal) => {
|
||||
@@ -86,28 +87,16 @@ const TransferToLayingFilterModal = ({
|
||||
|
||||
const formik = useFormik<TransferToLayingFilterValues>({
|
||||
initialValues: {
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
flockSource: [],
|
||||
flockDestination: [],
|
||||
status: [],
|
||||
startDate: initialValuesProp?.startDate ?? '',
|
||||
endDate: initialValuesProp?.endDate ?? '',
|
||||
flockSource: initialValuesProp?.flockSource ?? [],
|
||||
flockDestination: initialValuesProp?.flockDestination ?? [],
|
||||
status: initialValuesProp?.status ?? [],
|
||||
},
|
||||
enableReinitialize: true,
|
||||
validationSchema: TransferToLayingFilterSchema,
|
||||
onSubmit: async (values) => {
|
||||
const formattedValues = {
|
||||
...values,
|
||||
flockSource: values.flockSource
|
||||
? (values.flockSource as OptionType[]).map((item) => item.value)
|
||||
: [],
|
||||
flockDestination: values.flockDestination
|
||||
? (values.flockDestination as OptionType[]).map((item) => item.value)
|
||||
: [],
|
||||
status: values.status
|
||||
? (values.status as OptionType[]).map((item) => item.value)
|
||||
: [],
|
||||
};
|
||||
|
||||
onSubmit?.(formattedValues as TransferToLayingFilter);
|
||||
onSubmit?.(values);
|
||||
closeModalHandler();
|
||||
},
|
||||
onReset: () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { ChangeEventHandler, useEffect, useState } from 'react';
|
||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useUiStore } from '@/stores/ui/ui.store';
|
||||
import useSWR from 'swr';
|
||||
@@ -26,10 +26,9 @@ import TransferToLayingFilterModal from '@/components/pages/production/transfer-
|
||||
import TransferToLayingConfirmationModal from '@/components/pages/production/transfer-to-laying/TransferToLayingConfirmationModal';
|
||||
import TransferToLayingTableSkeleton from '@/components/pages/production/transfer-to-laying/skeleton/TransferToLayingTableSkeleton';
|
||||
|
||||
import {
|
||||
TransferToLaying,
|
||||
TransferToLayingFilter,
|
||||
} from '@/types/api/production/transfer-to-laying';
|
||||
import { TransferToLaying } from '@/types/api/production/transfer-to-laying';
|
||||
import { TransferToLayingFilterValues } from '@/components/pages/production/transfer-to-laying/filter/TransferToLayingFilter';
|
||||
import { OptionType } from '@/components/input/SelectInput';
|
||||
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
|
||||
import { cn, formatDate, formatNumber } from '@/lib/helper';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
@@ -142,6 +141,8 @@ const TransferToLayingsTable = () => {
|
||||
status: '',
|
||||
filter_by: '',
|
||||
sort_by: '',
|
||||
flockSourceNames: '',
|
||||
flockDestinationNames: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
@@ -154,6 +155,9 @@ const TransferToLayingsTable = () => {
|
||||
filter_by: 'filter_by',
|
||||
sort_by: 'sort_by',
|
||||
},
|
||||
excludeKeysFromUrl: ['flockSourceNames', 'flockDestinationNames'],
|
||||
persist: true,
|
||||
storeName: 'transfer-to-laying-table',
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -431,12 +435,72 @@ const TransferToLayingsTable = () => {
|
||||
updateFilter('search', e.target.value);
|
||||
};
|
||||
|
||||
const filterSubmitHandler = (values: TransferToLayingFilter) => {
|
||||
updateFilter('startDate', values.startDate);
|
||||
updateFilter('endDate', values.endDate);
|
||||
updateFilter('flockSource', values.flockSource.join(','));
|
||||
updateFilter('flockDestination', values.flockDestination.join(','));
|
||||
updateFilter('status', values.status.join(','));
|
||||
const STATUS_FILTER_OPTIONS = [
|
||||
{ value: 'PENDING', label: 'Pengajuan' },
|
||||
{ value: 'APPROVED', label: 'Disetujui' },
|
||||
{ value: 'REJECTED', label: 'Ditolak' },
|
||||
];
|
||||
|
||||
const filterModalInitialValues = useMemo(() => {
|
||||
const flockSourceIds = tableFilterState.flockSource
|
||||
? tableFilterState.flockSource.split(',')
|
||||
: [];
|
||||
const flockSourceNameList = tableFilterState.flockSourceNames
|
||||
? tableFilterState.flockSourceNames.split(',')
|
||||
: [];
|
||||
const flockSourceOptions = flockSourceIds.filter(Boolean).map((id, i) => ({
|
||||
value: parseInt(id),
|
||||
label: flockSourceNameList[i] || id,
|
||||
}));
|
||||
|
||||
const flockDestIds = tableFilterState.flockDestination
|
||||
? tableFilterState.flockDestination.split(',')
|
||||
: [];
|
||||
const flockDestNameList = tableFilterState.flockDestinationNames
|
||||
? tableFilterState.flockDestinationNames.split(',')
|
||||
: [];
|
||||
const flockDestOptions = flockDestIds.filter(Boolean).map((id, i) => ({
|
||||
value: parseInt(id),
|
||||
label: flockDestNameList[i] || id,
|
||||
}));
|
||||
|
||||
const statusIds = tableFilterState.status
|
||||
? tableFilterState.status.split(',')
|
||||
: [];
|
||||
const statusOptions = statusIds.filter(Boolean).map((id) => {
|
||||
const found = STATUS_FILTER_OPTIONS.find((opt) => opt.value === id);
|
||||
return found || { value: id, label: id };
|
||||
});
|
||||
|
||||
return {
|
||||
startDate: tableFilterState.startDate || '',
|
||||
endDate: tableFilterState.endDate || '',
|
||||
flockSource: flockSourceOptions,
|
||||
flockDestination: flockDestOptions,
|
||||
status: statusOptions,
|
||||
};
|
||||
}, [
|
||||
tableFilterState.startDate,
|
||||
tableFilterState.endDate,
|
||||
tableFilterState.flockSource,
|
||||
tableFilterState.flockDestination,
|
||||
tableFilterState.status,
|
||||
tableFilterState.flockSourceNames,
|
||||
tableFilterState.flockDestinationNames,
|
||||
]);
|
||||
|
||||
const filterSubmitHandler = (values: TransferToLayingFilterValues) => {
|
||||
const flockSourceOpts = (values.flockSource as OptionType[]) || [];
|
||||
const flockDestOpts = (values.flockDestination as OptionType[]) || [];
|
||||
const statusOpts = (values.status as OptionType[]) || [];
|
||||
|
||||
updateFilter('startDate', values.startDate || '');
|
||||
updateFilter('endDate', values.endDate || '');
|
||||
updateFilter('flockSource', flockSourceOpts.map((o) => String(o.value)).join(','));
|
||||
updateFilter('flockDestination', flockDestOpts.map((o) => String(o.value)).join(','));
|
||||
updateFilter('status', statusOpts.map((o) => String(o.value)).join(','));
|
||||
updateFilter('flockSourceNames', flockSourceOpts.map((o) => String(o.label)).join(','));
|
||||
updateFilter('flockDestinationNames', flockDestOpts.map((o) => String(o.label)).join(','));
|
||||
};
|
||||
|
||||
const filterResetHandler = () => {
|
||||
@@ -445,6 +509,8 @@ const TransferToLayingsTable = () => {
|
||||
updateFilter('flockSource', '');
|
||||
updateFilter('flockDestination', '');
|
||||
updateFilter('status', '');
|
||||
updateFilter('flockSourceNames', '');
|
||||
updateFilter('flockDestinationNames', '');
|
||||
};
|
||||
|
||||
const exportToExcelHandler = async () => {
|
||||
@@ -558,6 +624,8 @@ const TransferToLayingsTable = () => {
|
||||
'search',
|
||||
'filter_by',
|
||||
'sort_by',
|
||||
'flockSourceNames',
|
||||
'flockDestinationNames',
|
||||
]}
|
||||
fieldGroups={[['startDate', 'endDate']]}
|
||||
onClick={filterModal.openModal}
|
||||
@@ -670,6 +738,7 @@ const TransferToLayingsTable = () => {
|
||||
|
||||
<TransferToLayingFilterModal
|
||||
ref={filterModal.ref}
|
||||
initialValues={filterModalInitialValues}
|
||||
onSubmit={filterSubmitHandler}
|
||||
onReset={filterResetHandler}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { RefObject, useMemo, useState } from 'react';
|
||||
import { RefObject, useEffect, useMemo, useState } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import * as yup from 'yup';
|
||||
|
||||
@@ -60,6 +60,11 @@ const ReportDepreciationFilterModal = ({
|
||||
string | undefined
|
||||
>(initialValues?.location_id || undefined);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedAreaId(initialValues?.area_id || undefined);
|
||||
setSelectedLocationId(initialValues?.location_id || undefined);
|
||||
}, [initialValues?.area_id, initialValues?.location_id]);
|
||||
|
||||
const closeModalHandler = () => {
|
||||
ref.current?.close();
|
||||
};
|
||||
@@ -97,6 +102,7 @@ const ReportDepreciationFilterModal = ({
|
||||
|
||||
const formik = useFormik<ReportDepreciationFilterValues>({
|
||||
initialValues: initialValues || defaultInitialValues,
|
||||
enableReinitialize: true,
|
||||
validationSchema: ReportDepreciationFilterSchema,
|
||||
onSubmit: async (values) => {
|
||||
onSubmit?.(values);
|
||||
|
||||
@@ -126,8 +126,35 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => {
|
||||
});
|
||||
|
||||
handleFilterModalOpenRef.current = () => {
|
||||
const restoredLocation = filterParams.location_id
|
||||
? locationOptions.find((opt) => String(opt.value) === filterParams.location_id) ||
|
||||
{ value: filterParams.location_id, label: filterParams.location_id }
|
||||
: null;
|
||||
const restoredSupplier = filterParams.supplier_id
|
||||
? supplierOptions.find((opt) => String(opt.value) === filterParams.supplier_id) ||
|
||||
{ value: filterParams.supplier_id, label: filterParams.supplier_id }
|
||||
: null;
|
||||
const restoredKandang = filterParams.kandang_id
|
||||
? projectFlockKandangOptions.find((opt) => String(opt.value) === filterParams.kandang_id) ||
|
||||
{ value: filterParams.kandang_id, label: filterParams.kandang_id }
|
||||
: null;
|
||||
const restoredNonstock = filterParams.nonstock_id
|
||||
? nonstockOptions.find((opt) => String(opt.value) === filterParams.nonstock_id) ||
|
||||
{ value: filterParams.nonstock_id, label: filterParams.nonstock_id }
|
||||
: null;
|
||||
const restoredCategory = filterParams.category
|
||||
? categoryOptions.find((opt) => opt.value === filterParams.category) || null
|
||||
: null;
|
||||
|
||||
formik.setValues({
|
||||
location_id: restoredLocation,
|
||||
supplier_id: restoredSupplier,
|
||||
kandang_id: restoredKandang,
|
||||
nonstock_id: restoredNonstock,
|
||||
realization_date: filterParams.realization_date || null,
|
||||
category: restoredCategory,
|
||||
});
|
||||
filterModal.openModal();
|
||||
formik.validateForm();
|
||||
};
|
||||
|
||||
// ===== OPTIONS =====
|
||||
|
||||
@@ -38,6 +38,7 @@ import CustomerSupplierSkeleton from '@/components/pages/report/finance/skeleton
|
||||
import { OptionType } from '@/components/table/TableRowSizeSelector';
|
||||
import { Color } from '@/types/theme';
|
||||
import ButtonFilter from '@/components/helper/ButtonFilter';
|
||||
import Pagination from '@/components/Pagination';
|
||||
|
||||
interface CustomerPaymentTabProps {
|
||||
tabId: string;
|
||||
@@ -58,7 +59,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
|
||||
|
||||
// ===== PAGINATION STATE =====
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize] = useState(10);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
|
||||
// ===== SUBMISSION STATE =====
|
||||
const [filterParams, setFilterParams] = useState<FilterParams>({});
|
||||
@@ -117,8 +118,13 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
|
||||
});
|
||||
|
||||
handleFilterModalOpenRef.current = () => {
|
||||
formik.setValues({
|
||||
start_date: filterParams.start_date || null,
|
||||
end_date: filterParams.end_date || null,
|
||||
customer_ids: filterParams.customer_ids || null,
|
||||
filter_by: filterParams.filter_by || null,
|
||||
});
|
||||
filterModal.openModal();
|
||||
formik.validateForm();
|
||||
};
|
||||
|
||||
const getPaymentStatusBadgeColor = (notes: string): Color => {
|
||||
@@ -249,6 +255,14 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
|
||||
[customerPayment]
|
||||
);
|
||||
|
||||
const meta = useMemo(
|
||||
() =>
|
||||
isResponseSuccess(customerPayment) && customerPayment.meta
|
||||
? customerPayment.meta
|
||||
: null,
|
||||
[customerPayment]
|
||||
);
|
||||
|
||||
// ===== EXPORT DATA FETCHER =====
|
||||
const customerPaymentExport = useCallback(async (): Promise<
|
||||
CustomerPaymentReport[] | null
|
||||
@@ -717,6 +731,29 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isLoading && data.length > 0 && meta && (
|
||||
<div className='max-w-sm ml-auto'>
|
||||
<Pagination
|
||||
totalItems={meta.total_results || 0}
|
||||
itemsPerPage={meta.limit || 0}
|
||||
currentPage={meta.page || 0}
|
||||
onPrevPage={() =>
|
||||
setCurrentPage((curr) => (curr > 1 ? curr - 1 : curr))
|
||||
}
|
||||
onNextPage={() =>
|
||||
setCurrentPage((curr) =>
|
||||
meta.total_pages && curr < meta.total_pages
|
||||
? curr + 1
|
||||
: curr
|
||||
)
|
||||
}
|
||||
onPageChange={(pageNumber) => setCurrentPage(pageNumber)}
|
||||
rowOptions={[10, 20, 50, 100]}
|
||||
onRowChange={setPageSize}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoading &&
|
||||
data.length > 0 &&
|
||||
data.map((customerReport) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Button from '@/components/Button';
|
||||
import Card from '@/components/Card';
|
||||
import Dropdown from '@/components/Dropdown';
|
||||
import Pagination from '@/components/Pagination';
|
||||
import DateInput from '@/components/input/DateInput';
|
||||
import { OptionType, useSelect } from '@/components/input/SelectInput';
|
||||
import Modal, { useModal } from '@/components/Modal';
|
||||
@@ -78,6 +79,10 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => {
|
||||
const [isExcelExportLoading, setIsExcelExportLoading] = useState(false);
|
||||
const isAnyExportLoading = isPdfExportLoading || isExcelExportLoading;
|
||||
|
||||
// ===== PAGINATION STATE =====
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
|
||||
// ===== SUBMISSION STATE =====
|
||||
const [filterParams, setFilterParams] = useState<DebtSupplierFilter>({
|
||||
start_date: undefined,
|
||||
@@ -128,7 +133,7 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => {
|
||||
filter_by: values.filterBy?.value?.toString() || undefined,
|
||||
});
|
||||
filterModal.closeModal();
|
||||
// setIsSubmitted(true);
|
||||
setCurrentPage(1);
|
||||
},
|
||||
onReset: () => {
|
||||
setFilterParams({
|
||||
@@ -137,14 +142,29 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => {
|
||||
supplier_ids: undefined,
|
||||
filter_by: undefined,
|
||||
});
|
||||
// setIsSubmitted(false);
|
||||
setCurrentPage(1);
|
||||
filterModal.closeModal();
|
||||
},
|
||||
});
|
||||
|
||||
handleFilterModalOpenRef.current = () => {
|
||||
const restoredFilterBy =
|
||||
dataTypeOptions.find((opt) => opt.value === filterParams.filter_by) || null;
|
||||
|
||||
const supplierIdList = filterParams.supplier_ids
|
||||
? filterParams.supplier_ids.split(',')
|
||||
: [];
|
||||
const restoredSupplierIds = supplierOptions.filter((opt) =>
|
||||
supplierIdList.includes(String(opt.value))
|
||||
);
|
||||
|
||||
formik.setValues({
|
||||
startDate: filterParams.start_date || null,
|
||||
endDate: filterParams.end_date || null,
|
||||
supplierIds: restoredSupplierIds.length > 0 ? restoredSupplierIds : null,
|
||||
filterBy: restoredFilterBy,
|
||||
});
|
||||
filterModal.openModal();
|
||||
formik.validateForm();
|
||||
};
|
||||
|
||||
// ===== DATA FETCHING =====
|
||||
@@ -155,6 +175,8 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => {
|
||||
filter_by: filterParams.filter_by,
|
||||
start_date: filterParams.start_date,
|
||||
end_date: filterParams.end_date,
|
||||
page: currentPage,
|
||||
limit: pageSize,
|
||||
};
|
||||
|
||||
return ['debt-supplier-report', params];
|
||||
@@ -164,7 +186,9 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => {
|
||||
params.supplier_ids,
|
||||
params.filter_by,
|
||||
params.start_date,
|
||||
params.end_date
|
||||
params.end_date,
|
||||
params.page,
|
||||
params.limit
|
||||
)
|
||||
);
|
||||
|
||||
@@ -176,6 +200,14 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => {
|
||||
[debtSupplier]
|
||||
);
|
||||
|
||||
const meta = useMemo(
|
||||
() =>
|
||||
isResponseSuccess(debtSupplier) && debtSupplier.meta
|
||||
? debtSupplier.meta
|
||||
: null,
|
||||
[debtSupplier]
|
||||
);
|
||||
|
||||
// ===== EXPORT DATA FETCHER =====
|
||||
const debtSupplierExport = useCallback(async (): Promise<
|
||||
DebtSupplier[] | null
|
||||
@@ -630,6 +662,29 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isLoading && data.length > 0 && meta && (
|
||||
<div className='max-w-sm ml-auto'>
|
||||
<Pagination
|
||||
totalItems={meta.total_results || 0}
|
||||
itemsPerPage={meta.limit || 0}
|
||||
currentPage={meta.page || 0}
|
||||
onPrevPage={() =>
|
||||
setCurrentPage((curr) => (curr > 1 ? curr - 1 : curr))
|
||||
}
|
||||
onNextPage={() =>
|
||||
setCurrentPage((curr) =>
|
||||
meta.total_pages && curr < meta.total_pages
|
||||
? curr + 1
|
||||
: curr
|
||||
)
|
||||
}
|
||||
onPageChange={(pageNumber) => setCurrentPage(pageNumber)}
|
||||
rowOptions={[10, 20, 50, 100]}
|
||||
onRowChange={setPageSize}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoading &&
|
||||
data.length > 0 &&
|
||||
data.map((supplierReport) => {
|
||||
|
||||
@@ -156,8 +156,17 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => {
|
||||
});
|
||||
|
||||
handleFilterModalOpenRef.current = () => {
|
||||
formik.setValues({
|
||||
start_date: filterParams.start_date || null,
|
||||
end_date: filterParams.end_date || null,
|
||||
area_ids: filterParams.area_id || null,
|
||||
supplier_ids: filterParams.supplier_id || null,
|
||||
product_ids: filterParams.product_id || null,
|
||||
product_category_ids: filterParams.product_category_id || null,
|
||||
filter_by: filterParams.filter_by || null,
|
||||
sort_by: filterParams.sort_by || null,
|
||||
});
|
||||
filterModal.openModal();
|
||||
formik.validateForm();
|
||||
};
|
||||
|
||||
const { setFieldValue } = formik;
|
||||
|
||||
@@ -156,8 +156,21 @@ const DailyMarketingTab = ({ tabId }: DailyMarketingTabProps) => {
|
||||
});
|
||||
|
||||
handleFilterModalOpenRef.current = () => {
|
||||
formik.setValues({
|
||||
page: formik.values.page,
|
||||
pageSize: formik.values.pageSize,
|
||||
search: formik.values.search,
|
||||
area_id: filterParams.area_id || null,
|
||||
location_id: filterParams.location_id || null,
|
||||
warehouse_id: filterParams.warehouse_id || null,
|
||||
customer_id: filterParams.customer_id || null,
|
||||
start_date: filterParams.start_date || null,
|
||||
end_date: filterParams.end_date || null,
|
||||
filter_by: filterParams.filter_by || null,
|
||||
marketing_type: filterParams.marketing_type || null,
|
||||
sort_by: filterParams.sort_by || null,
|
||||
});
|
||||
filterModal.openModal();
|
||||
formik.validateForm();
|
||||
};
|
||||
|
||||
// ===== SEARCH CHANGE HANDLER =====
|
||||
|
||||
@@ -152,8 +152,19 @@ const HppPerKandangTab = ({ tabId }: HppPerKandangTabProps) => {
|
||||
});
|
||||
|
||||
handleFilterModalOpenRef.current = () => {
|
||||
formik.setValues({
|
||||
page: formik.values.page,
|
||||
pageSize: formik.values.pageSize,
|
||||
area_id: filterParams.area_id || null,
|
||||
location_id: filterParams.location_id || null,
|
||||
kandang_id: filterParams.kandang_id || null,
|
||||
weight_min: filterParams.weight_min || null,
|
||||
weight_max: filterParams.weight_max || null,
|
||||
period: filterParams.period || null,
|
||||
sort_by: filterParams.sort_by || null,
|
||||
show_unrecorded: filterParams.show_unrecorded ?? false,
|
||||
});
|
||||
filterModal.openModal();
|
||||
formik.validateForm();
|
||||
};
|
||||
|
||||
// ===== WEIGHT CHANGE HANDLERS =====
|
||||
|
||||
+23
-1
@@ -263,8 +263,30 @@ const ProductionResultContent = ({ tabId }: ProductionResultTabProps) => {
|
||||
});
|
||||
|
||||
handleFilterModalOpenRef.current = () => {
|
||||
const restoredAreaId = filterParams.area_id
|
||||
? areaOptions.find((opt) => String(opt.value) === filterParams.area_id) ||
|
||||
{ value: filterParams.area_id, label: filterParams.area_id }
|
||||
: null;
|
||||
const restoredLocationId = filterParams.location_id
|
||||
? locationOptions.find((opt) => String(opt.value) === filterParams.location_id) ||
|
||||
{ value: filterParams.location_id, label: filterParams.location_id }
|
||||
: null;
|
||||
const restoredProjectFlockId = filterParams.project_flock_id
|
||||
? projectFlockOptions.find((opt) => String(opt.value) === filterParams.project_flock_id) ||
|
||||
{ value: filterParams.project_flock_id, label: filterParams.project_flock_id }
|
||||
: null;
|
||||
const restoredKandangId = filterParams.project_flock_kandang_id
|
||||
? projectFlockKandangOptions.find((opt) => String(opt.value) === filterParams.project_flock_kandang_id) ||
|
||||
{ value: filterParams.project_flock_kandang_id, label: filterParams.project_flock_kandang_id }
|
||||
: null;
|
||||
|
||||
formik.setValues({
|
||||
area_id: restoredAreaId,
|
||||
location_id: restoredLocationId,
|
||||
project_flock_id: restoredProjectFlockId,
|
||||
kandang_id: restoredKandangId,
|
||||
});
|
||||
filterModal.openModal();
|
||||
formik.validateForm();
|
||||
};
|
||||
|
||||
const [selectedProjectFlockKandang, setSelectedProjectFlockKandang] =
|
||||
|
||||
@@ -15,7 +15,9 @@ export class DebtSupplierApiService extends BaseApiService<
|
||||
supplier_ids?: string,
|
||||
filter_by?: string,
|
||||
start_date?: string,
|
||||
end_date?: string
|
||||
end_date?: string,
|
||||
page?: number,
|
||||
limit?: number
|
||||
): Promise<BaseApiResponse<DebtSupplier[]> | undefined> {
|
||||
return await this.customRequest<BaseApiResponse<DebtSupplier[]>>(
|
||||
`debt-supplier`,
|
||||
@@ -26,6 +28,8 @@ export class DebtSupplierApiService extends BaseApiService<
|
||||
filter_by: filter_by,
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
page: page,
|
||||
limit: limit,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user