'use client'; import { useState, useMemo, useEffect } from 'react'; import useSWR from 'swr'; import { Icon } from '@iconify/react'; import { useFormik } from 'formik'; import toast from 'react-hot-toast'; import { ColumnDef } from '@tanstack/react-table'; import { AxiosError } from 'axios'; import { FinanceApi } from '@/services/api/report/finance-report'; import { CustomerApi } from '@/services/api/master-data'; import { UserApi } from '@/services/api/user'; import { useSelect, OptionType } from '@/components/input/SelectInput'; import { httpClientFetcher, SWRHttpKey } from '@/services/http/client'; import { BaseApiResponse } from '@/types/api/api-general'; import SelectInputCheckbox from '@/components/input/SelectInputCheckbox'; import SelectInputRadio from '@/components/input/SelectInputRadio'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { useTabActionsStore } from '@/stores/tab-actions/tab-actions.store'; import ButtonFilter from '@/components/helper/ButtonFilter'; import { formatCurrency, formatNumber } from '@/lib/helper'; import { isResponseSuccess } from '@/lib/api-helper'; import { BalanceMonitoringRow } from '@/types/api/report/balance-monitoring'; import { CustomerPaymentRow } from '@/types/api/report/customer-payment'; import Modal, { useModal } from '@/components/Modal'; import Button from '@/components/Button'; import DateInput from '@/components/input/DateInput'; import Table from '@/components/Table'; import CustomerSupplierSkeleton from '@/components/pages/report/finance/skeleton/CustomerSupplierSkeleton'; interface BalanceMonitoringTabProps { tabId: string; } const filterByOptions: OptionType[] = [ { label: 'Tanggal Penjualan (SO Date)', value: 'sold_at' }, { label: 'Tanggal Realisasi (Delivery Date)', value: 'realized_at' }, ]; const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => { const [hasDateError, setHasDateError] = useState(false); const [dateErrorShown, setDateErrorShown] = useState(false); const filterModal = useModal(); const setTabActions = useTabActionsStore((state) => state.setTabActions); const clearTabActions = useTabActionsStore((state) => state.clearTabActions); const { state: tableFilterState, updateFilter, setPage, setPageSize, toQueryString: getTableFilterQueryString, reset: resetFilter, } = useTableFilter<{ start_date: string; end_date: string; customers: OptionType[]; salesPersons: OptionType[]; filterBy?: OptionType; sort_by: string; order_by: string; }>({ initial: { start_date: '', end_date: '', customers: [], salesPersons: [], filterBy: undefined, sort_by: '', order_by: '', }, paramMap: { page: 'page', pageSize: 'limit', start_date: 'start_date', end_date: 'end_date', customers: 'customer_ids', salesPersons: 'sales_ids', filterBy: 'filter_by', sort_by: 'sort_by', order_by: 'sort_order', }, persist: true, storeName: 'balance-monitoring-table', }); // const sorting: SortingState = tableFilterState.sort_by // ? [ // { // id: tableFilterState.sort_by, // desc: tableFilterState.order_by === 'desc', // }, // ] // : []; // const handleSortingChange = (updater: Updater) => { // const next = typeof updater === 'function' ? updater(sorting) : updater; // if (next.length > 0) { // updateFilter('sort_by', next[0].id, true); // updateFilter('order_by', next[0].desc ? 'desc' : 'asc', true); // } else { // updateFilter('sort_by', '', true); // updateFilter('order_by', '', true); // } // }; const { options: customerOptions, setInputValue: setCustomerInput, isLoadingOptions: isLoadingCustomers, loadMore: loadMoreCustomers, } = useSelect(CustomerApi.basePath, 'id', 'name', 'search'); const { options: salesOptions, setInputValue: setSalesInput, isLoadingOptions: isLoadingSales, loadMore: loadMoreSales, } = useSelect(UserApi.basePath, 'id', 'name', 'search'); const formik = useFormik({ initialValues: { start_date: tableFilterState.start_date, end_date: tableFilterState.end_date, customers: tableFilterState.customers, salesPersons: tableFilterState.salesPersons, filterBy: tableFilterState.filterBy, }, onSubmit: (values) => { updateFilter('start_date', values.start_date, true); updateFilter('end_date', values.end_date, true); updateFilter('customers', values.customers, true); updateFilter('salesPersons', values.salesPersons, true); updateFilter('filterBy', values.filterBy, true); filterModal.closeModal(); }, }); const formikResetHandler = () => { resetFilter(); setHasDateError(false); if (dateErrorShown) { toast.dismiss(); setDateErrorShown(false); } formik.resetForm({ values: { start_date: '', end_date: '', customers: [], salesPersons: [], filterBy: undefined, }, }); filterModal.closeModal(); }; const handleStartDateChange = (e: React.ChangeEvent) => { const value = e.target.value; formik.setFieldValue('start_date', value); if (value && formik.values.end_date) { if (new Date(formik.values.end_date) < new Date(value)) { setHasDateError(true); if (!dateErrorShown) { toast.error('Tanggal akhir tidak boleh masa lampau', { duration: Infinity, }); setDateErrorShown(true); } } else { setHasDateError(false); if (dateErrorShown) { toast.dismiss(); setDateErrorShown(false); } } } else { setHasDateError(false); } }; const handleEndDateChange = (e: React.ChangeEvent) => { const value = e.target.value; formik.setFieldValue('end_date', value); if (value && formik.values.start_date) { if (new Date(value) < new Date(formik.values.start_date)) { setHasDateError(true); if (!dateErrorShown) { toast.error('Tanggal akhir tidak boleh masa lampau', { duration: Infinity, }); setDateErrorShown(true); } return; } } setHasDateError(false); if (dateErrorShown) { toast.dismiss(); setDateErrorShown(false); } }; const { data: balanceMonitoringsResponse, isLoading } = useSWR< BaseApiResponse, AxiosError, SWRHttpKey >( `${FinanceApi.basePath}/balance-monitoring${getTableFilterQueryString()}`, httpClientFetcher ); const balanceMonitorings: BalanceMonitoringRow[] = isResponseSuccess( balanceMonitoringsResponse ) ? ((balanceMonitoringsResponse.data as BalanceMonitoringRow[]) ?? []) : []; const meta = isResponseSuccess(balanceMonitoringsResponse) && balanceMonitoringsResponse.meta ? balanceMonitoringsResponse.meta : null; // Inject tab actions directly — no nested component, no remount cycle useEffect(() => { setTabActions( tabId,
); }, [tabId, setTabActions, tableFilterState, filterModal.openModal]); useEffect(() => { return () => clearTabActions(tabId); }, [tabId, clearTabActions]); const columns = useMemo( (): ColumnDef[] => [ { header: 'No', enableSorting: false, cell: (props) => (tableFilterState.page - 1) * tableFilterState.pageSize + props.row.index + 1, }, { header: 'Customer', accessorKey: 'customer.name', enableSorting: true, id: 'customer_name', cell: ({ row }) => row.original.customer.name, }, { header: 'Saldo Awal', accessorKey: 'saldo_awal', id: 'saldo_awal', enableSorting: true, cell: ({ row }) => (
{formatCurrency(row.original.saldo_awal)}
), }, { header: 'Penjualan Ayam', columns: [ { header: 'Ekor', accessorKey: 'penjualan_ayam.ekor', id: 'penjualan_ayam_ekor', enableSorting: true, cell: ({ row }) => (
{formatNumber(row.original.penjualan_ayam.ekor)}
), }, { header: 'Kg', accessorKey: 'penjualan_ayam.kg', id: 'penjualan_ayam_kg', enableSorting: true, cell: ({ row }) => (
{formatNumber(row.original.penjualan_ayam.kg)}
), }, { header: 'Nominal', accessorKey: 'penjualan_ayam.nominal', id: 'penjualan_ayam_nominal', enableSorting: true, cell: ({ row }) => (
{formatCurrency(row.original.penjualan_ayam.nominal)}
), }, ], }, { header: 'Penjualan Telur', columns: [ { header: 'Butir', accessorKey: 'penjualan_telur.butir', id: 'penjualan_telur_butir', enableSorting: true, cell: ({ row }) => (
{formatNumber(row.original.penjualan_telur.butir)}
), }, { header: 'Kg', accessorKey: 'penjualan_telur.kg', id: 'penjualan_telur_kg', enableSorting: true, cell: ({ row }) => (
{formatNumber(row.original.penjualan_telur.kg)}
), }, { header: 'Nominal', accessorKey: 'penjualan_telur.nominal', id: 'penjualan_telur_nominal', enableSorting: true, cell: ({ row }) => (
{formatCurrency(row.original.penjualan_telur.nominal)}
), }, ], }, { header: 'Penjualan Trading', accessorKey: 'penjualan_trading.nominal', id: 'penjualan_trading', enableSorting: true, cell: ({ row }) => (
{formatCurrency(row.original.penjualan_trading.nominal)}
), }, { header: 'Pembayaran', accessorKey: 'pembayaran', id: 'pembayaran', enableSorting: true, cell: ({ row }) => (
{formatCurrency(row.original.pembayaran)}
), }, { header: 'Aging', accessorKey: 'aging', id: 'aging', enableSorting: true, cell: ({ row }) => (
{formatNumber(row.original.aging)} hari
), }, { header: 'Aging Rata-Rata', accessorKey: 'aging_rata_rata', id: 'aging_rata_rata', enableSorting: true, cell: ({ row }) => (
{formatNumber(row.original.aging_rata_rata)} hari
), }, { header: 'Saldo Akhir', accessorKey: 'saldo_akhir', id: 'saldo_akhir', enableSorting: true, cell: ({ row }) => (
{formatCurrency(row.original.saldo_akhir)}
), }, ], [tableFilterState.page, tableFilterState.pageSize] ); return ( <>
{isLoading && (
)} {!isLoading && balanceMonitorings.length === 0 && ( []} icon={ } title='Data Not Yet Available' subtitle='Please change your filters to get the data.' /> )} {!isLoading && balanceMonitorings.length > 0 && ( <>
)} {/* Filter Modal */} {/* Modal Header */}

Filter Data


formik.setFieldValue('customers', Array.isArray(val) ? val : []) } onInputChange={setCustomerInput} isLoading={isLoadingCustomers} isClearable onMenuScrollToBottom={loadMoreCustomers} className={{ wrapper: 'w-full' }} /> formik.setFieldValue( 'salesPersons', Array.isArray(val) ? val : [] ) } onInputChange={setSalesInput} isLoading={isLoadingSales} isClearable onMenuScrollToBottom={loadMoreSales} className={{ wrapper: 'w-full' }} /> formik.setFieldValue( 'filterBy', !Array.isArray(val) ? (val ?? undefined) : undefined ) } isClearable className={{ wrapper: 'w-full' }} />
{/* Modal Footer */}
); }; export default BalanceMonitoringTab;