From e9eee6eb3e18baee3e9beace8b7031f39c5a068c Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Thu, 6 Nov 2025 15:22:03 +0700 Subject: [PATCH] feat(FE-188,193): create ExpenseKandangsTable component --- .../expense/form/ExpenseKandangsTable.tsx | 230 ++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 src/components/pages/expense/form/ExpenseKandangsTable.tsx diff --git a/src/components/pages/expense/form/ExpenseKandangsTable.tsx b/src/components/pages/expense/form/ExpenseKandangsTable.tsx new file mode 100644 index 00000000..78b53541 --- /dev/null +++ b/src/components/pages/expense/form/ExpenseKandangsTable.tsx @@ -0,0 +1,230 @@ +'use client'; + +import { useCallback, useEffect, useState } from 'react'; +import useSWR from 'swr'; + +import { Icon } from '@iconify/react'; +import Collapse from '@/components/Collapse'; +import Card from '@/components/Card'; +import Table from '@/components/Table'; +import CheckboxInput from '@/components/input/CheckboxInput'; + +import { ColumnDef, ColumnSort, SortingState } from '@tanstack/react-table'; +import { cn, convertRowSelectionArrToObj } from '@/lib/helper'; +import { Kandang } from '@/types/api/master-data/kandang'; +import { useTableFilter } from '@/services/hooks/useTableFilter'; +import { KandangApi } from '@/services/api/master-data'; +import { isResponseSuccess } from '@/lib/api-helper'; + +interface ExpenseKandangsTableProps { + locationId?: number; + type: 'add' | 'edit' | 'detail'; + selectedKandangs: { + id: number; + name: string; + }[]; + onChange: (kandangs: { id: number; name: string }[]) => void; + className?: { + wrapper?: string; + }; +} + +const ExpenseKandangsTable = ({ + type, + locationId, + selectedKandangs, + onChange, + className, +}: ExpenseKandangsTableProps) => { + const { + state: tableFilterState, + updateFilter, + setPage, + toQueryString: getTableFilterQueryString, + } = useTableFilter({ + initial: { + search: '', + nameSort: '', + picSort: '', + locationId, + }, + paramMap: { + page: 'page', + pageSize: 'limit', + nameSort: 'sort_name', + picSort: 'sort_pic', + locationId: 'location_id', + }, + }); + + const { data: kandangs, isLoading } = useSWR( + locationId ? `${KandangApi.basePath}${getTableFilterQueryString()}` : null, + KandangApi.getAllFetcher + ); + + const [open, setOpen] = useState( + isResponseSuccess(kandangs) ? kandangs.data.length > 0 : false + ); + const [sorting, setSorting] = useState([]); + const [rowSelection, setRowSelection] = useState>( + convertRowSelectionArrToObj(selectedKandangs.map((item) => item.id)) + ); + + const kandangsColumns: ColumnDef[] = [ + { + id: 'select', + header: ({ table }) => ( +
+ +
+ ), + cell: ({ row }) => ( +
+ +
+ ), + }, + { + accessorKey: 'name', + header: 'Nama', + }, + { + accessorKey: 'pic', + header: 'PIC', + cell: (props) => props.row.original.pic.name, + }, + ]; + + const updateSortingFilter = useCallback( + ( + sortName: Exclude, + sortFilter: ColumnSort | undefined + ) => { + if (!sortFilter) { + updateFilter(sortName, ''); + } else { + updateFilter(sortName, sortFilter.desc ? 'desc' : 'asc'); + } + }, + [updateFilter] + ); + + useEffect(() => { + if (locationId) updateFilter('locationId', locationId); + }, [locationId, updateFilter]); + + useEffect(() => { + setOpen(isResponseSuccess(kandangs) ? kandangs.data.length > 0 : false); + }, [kandangs, isResponseSuccess]); + + 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) + ); + + return { + id: parseInt(item), + name: selectedKandang?.name ?? 'Kandang tidak ditemukan!', + }; + } + ); + + onChange(formattedSelectedKandangs); + } + }, [rowSelection]); + + useEffect(() => { + setRowSelection({}); + }, [locationId]); + + // track sorting + useEffect(() => { + const nameSortFilter = sorting.find((sortItem) => sortItem.id === 'name'); + const picSortFilter = sorting.find((sortItem) => sortItem.id === 'pic'); + + updateSortingFilter('nameSort', nameSortFilter); + updateSortingFilter('picSort', picSortFilter); + }, [sorting, updateSortingFilter]); + + return ( + + +
Pilih Kandang
+ + + + } + className='w-full!' + titleClassName='w-full p-0!' + > + + data={isResponseSuccess(kandangs) ? kandangs?.data : []} + columns={kandangsColumns} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(kandangs) ? kandangs?.meta?.page : 0} + totalItems={ + isResponseSuccess(kandangs) ? kandangs?.meta?.total_results : 0 + } + onPageChange={setPage} + isLoading={isLoading} + sorting={sorting} + setSorting={setSorting} + rowSelection={rowSelection} + setRowSelection={setRowSelection} + className={{ + containerClassName: cn({ + 'mb-20': + isResponseSuccess(kandangs) && kandangs?.data?.length === 0, + }), + tableWrapperClassName: 'overflow-x-auto min-h-full!', + tableClassName: 'font-inter w-full table-auto min-h-full!', + headerRowClassName: 'border-b border-b-gray-200', + headerColumnClassName: + 'px-6 py-3 text-xs font-semibold text-gray-500 first:flex first:flex-row first:justify-start', + bodyRowClassName: 'border-b border-b-gray-200', + bodyColumnClassName: + 'px-6 py-3 first:flex first:flex-row first:justify-start', + paginationClassName: cn({ + hidden: + isResponseSuccess(kandangs) && + kandangs?.meta?.total_pages === 1, + }), + }} + /> +
+
+ ); +}; + +export default ExpenseKandangsTable;