diff --git a/src/components/Drawer.tsx b/src/components/Drawer.tsx index fc0af9fb..280b4b34 100644 --- a/src/components/Drawer.tsx +++ b/src/components/Drawer.tsx @@ -108,7 +108,9 @@ const Drawer = ({ if (closeOnBackdropClick) { setOpen(false); } - onBackdropClick && onBackdropClick(); + if (onBackdropClick) { + onBackdropClick(); + } }; return ( diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index caa07870..7f65534d 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -31,7 +31,11 @@ export const useModal = (isNestingModal = false) => { }, []); const toggle = useCallback(() => { - open ? closeModal() : openModal(); + if (open) { + closeModal(); + } else { + openModal(); + } }, [open, closeModal, openModal]); useEffect(() => { diff --git a/src/components/pages/closing/table/SalesClosingTable.tsx b/src/components/pages/closing/table/SalesClosingTable.tsx index 5105d965..e362f1e0 100644 --- a/src/components/pages/closing/table/SalesClosingTable.tsx +++ b/src/components/pages/closing/table/SalesClosingTable.tsx @@ -308,7 +308,16 @@ const SalesClosingTable = ({ projectFlockId }: SalesClosingTableProps) => { // }, // }, ], - [] + [ + summary, + totals.avgActualPrice, + totals.avgSalesPrice, + totals.avgWeight, + totals.totalActualPrice, + totals.totalQuantity, + totals.totalSalesPrice, + totals.totalWeight, + ] ); return ( diff --git a/src/components/pages/dashboard/DashboardProduction.tsx b/src/components/pages/dashboard/DashboardProduction.tsx index 674f3719..27db0039 100644 --- a/src/components/pages/dashboard/DashboardProduction.tsx +++ b/src/components/pages/dashboard/DashboardProduction.tsx @@ -150,33 +150,39 @@ const DashboardProduction = () => { }, }); + const { resetForm } = formik; + const handleResetFilter = useCallback(() => { - formik.resetForm(); + resetForm(); resetFilterValues(); // Clear stored filter values setAnalysisMode('OVERVIEW'); setEndpointUrl('/dashboards'); setSelectedLocationIds([]); - }, [resetFilterValues, filterValues, selectedLocationIds]); + }, [resetForm, resetFilterValues]); - const handleApplyFilter = (values: DashboardFilter) => { - // Build query params object, only include non-empty values - const params: Record = {}; + const handleApplyFilter = useCallback( + (values: DashboardFilter) => { + // Build query params object, only include non-empty values + const params: Record = {}; - if (values.start_date) params.start_date = values.start_date; - if (values.end_date) params.end_date = values.end_date; - if (values.analysis_mode) params.analysis_mode = values.analysis_mode; - if (values.location_ids.length > 0) - params.location_ids = values.location_ids.toString(); - if (values.flock_ids.length > 0) - params.flock_ids = values.flock_ids.toString(); - if (values.kandang_ids.length > 0) - params.kandang_ids = values.kandang_ids.toString(); - if (values.comparison_type) params.comparison_type = values.comparison_type; + if (values.start_date) params.start_date = values.start_date; + if (values.end_date) params.end_date = values.end_date; + if (values.analysis_mode) params.analysis_mode = values.analysis_mode; + if (values.location_ids.length > 0) + params.location_ids = values.location_ids.toString(); + if (values.flock_ids.length > 0) + params.flock_ids = values.flock_ids.toString(); + if (values.kandang_ids.length > 0) + params.kandang_ids = values.kandang_ids.toString(); + if (values.comparison_type) + params.comparison_type = values.comparison_type; - setEndpointUrl(`/dashboards?${new URLSearchParams(params).toString()}`); - filterModal.closeModal(); - refreshDashboardProductionData(); - }; + setEndpointUrl(`/dashboards?${new URLSearchParams(params).toString()}`); + filterModal.closeModal(); + refreshDashboardProductionData(); + }, + [filterModal, refreshDashboardProductionData] + ); // ===== Load filter from store on mount ===== useEffect(() => { @@ -190,20 +196,20 @@ const DashboardProduction = () => { kandang_ids: normalizeToArray(filterValues.kandang), comparison_type: filterValues.comparisonType, }); - }, [filterValues]); + }, [filterValues, handleApplyFilter]); // ===== Formik Error List ===== const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik); // ===== Export PDF ===== - const handleExportPDF = async () => { + const handleExportPDF = useCallback(async () => { await generateDashboardPDF({ filterValues: formik.values, allStatsRef, allChartsRef, setExporting, }); - }; + }, [formik.values]); // ===== Register Navbar Actions ===== const openFilterModalRef = useRef(filterModal.openModal); @@ -253,7 +259,7 @@ const DashboardProduction = () => { ); - }, [formik.values, exporting, setNavbarActions]); + }, [formik.values, exporting, setNavbarActions, handleExportPDF]); // Cleanup only on unmount useEffect(() => { diff --git a/src/components/pages/dashboard/chart/DashboardLineChart.tsx b/src/components/pages/dashboard/chart/DashboardLineChart.tsx index bfb13d9a..b7e0e1c2 100644 --- a/src/components/pages/dashboard/chart/DashboardLineChart.tsx +++ b/src/components/pages/dashboard/chart/DashboardLineChart.tsx @@ -409,14 +409,14 @@ const DashboardLineChart = ({ axisLine={{ stroke: '#C1C1C180', opacity: 0.5 }} domain={(() => { // Calculate dynamic domain based on visible data - let seriesData: DashboardChartsSeries[] = []; + // let seriesData: DashboardChartsSeries[] = []; let dataset: DashboardChartsDataset[] = []; if ( analysisMode === 'OVERVIEW' && isOverviewCharts(data.charts) ) { - seriesData = data.charts[chartData]?.series || []; + // seriesData = data.charts[chartData]?.series || []; dataset = data.charts[chartData]?.dataset || []; } else if ( analysisMode === 'COMPARISON' && @@ -426,7 +426,7 @@ const DashboardLineChart = ({ data.charts.farm || data.charts.flock || data.charts.kandang; - seriesData = comparisonChart?.series || []; + // seriesData = comparisonChart?.series || []; dataset = comparisonChart?.dataset || []; } diff --git a/src/components/pages/expense/form/ExpenseKandangsTable.tsx b/src/components/pages/expense/form/ExpenseKandangsTable.tsx index 5c60ae1e..bc779417 100644 --- a/src/components/pages/expense/form/ExpenseKandangsTable.tsx +++ b/src/components/pages/expense/form/ExpenseKandangsTable.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import useSWR from 'swr'; import { Icon } from '@iconify/react'; @@ -75,6 +75,12 @@ const ExpenseKandangsTable = ({ .filter((id): id is number => id !== undefined) ) ); + const rowSelectionRef = useRef(rowSelection); + const prevRowSelectionRef = useRef>({}); + + useEffect(() => { + rowSelectionRef.current = rowSelection; + }, [rowSelection]); const kandangsColumns: ColumnDef[] = [ { @@ -133,33 +139,43 @@ const ExpenseKandangsTable = ({ useEffect(() => { setOpen(isResponseSuccess(kandangs) ? kandangs.data.length > 0 : false); - }, [kandangs, isResponseSuccess]); + }, [kandangs]); useEffect(() => { - if (Object.keys(rowSelection).length !== 0 && isResponseSuccess(kandangs)) { - const formattedSelectedKandangs = Object.keys(rowSelection).map( - (item) => { - const selectedKandang = kandangs.data.find( - (kandang) => kandang.id === parseInt(item) - ); + const currentKeys = Object.keys(rowSelection).sort().join(','); + const prevKeys = Object.keys(prevRowSelectionRef.current).sort().join(','); - return { - id: parseInt(item), - name: selectedKandang?.name ?? 'Kandang tidak ditemukan!', - }; - } - ); + if (currentKeys !== prevKeys) { + prevRowSelectionRef.current = { ...rowSelection }; - onChange(formattedSelectedKandangs); - } else { - onChange([]); + if ( + Object.keys(rowSelection).length !== 0 && + isResponseSuccess(kandangs) + ) { + const formattedSelectedKandangs = Object.keys(rowSelection).map( + (item) => { + const selectedKandang = kandangs.data.find( + (kandang) => kandang.id === parseInt(item) + ); + + return { + id: parseInt(item), + name: selectedKandang?.name ?? 'Kandang tidak ditemukan!', + }; + } + ); + + onChange(formattedSelectedKandangs); + } else if (Object.keys(rowSelection).length === 0) { + onChange([]); + } } - }, [rowSelection]); + }, [rowSelection, kandangs, onChange]); useEffect(() => { if ( selectedKandangs.length === 0 && - Object.keys(rowSelection).length !== 0 + Object.keys(rowSelectionRef.current).length !== 0 ) { setRowSelection({}); } diff --git a/src/components/pages/expense/form/ExpenseRealizationForm.tsx b/src/components/pages/expense/form/ExpenseRealizationForm.tsx index acc0a393..e9720d0b 100644 --- a/src/components/pages/expense/form/ExpenseRealizationForm.tsx +++ b/src/components/pages/expense/form/ExpenseRealizationForm.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; import { useRouter } from 'next/navigation'; import { useFormik } from 'formik'; import toast from 'react-hot-toast'; @@ -90,6 +90,7 @@ const ExpenseRealizationForm = ({ const formik = useFormik({ initialValues: getExpenseRealizationFormInitialValues(initialValues), + enableReinitialize: true, validationSchema: type === 'edit' ? UpdateExpenseRealizationFormSchema @@ -143,7 +144,6 @@ const ExpenseRealizationForm = ({ }, }); - const { setValues: formikSetValues } = formik; const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik); const { @@ -254,10 +254,6 @@ const ExpenseRealizationForm = ({ formik.setFieldValue('documents', newRequestDocuments); }; - useEffect(() => { - formikSetValues(getExpenseRealizationFormInitialValues(initialValues)); - }, [formikSetValues, getExpenseRealizationFormInitialValues, initialValues]); - return (
diff --git a/src/components/pages/expense/form/ExpenseRequestForm.tsx b/src/components/pages/expense/form/ExpenseRequestForm.tsx index 733204d9..adc825c2 100644 --- a/src/components/pages/expense/form/ExpenseRequestForm.tsx +++ b/src/components/pages/expense/form/ExpenseRequestForm.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; import { useRouter } from 'next/navigation'; import { useFormik } from 'formik'; import { toast } from 'react-hot-toast'; @@ -102,6 +102,7 @@ const ExpenseRequestForm = ({ const formik = useFormik({ initialValues: getExpenseFormInitialValues(initialValues), + enableReinitialize: true, validationSchema: type === 'edit' ? UpdateExpenseRequestFormSchema @@ -171,7 +172,7 @@ const ExpenseRequestForm = ({ }, }); - const { setValues: formikSetValues } = formik; + const { setFieldValue, setFieldTouched } = formik; const { setInputValue: setLocationInputValue, @@ -186,8 +187,8 @@ const ExpenseRequestForm = ({ } = useSelect(SupplierApi.basePath, 'id', 'name'); const categoryChangeHandler = (val: OptionType | OptionType[] | null) => { - formik.setFieldTouched('category', true); - formik.setFieldValue('category', val); + setFieldTouched('category', true); + setFieldValue('category', val); }; const locationChangeHandler = useCallback( @@ -195,12 +196,12 @@ const ExpenseRequestForm = ({ const location = val as OptionType | null; const locationId = location ? Number(location.value) : 0; - formik.setFieldTouched('location', true); - formik.setFieldValue('location', location); - formik.setFieldTouched('location_id', true); - formik.setFieldValue('location_id', locationId); + setFieldTouched('location', true); + setFieldValue('location', location); + setFieldTouched('location_id', true); + setFieldValue('location_id', locationId); }, - [] + [setFieldTouched, setFieldValue] ); const kandangsChangeHandler = ( @@ -343,10 +344,6 @@ const ExpenseRequestForm = ({ formik.handleSubmit(e); }; - useEffect(() => { - formikSetValues(getExpenseFormInitialValues(initialValues)); - }, [formikSetValues, getExpenseFormInitialValues, initialValues]); - return ( <>
diff --git a/src/components/pages/finance/FinanceDetail.tsx b/src/components/pages/finance/FinanceDetail.tsx index 622fff6f..0887e029 100644 --- a/src/components/pages/finance/FinanceDetail.tsx +++ b/src/components/pages/finance/FinanceDetail.tsx @@ -2,7 +2,6 @@ import Button from '@/components/Button'; import Card from '@/components/Card'; import { FormHeader } from '@/components/helper/form/FormHeader'; import RequirePermission from '@/components/helper/RequirePermission'; -import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import Table from '@/components/Table'; @@ -26,11 +25,13 @@ const FinanceDetail = ({ finance }: { finance: Finance }) => { const informasiUmum = [ { label: 'ID', - value: finance.payment_code, + value: finance.payment_code || '-', }, { label: 'Jenis Transaksi', - value: formatTitleCase(finance.transaction_type.split('_').join(' ')), + value: formatTitleCase( + (finance.transaction_type || '').split('_').join(' ') + ), }, { label: 'Pihak', @@ -38,11 +39,13 @@ const FinanceDetail = ({ finance }: { finance: Finance }) => { }, { label: 'Tanggal', - value: formatDate(finance.payment_date, 'DD MMM yyyy'), + value: finance.payment_date + ? formatDate(finance.payment_date, 'DD MMM yyyy') + : '-', }, { label: 'Metode Pembayaran', - value: finance.payment_method, + value: finance.payment_method || '-', }, { label: 'Catatan', @@ -61,22 +64,22 @@ const FinanceDetail = ({ finance }: { finance: Finance }) => { : '-', }, { - label: `Rekening ${formatTitleCase(finance.party?.type)}`, - value: finance.party?.account_number, + label: `Rekening ${formatTitleCase(finance.party?.type || '')}`, + value: finance.party?.account_number || '-', }, { label: 'Nominal', value: formatCurrency( finance.transaction_type === 'INJECTION' - ? finance.nominal - : Math.abs(finance.nominal) + ? finance.nominal || 0 + : Math.abs(finance.nominal || 0) ), }, ].filter((item) => { // Hide party account number row if transaction type is INJECTION if ( - FINANCE_INJECTION_STATUS.includes(finance.transaction_type) && - item.label === `Rekening ${formatTitleCase(finance.party?.type)}` + FINANCE_INJECTION_STATUS.includes(finance.transaction_type || '') && + item.label === `Rekening ${formatTitleCase(finance.party?.type || '')}` ) { return false; } @@ -150,7 +153,7 @@ const FinanceDetail = ({ finance }: { finance: Finance }) => {
- {FINANCE_TRANSACTION_STATUS.includes(finance.transaction_type) && + {FINANCE_TRANSACTION_STATUS.includes(finance.transaction_type || '') && finance.party?.type !== 'SUPPLIER' && ( )} - {FINANCE_INITIAL_BALANCE_STATUS.includes(finance.transaction_type) && ( + {FINANCE_INITIAL_BALANCE_STATUS.includes( + finance.transaction_type || '' + ) && ( )} - {FINANCE_INJECTION_STATUS.includes(finance.transaction_type) && ( + {FINANCE_INJECTION_STATUS.includes(finance.transaction_type || '') && ( + } > -
- + + + +
+ ); + }, [setTabActions]); - Export + useEffect(() => { + return () => { + clearTabActions(tabId); + }; + }, [clearTabActions]); -
- - -
- - } - > - - - -
- ); + return null; + }; }, [ tabId, - formik.values, + filterParams, isAnyExportLoading, handleExportExcel, handleExportPDF, - setTabActions, + isExcelExportLoading, + isPdfExportLoading, ]); - useEffect(() => { - return () => { - clearTabActions(tabId); - }; - }, [tabId, clearTabActions]); + const TabActionsElement = useMemo(() => , [TabActions]); // ===== TABLE COLUMNS DEFINITION ===== const columns = useMemo((): ColumnDef[] => { @@ -505,6 +527,7 @@ const ReportExpenseTab = ({ tabId }: ReportExpenseTabProps) => { return ( <> + {TabActionsElement}
{!isSubmitted ? ( { const [dateErrorShown, setDateErrorShown] = useState(false); const [hasDateError, setHasDateError] = useState(false); + const handleFilterModalOpenRef = useRef(() => {}); + const filterModal = useModal(); const dataTypeOptions = useMemo( @@ -84,11 +85,6 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => { loadMore: loadMoreCustomers, } = useSelect(CustomerApi.basePath, 'id', 'name', 'search'); - const handleFilterModalOpen = () => { - filterModal.openModal(); - formik.validateForm(); - }; - // ===== FORMIK SETUP ===== const formik = useFormik({ initialValues: { @@ -123,6 +119,11 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => { }, }); + handleFilterModalOpenRef.current = () => { + filterModal.openModal(); + formik.validateForm(); + }; + const getPaymentStatusBadgeColor = (notes: string): Color => { const normalizedValue = notes.toLowerCase(); @@ -213,7 +214,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => { dataTypeOptions.find((opt) => opt.value === formik.values.filter_by) || null ); - }, [formik.values.filter_by]); + }, [formik.values.filter_by, dataTypeOptions]); // ===== DATA FETCHING ===== const { data: customerPayment, isLoading } = useSWR( @@ -350,90 +351,104 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => { } }, [customerPaymentExport, filterParams, customerOptions]); - // ===== REGISTER TAB ACTIONS TO STORE ===== - const setTabActions = useTabActionsStore((state) => state.setTabActions); - const clearTabActions = useTabActionsStore((state) => state.clearTabActions); + // ===== TAB ACTIONS COMPONENT ===== + const TabActions = useMemo(() => { + return function TabActionsComponent() { + const setTabActions = useTabActionsStore((state) => state.setTabActions); + const clearTabActions = useTabActionsStore( + (state) => state.clearTabActions + ); - useEffect(() => { - setTabActions( - tabId, -
- - - { + setTabActions( + tabId, +
+ handleFilterModalOpenRef.current()} variant='outline' - color='none' - isLoading={isAnyExportLoading} - className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft' + className='px-3 py-2.5' + /> + + +
+ + + Export + +
+ + +
+ + } > -
- + + + +
+ ); + }, [setTabActions]); - Export + useEffect(() => { + return () => { + clearTabActions(tabId); + }; + }, [clearTabActions]); -
- - -
- - } - > - - - -
- ); + return null; + }; }, [ tabId, - formik.values, isAnyExportLoading, handleExportExcel, handleExportPdf, - filterModal.open, - setTabActions, + isExcelExportLoading, + isPdfExportLoading, + filterParams, ]); - useEffect(() => { - return () => { - clearTabActions(tabId); - }; - }, [tabId, clearTabActions]); + const TabActionsElement = useMemo(() => , [TabActions]); const getTableColumns = ( summary: CustomerPaymentSummary @@ -683,6 +698,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => { return ( <> + {TabActionsElement}
{!isSubmitted ? ( { const [dateErrorShown, setDateErrorShown] = useState(false); const [hasDateError, setHasDateError] = useState(false); + const handleFilterModalOpenRef = useRef(() => {}); + const filterModal = useModal(); const { @@ -108,11 +110,6 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => { [] ); - const handleFilterModalOpen = () => { - filterModal.openModal(); - formik.validateForm(); - }; - // ===== FORMIK SETUP ===== const formik = useFormik({ initialValues: { @@ -146,6 +143,11 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => { }, }); + handleFilterModalOpenRef.current = () => { + filterModal.openModal(); + formik.validateForm(); + }; + // ===== DATA FETCHING ===== const { data: debtSupplier, isLoading } = useSWR( isSubmitted @@ -268,92 +270,112 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => { } finally { setIsPdfExportLoading(false); } - }, [debtSupplierExport]); + }, [ + debtSupplierExport, + formik.values.supplierIds, + formik.values.filterBy, + formik.values.startDate, + formik.values.endDate, + ]); - // ===== REGISTER TAB ACTIONS TO STORE ===== - const setTabActions = useTabActionsStore((state) => state.setTabActions); - const clearTabActions = useTabActionsStore((state) => state.clearTabActions); + // ===== TAB ACTIONS COMPONENT ===== + const TabActions = useMemo(() => { + return function TabActionsComponent() { + const setTabActions = useTabActionsStore((state) => state.setTabActions); + const clearTabActions = useTabActionsStore( + (state) => state.clearTabActions + ); - useEffect(() => { - setTabActions( - tabId, -
- - - { + setTabActions( + tabId, +
+ handleFilterModalOpenRef.current()} variant='outline' - color='none' - isLoading={isAnyExportLoading} - className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft' + className='px-3 py-2.5' + /> + + +
+ + + Export + +
+ + +
+ + } > -
- + + + +
+ ); + }, [setTabActions]); - Export + useEffect(() => { + return () => { + clearTabActions(tabId); + }; + }, [clearTabActions]); -
- - -
- - } - > - - - -
- ); + return null; + }; }, [ tabId, - formik.values, + filterParams, isAnyExportLoading, handleExportExcel, handleExportPdf, - setTabActions, + isExcelExportLoading, + isPdfExportLoading, ]); - // Cleanup on unmount - useEffect(() => { - return () => { - clearTabActions(tabId); - }; - }, [tabId, clearTabActions]); + const TabActionsElement = useMemo(() => , [TabActions]); useEffect(() => { return () => { @@ -587,6 +609,7 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => { ]; return ( <> + {TabActionsElement}
{!isSubmitted ? ( { const [dateErrorShown, setDateErrorShown] = useState(false); const [hasDateError, setHasDateError] = useState(false); + const handleFilterModalOpenRef = useRef(() => {}); + const filterModal = useModal(); // ===== OPTIONS ===== @@ -104,11 +106,6 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => { [] ); - const handleFilterModalOpen = () => { - filterModal.openModal(); - formik.validateForm(); - }; - // ===== FORMIK SETUP ===== const formik = useFormik({ initialValues: { @@ -151,11 +148,18 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => { }, }); + handleFilterModalOpenRef.current = () => { + filterModal.openModal(); + formik.validateForm(); + }; + + const { setFieldValue } = formik; + // ===== DATE CHANGE HANDLERS ===== const handleStartDateChange = useCallback( (e: React.ChangeEvent) => { const value = e.target.value; - formik.setFieldValue('start_date', value || null); + setFieldValue('start_date', value || null); if (value && formik.values.end_date) { const startDate = new Date(value); @@ -180,13 +184,13 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => { setHasDateError(false); } }, - [formik, dateErrorShown] + [setFieldValue, dateErrorShown, formik.values.end_date] ); const handleEndDateChange = useCallback( (e: React.ChangeEvent) => { const value = e.target.value; - formik.setFieldValue('end_date', value || null); + setFieldValue('end_date', value || null); if (value && formik.values.start_date) { const startDateObj = new Date(formik.values.start_date); @@ -210,7 +214,7 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => { setDateErrorShown(false); } }, - [formik, dateErrorShown] + [setFieldValue, dateErrorShown, formik.values.start_date] ); // ===== DERIVED VALUES ===== @@ -443,88 +447,104 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => { productCategoryOptions, ]); - // ===== REGISTER TAB ACTIONS TO STORE ===== - const setTabActions = useTabActionsStore((state) => state.setTabActions); - const clearTabActions = useTabActionsStore((state) => state.clearTabActions); + // ===== TAB ACTIONS COMPONENT ===== + const TabActions = useMemo(() => { + return function TabActionsComponent() { + const setTabActions = useTabActionsStore((state) => state.setTabActions); + const clearTabActions = useTabActionsStore( + (state) => state.clearTabActions + ); - useEffect(() => { - setTabActions( - tabId, -
- - - { + setTabActions( + tabId, +
+ handleFilterModalOpenRef.current()} variant='outline' - color='none' - isLoading={isAnyExportLoading} - className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft' + className='px-3 py-2.5' + /> + + +
+ + + Export + +
+ + +
+ + } > -
- + + + +
+ ); + }, [setTabActions]); - Export + useEffect(() => { + return () => { + clearTabActions(tabId); + }; + }, [clearTabActions]); -
- - -
- - } - > - - - -
- ); + return null; + }; }, [ tabId, - formik.values, + filterParams, isAnyExportLoading, - filterModal.open, - setTabActions, + handleExportExcel, + handleExportPdf, + isExcelExportLoading, + isPdfExportLoading, ]); - useEffect(() => { - return () => { - clearTabActions(tabId); - }; - }, [tabId, clearTabActions]); + const TabActionsElement = useMemo(() => , [TabActions]); const getTableColumns = ( summary: LogisticPurchasePerSupplierSummary @@ -704,6 +724,7 @@ const PurchasesPerSupplierTab = ({ tabId }: PurchasesPerSupplierTabProps) => { return ( <> + {TabActionsElement}
{!isSubmitted ? ( { const [dateErrorShown, setDateErrorShown] = useState(false); const [hasDateError, setHasDateError] = useState(false); + const handleFilterModalOpenRef = useRef(() => {}); + const filterModal = useModal(); // ===== OPTIONS ===== @@ -102,11 +104,6 @@ const DailyMarketingTab = ({ tabId }: DailyMarketingTabProps) => { const { options: customerOptions, isLoadingOptions: isLoadingCustomers } = useSelect(CustomerApi.basePath, 'id', 'name', 'search'); - const handleFilterModalOpen = () => { - filterModal.openModal(); - formik.validateForm(); - }; - // ===== FORMIK SETUP ===== const formik = useFormik({ initialValues: { @@ -145,6 +142,11 @@ const DailyMarketingTab = ({ tabId }: DailyMarketingTabProps) => { }, }); + handleFilterModalOpenRef.current = () => { + filterModal.openModal(); + formik.validateForm(); + }; + // ===== SEARCH CHANGE HANDLER ===== const searchChangeHandler = useCallback( (e: React.ChangeEvent) => { @@ -353,121 +355,126 @@ const DailyMarketingTab = ({ tabId }: DailyMarketingTabProps) => { } }, [dailyMarketingsExport, summaryTotal]); - // ===== REGISTER TAB ACTIONS TO STORE ===== - const setTabActions = useTabActionsStore((state) => state.setTabActions); - const clearTabActions = useTabActionsStore((state) => state.clearTabActions); + // ===== TAB ACTIONS COMPONENT ===== + const TabActions = useMemo(() => { + return function TabActionsComponent() { + const setTabActions = useTabActionsStore((state) => state.setTabActions); + const clearTabActions = useTabActionsStore( + (state) => state.clearTabActions + ); - useEffectHook(() => { - setTabActions( - tabId, -
- - } - className={{ - wrapper: 'w-full min-w-48 max-w-3xs', - inputWrapper: 'rounded-xl! shadow-button-soft', - input: 'placeholder:font-semibold placeholder:text-base-content/50', - }} - /> - - - - -
+ useEffectHook(() => { + setTabActions( + tabId, +
+ + } + className={{ + wrapper: 'w-full min-w-48 max-w-3xs', + inputWrapper: 'rounded-xl! shadow-button-soft', + input: + 'placeholder:font-semibold placeholder:text-base-content/50', + }} + /> - Export + handleFilterModalOpenRef.current()} + variant='outline' + className='px-3 py-2.5' + /> -
+ +
+ - -
- - } - > - - -
-
- ); + Export + +
+ + +
+ + } + > + + + +
+ ); + }, [setTabActions]); + + useEffectHook(() => { + return () => { + clearTabActions(tabId); + }; + }, [clearTabActions]); + + return null; + }; }, [ tabId, + filterParams, searchValue, - formik.values, isAnyExportLoading, - filterModal.open, - setTabActions, + handleExportExcel, + handleExportPDF, + isExcelExportLoading, + isPdfExportLoading, + searchChangeHandler, ]); - useEffectHook(() => { - return () => { - clearTabActions(tabId); - }; - }, [tabId, clearTabActions]); - - useEffectHook(() => { - return () => { - if (dateErrorShown) { - toast.dismiss(); - } - }; - }, [dateErrorShown]); - - useEffectHook(() => { - return () => { - if (dateErrorShown) { - toast.dismiss(); - setDateErrorShown(false); - } - }; - }, [filterModal.open, dateErrorShown]); + const TabActionsElement = useMemo(() => , [TabActions]); const getTableColumns = (): ColumnDef[] => { const tableColumns: ColumnDef[] = [ @@ -639,6 +646,7 @@ const DailyMarketingTab = ({ tabId }: DailyMarketingTabProps) => { return ( <> + {TabActionsElement}
{!isSubmitted ? ( { // ===== FILTER STATE ===== const [filterParams, setFilterParams] = useState({}); + const handleFilterModalOpenRef = useRef(() => {}); + const filterModal = useModal(); // ===== OPTIONS ===== @@ -96,11 +97,6 @@ const HppPerKandangTab = ({ tabId }: HppPerKandangTabProps) => { [] ); - const handleFilterModalOpen = () => { - filterModal.openModal(); - formik.validateForm(); - }; - // ===== FORMIK SETUP ===== const formik = useFormik({ initialValues: { @@ -141,6 +137,11 @@ const HppPerKandangTab = ({ tabId }: HppPerKandangTabProps) => { }, }); + handleFilterModalOpenRef.current = () => { + filterModal.openModal(); + formik.validateForm(); + }; + // ===== WEIGHT CHANGE HANDLERS ===== const handleWeightMinChange = useCallback( (e: React.ChangeEvent) => { @@ -443,87 +444,103 @@ const HppPerKandangTab = ({ tabId }: HppPerKandangTabProps) => { allDocSuppliers, ]); - // ===== REGISTER TAB ACTIONS TO STORE ===== - const setTabActions = useTabActionsStore((state) => state.setTabActions); - const clearTabActions = useTabActionsStore((state) => state.clearTabActions); + // ===== TAB ACTIONS COMPONENT ===== + const TabActions = useMemo(() => { + return function TabActionsComponent() { + const setTabActions = useTabActionsStore((state) => state.setTabActions); + const clearTabActions = useTabActionsStore( + (state) => state.clearTabActions + ); - useEffectHook(() => { - setTabActions( - tabId, -
- - - { + setTabActions( + tabId, +
+ handleFilterModalOpenRef.current()} variant='outline' - color='none' - isLoading={isAnyExportLoading} - className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft' + className='px-3 py-2.5' + /> + + +
+ + + Export + +
+ + +
+ + } > -
- + + + +
+ ); + }, [setTabActions]); - Export + useEffectHook(() => { + return () => { + clearTabActions(tabId); + }; + }, [clearTabActions]); -
- - -
- - } - > - - - -
- ); + return null; + }; }, [ tabId, - formik.values, + filterParams, isAnyExportLoading, - filterModal.open, - setTabActions, + handleExportExcel, + handleExportPDF, + isExcelExportLoading, + isPdfExportLoading, ]); - useEffectHook(() => { - return () => { - clearTabActions(tabId); - }; - }, [tabId, clearTabActions]); + const TabActionsElement = useMemo(() => , [TabActions]); const getTableColumns = (): ColumnDef[] => { const tableColumns: ColumnDef[] = [ @@ -768,6 +785,7 @@ const HppPerKandangTab = ({ tabId }: HppPerKandangTabProps) => { return ( <> + {TabActionsElement}
{!isSubmitted ? ( { const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); + const handleFilterModalOpenRef = useRef(() => {}); + const filterModal = useModal(); // ===== TABLE COLUMNS ===== @@ -242,6 +250,11 @@ const ProductionResultContent = ({ tabId }: ProductionResultTabProps) => { }, }); + handleFilterModalOpenRef.current = () => { + filterModal.openModal(); + formik.validateForm(); + }; + // ===== OPTIONS ===== const { setInputValue: setAreaInputValue, @@ -519,91 +532,108 @@ const ProductionResultContent = ({ tabId }: ProductionResultTabProps) => { setIsPdfExportLoading(false); }, [filterParams]); - // ===== REGISTER TAB ACTIONS TO STORE ===== - const setTabActions = useTabActionsStore((state) => state.setTabActions); - const clearTabActions = useTabActionsStore((state) => state.clearTabActions); + // ===== TAB ACTIONS COMPONENT ===== + const TabActions = useMemo(() => { + return function TabActionsComponent() { + const setTabActions = useTabActionsStore((state) => state.setTabActions); + const clearTabActions = useTabActionsStore( + (state) => state.clearTabActions + ); - useEffect(() => { - setTabActions( - tabId, -
- filterModal.openModal()} - variant='outline' - className='px-3 py-2.5' - /> - - { + setTabActions( + tabId, +
+ handleFilterModalOpenRef.current()} variant='outline' - color='none' - isLoading={isAnyExportLoading} - className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft' + className='px-3 py-2.5' + /> + + +
+ + + Export + +
+ + +
+ + } > -
- + + + +
+ ); + }, [setTabActions]); - Export + useEffect(() => { + return () => { + clearTabActions(tabId); + }; + }, [clearTabActions]); -
- - -
- - } - > - - - -
- ); + return null; + }; }, [ tabId, filterParams, isAnyExportLoading, exportToExcelHandler, exportToPdfHandler, - setTabActions, + isExcelExportLoading, + isPdfExportLoading, ]); - useEffect(() => { - return () => { - clearTabActions(tabId); - }; - }, [tabId, clearTabActions]); + // Render the TabActions component + const TabActionsElement = useMemo(() => , [TabActions]); return ( <> + {TabActionsElement}
{!isSubmitted ? ( ({ + return Object.entries(aliases).map(([key]) => ({ value: key, label: formatConstantLabel(key), }));