From d8599a850aa9613e6eb9b441b84afdfab1019b4b Mon Sep 17 00:00:00 2001 From: rstubryan Date: Mon, 3 Nov 2025 10:40:13 +0700 Subject: [PATCH] refactor(FE-170): streamline RecordingTable component by removing unused state and optimizing layout for better usability --- .../production/recording/RecordingTable.tsx | 522 ++++-------------- 1 file changed, 114 insertions(+), 408 deletions(-) diff --git a/src/components/pages/production/recording/RecordingTable.tsx b/src/components/pages/production/recording/RecordingTable.tsx index b4dbee3a..722589c8 100644 --- a/src/components/pages/production/recording/RecordingTable.tsx +++ b/src/components/pages/production/recording/RecordingTable.tsx @@ -1,26 +1,22 @@ 'use client'; -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useState } from 'react'; import useSWR from 'swr'; import { Icon } from '@iconify/react'; -import { SortingState } from '@tanstack/react-table'; +import { SortingState, CellContext } from '@tanstack/react-table'; import { cn } from '@/lib/helper'; import { useModal } from '@/components/Modal'; import Button from '@/components/Button'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import { OptionType } from '@/components/input/SelectInput'; import SelectInput from '@/components/input/SelectInput'; +import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import { ROWS_OPTIONS } from '@/config/constant'; -import CheckboxInput from '@/components/input/CheckboxInput'; -import { TableToolbar } from '@/components/table/TableToolbar'; -import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector'; import Table from '@/components/Table'; import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; -import { type CellContext } from '@tanstack/react-table'; import { type Recording } from '@/types/api/production/recording'; -import { type BaseApiResponse } from '@/types/api/api-general'; import { RecordingApi } from '@/services/api/production'; import { AreaApi } from '@/services/api/master-data'; import { LocationApi } from '@/services/api/master-data'; @@ -103,17 +99,12 @@ const RecordingTable = () => { }); const [sorting, setSorting] = useState([]); - const [rowSelection, setRowSelection] = useState>({}); const [selectedRecording, setSelectedRecording] = useState< Recording | undefined >(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); - const [isBulkApproveLoading, setIsBulkApproveLoading] = useState(false); - const [isBulkRejectLoading, setIsBulkRejectLoading] = useState(false); const singleDeleteModal = useModal(); - const bulkApproveModal = useModal(); - const bulkRejectModal = useModal(); // State for dropdown search const [locationSelectInputValue, setLocationSelectInputValue] = useState(''); @@ -205,62 +196,6 @@ const RecordingTable = () => { [setPageSize, setPage] ); - const paginatedData = useMemo(() => { - if (!recordings || recordings.status !== 'success') return []; - - return recordings.data; - }, [recordings]); - - const selectedRowIds = Object.keys(rowSelection).map((item) => - parseInt(item) - ); - - const bulkApproveHandler = async () => { - setIsBulkApproveLoading(true); - - const approveResponse = await RecordingApi.approve( - selectedRowIds, - 'Bulk Approved' - ); - - if (isResponseSuccess(approveResponse)) { - await refreshRecordings(); - setRowSelection({}); - bulkApproveModal.closeModal(); - toast.success( - `Successfully approved ${selectedRowIds.length} recordings!` - ); - } - if (isResponseError(approveResponse)) { - toast.error(approveResponse?.message as string); - bulkApproveModal.closeModal(); - } - setIsBulkApproveLoading(false); - }; - - const bulkRejectHandler = async () => { - setIsBulkRejectLoading(true); - - const rejectResponse = await RecordingApi.reject( - selectedRowIds, - 'Bulk Rejected' - ); - - if (isResponseSuccess(rejectResponse)) { - refreshRecordings(); - setRowSelection({}); - bulkRejectModal.closeModal(); - toast.success( - `Successfully rejected ${selectedRowIds.length} recordings!` - ); - } - if (isResponseError(rejectResponse)) { - toast.error(rejectResponse?.message as string); - bulkRejectModal.closeModal(); - } - setIsBulkRejectLoading(false); - }; - const singleDeleteHandler = async () => { setIsDeleteLoading(true); @@ -273,315 +208,123 @@ const RecordingTable = () => { }; return ( -
+
- - - - {/* Filter Dropdowns - Desktop */} -
- { - const selectedValue = selected as OptionType | null; - setSelectedArea(selectedValue); - setSelectedLocation(null); - setSelectedKandang(null); - updateFilter( - 'areaFilter', - selectedValue ? selectedValue.value.toString() : '' - ); - updateFilter('locationFilter', ''); - updateFilter('kandangFilter', ''); - setPage(1); - }} - className={{ wrapper: 'w-full' }} - onInputChange={(value) => setAreaSelectInputValue(value)} - isLoading={isLoadingAreas} - isClearable - /> - - { - const selectedValue = selected as OptionType | null; - setSelectedLocation(selectedValue); - setSelectedKandang(null); - updateFilter( - 'locationFilter', - selectedValue ? selectedValue.value.toString() : '' - ); - updateFilter('kandangFilter', ''); - setPage(1); - }} - className={{ wrapper: 'w-full' }} - onInputChange={(value) => setLocationSelectInputValue(value)} - isLoading={isLoadingLocations} - isClearable - isDisabled={!selectedArea} - /> - - { - const selectedValue = selected as OptionType | null; - setSelectedKandang(selectedValue); - updateFilter( - 'kandangFilter', - selectedValue ? selectedValue.value.toString() : '' - ); - setPage(1); - }} - className={{ wrapper: 'w-full' }} - onInputChange={(value) => setKandangSelectInputValue(value)} - isLoading={isLoadingKandang} - isClearable - isDisabled={!selectedLocation} - /> - - { - const selectedValue = selected as OptionType | null; - updateFilter( - 'periodFilter', - selectedValue ? selectedValue.value.toString() : '' - ); - setPage(1); - }} - className={{ wrapper: 'w-full' }} - isClearable - /> -
- - {/* Filter Dropdowns - Mobile */} -
- { - const selectedValue = selected as OptionType | null; - setSelectedArea(selectedValue); - setSelectedLocation(null); - setSelectedKandang(null); - updateFilter( - 'areaFilter', - selectedValue ? selectedValue.value.toString() : '' - ); - updateFilter('locationFilter', ''); - updateFilter('kandangFilter', ''); - setPage(1); - }} - className={{ wrapper: 'w-full' }} - onInputChange={(value) => setAreaSelectInputValue(value)} - isLoading={isLoadingAreas} - isClearable - /> - - { - const selectedValue = selected as OptionType | null; - setSelectedLocation(selectedValue); - setSelectedKandang(null); - updateFilter( - 'locationFilter', - selectedValue ? selectedValue.value.toString() : '' - ); - updateFilter('kandangFilter', ''); - setPage(1); - }} - className={{ wrapper: 'w-full' }} - onInputChange={(value) => setLocationSelectInputValue(value)} - isLoading={isLoadingLocations} - isClearable - isDisabled={!selectedArea} - /> - - { - const selectedValue = selected as OptionType | null; - setSelectedKandang(selectedValue); - updateFilter( - 'kandangFilter', - selectedValue ? selectedValue.value.toString() : '' - ); - setPage(1); - }} - className={{ wrapper: 'w-full' }} - onInputChange={(value) => setKandangSelectInputValue(value)} - isLoading={isLoadingKandang} - isClearable - isDisabled={!selectedLocation} - /> - - { - const selectedValue = selected as OptionType | null; - updateFilter( - 'periodFilter', - selectedValue ? selectedValue.value.toString() : '' - ); - setPage(1); - }} - className={{ wrapper: 'w-full' }} - isClearable - /> -
-
- - {/* Bulk action buttons */} -
- {selectedRowIds.length > 0 && ( -
+
+
-
- )} - + +
- +
+ { + const selectedValue = selected as OptionType | null; + setSelectedArea(selectedValue); + setSelectedLocation(null); + setSelectedKandang(null); + updateFilter( + 'areaFilter', + selectedValue ? selectedValue.value.toString() : '' + ); + updateFilter('locationFilter', ''); + updateFilter('kandangFilter', ''); + setPage(1); + }} + onInputChange={setAreaSelectInputValue} + isClearable + className={{ + wrapper: 'col-span-12 sm:col-span-3', + }} + /> + + { + const selectedValue = selected as OptionType | null; + setSelectedLocation(selectedValue); + setSelectedKandang(null); + updateFilter( + 'locationFilter', + selectedValue ? selectedValue.value.toString() : '' + ); + updateFilter('kandangFilter', ''); + setPage(1); + }} + onInputChange={setLocationSelectInputValue} + isClearable + isDisabled={!selectedArea} + className={{ + wrapper: 'col-span-12 sm:col-span-3', + }} + /> + + { + const selectedValue = selected as OptionType | null; + setSelectedKandang(selectedValue); + updateFilter( + 'kandangFilter', + selectedValue ? selectedValue.value.toString() : '' + ); + setPage(1); + }} + onInputChange={setKandangSelectInputValue} + isClearable + isDisabled={!selectedLocation} + className={{ + wrapper: 'col-span-12 sm:col-span-2', + }} + /> + + +
- + data={isResponseSuccess(recordings) ? recordings?.data : []} columns={[ - { - id: 'select', - header: ({ table }) => ( -
- -
- ), - cell: ({ row }) => ( -
- -
- ), - }, { header: '#', cell: (props) => @@ -609,38 +352,6 @@ const RecordingTable = () => { cell: (props) => props.row.original.total_chick_qty?.toLocaleString() || '-', }, - { - header: 'BW', - cell: (props) => - props.row.original.avg_daily_gain?.toFixed(2) || '-', - }, - { - header: 'Pakan', - cell: (props) => - props.row.original.cum_intake?.toLocaleString() || '-', - }, - { - header: 'FCR', - cell: (props) => props.row.original.fcr_value?.toFixed(2) || '-', - }, - { - accessorKey: 'total_depletion', - header: 'Total Deplesi', - cell: (props) => props.row.original.total_depletion_qty, - }, - { - header: 'Deplesi (%)', - cell: (props) => - props.row.original.daily_depletion_rate?.toFixed(2) || '-', - }, - { - header: 'Populasi Akhir', - cell: (props) => - ( - props.row.original.total_chick_qty - - props.row.original.total_depletion_qty - )?.toLocaleString() || '-', - }, { header: 'Tanggal Submit', cell: (props) => @@ -690,23 +401,18 @@ const RecordingTable = () => { }, ]} pageSize={tableFilterState.pageSize} - page={ - recordings?.status === 'success' - ? recordings.meta?.page - : tableFilterState.page - } + page={isResponseSuccess(recordings) ? recordings?.meta?.page : 0} totalItems={ - recordings?.status === 'success' ? recordings.meta?.total_results : 0 + isResponseSuccess(recordings) ? recordings?.meta?.total_results : 0 } onPageChange={setPage} isLoading={isLoading} sorting={sorting} setSorting={setSorting} - rowSelection={rowSelection} - setRowSelection={setRowSelection} className={{ containerClassName: cn({ - 'mb-20': paginatedData.length === 0, + 'mb-20': + isResponseSuccess(recordings) && recordings?.data?.length === 0, }), tableWrapperClassName: 'overflow-x-auto min-h-full!', tableClassName: 'font-inter w-full table-auto min-h-full!',