'use client'; import React, { useCallback, useState, useEffect, useMemo } from 'react'; import useSWR from 'swr'; import { Icon } from '@iconify/react'; import { ColumnDef, SortingState } from '@tanstack/react-table'; import { cn, formatDate } from '@/lib/helper'; import Button from '@/components/Button'; import UniformityChart from '@/components/pages/uniformity/UniformityChart'; // import UniformityStat from '@/components/pages/uniformity/chart/UniformityStat'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { UniformityApi } from '@/services/api/uniformity'; import { type Uniformity } from '@/types/api/uniformity/uniformity'; import { isResponseSuccess } from '@/lib/api-helper'; import Table from '@/components/Table'; import Badge from '@/components/Badge'; import CheckboxInput from '@/components/input/CheckboxInput'; import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import toast from 'react-hot-toast'; import Card from '@/components/Card'; import UniformityTableSkeleton from './skeleton/UniformityTableSkeleton'; import RequirePermission from '@/components/helper/RequirePermission'; import { useUniformityStore } from '@/stores/uniformity/uniformity.store'; import FloatingActionsButton from '@/components/FloatingActionsButton'; const statusColorMap: Record = { APPROVED: 'bg-[#00D39033]', REJECTED: 'bg-error/10', CREATED: 'bg-[#f3f3f4]', }; const statusIndicatorColorMap: Record = { APPROVED: 'bg-[#008000]', REJECTED: 'bg-error', CREATED: 'bg-[#D9D9D9]', }; const statusTextMap: Record = { APPROVED: 'Disetujui', REJECTED: 'Ditolak', CREATED: 'Pengajuan', }; const getStatusColor = (status: string): string => { return statusColorMap[status] || 'bg-info'; }; const getStatusIndicatorColor = (status: string): string => { return statusIndicatorColorMap[status] || 'bg-info'; }; const getStatusText = (status: string): string => { return statusTextMap[status] || status; }; const isUniformityLocked = (uniformity: Uniformity): boolean => { return uniformity.status === 'APPROVED' || uniformity.status === 'REJECTED'; }; const UniformityTable = ({ refresh }: { refresh?: () => void }) => { const isSuccess = useUniformityStore((s) => s.isSuccess); const setIsSuccess = useUniformityStore((s) => s.setIsSuccess); const { state: tableFilterState, setPage, toQueryString: getTableFilterQueryString, } = useTableFilter({ initial: { search: '', }, paramMap: { page: 'page', pageSize: 'limit', search: 'search', }, }); const [sorting, setSorting] = useState([]); const [rowSelection, setRowSelection] = useState>({}); const [selectedUniformity] = useState(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isBulkActionLoading, setIsBulkActionLoading] = useState(false); const singleDeleteModal = useModal(); const bulkDeleteModal = useModal(); const successModal = useModal(); const singleApproveModal = useModal(); const singleRejectModal = useModal(); const bulkApproveModal = useModal(); const bulkRejectModal = useModal(); const { data: uniformities, isLoading, mutate: refreshUniformities, } = useSWR( `${UniformityApi.basePath}${getTableFilterQueryString()}`, UniformityApi.getAllFetcher ); const selectedRowIds = useMemo(() => { return Object.keys(rowSelection) .filter((key) => rowSelection[key]) .map((key) => parseInt(key)); }, [rowSelection]); const selectedUniformities = useMemo(() => { if (!isResponseSuccess(uniformities) || !uniformities.data) return []; return uniformities.data.filter((u) => selectedRowIds.includes(u.id)); }, [uniformities, selectedRowIds]); const canApproveReject = useMemo(() => { return ( selectedUniformities.length > 0 && selectedUniformities.every((u) => u.status === 'CREATED') ); }, [selectedUniformities]); useEffect(() => { if (isSuccess) { successModal.openModal(); } }, [isSuccess, successModal]); const handleSuccessModalClose = () => { successModal.closeModal(); setIsSuccess(false); }; const singleDeleteHandler = useCallback(async () => { setIsDeleteLoading(true); await UniformityApi.delete(selectedUniformity?.id as number); refreshUniformities(); singleDeleteModal.closeModal(); toast.success('Successfully delete Uniformity!'); setIsDeleteLoading(false); }, [selectedUniformity?.id, refreshUniformities, singleDeleteModal]); const singleApproveHandler = useCallback(async () => { setIsDeleteLoading(true); try { await UniformityApi.approve([selectedUniformity?.id as number]); refreshUniformities(); singleApproveModal.closeModal(); toast.success('Successfully approved Uniformity!'); } catch { toast.error('Failed to approve Uniformity'); } finally { setIsDeleteLoading(false); } }, [selectedUniformity?.id, refreshUniformities, singleApproveModal]); const singleRejectHandler = useCallback(async () => { setIsDeleteLoading(true); try { await UniformityApi.reject([selectedUniformity?.id as number]); refreshUniformities(); singleRejectModal.closeModal(); toast.success('Successfully rejected Uniformity!'); } catch { toast.error('Failed to reject Uniformity'); } finally { setIsDeleteLoading(false); } }, [selectedUniformity?.id, refreshUniformities, singleRejectModal]); const handleBulkDelete = useCallback(() => { bulkDeleteModal.openModal(); }, [bulkDeleteModal]); const bulkDeleteHandler = useCallback(async () => { setIsBulkActionLoading(true); try { await UniformityApi.bulkDelete(selectedRowIds); setRowSelection({}); refreshUniformities(); bulkDeleteModal.closeModal(); toast.success( `Successfully deleted ${selectedRowIds.length} Uniformity data!` ); } catch { toast.error('Failed to delete Uniformity data'); } finally { setIsBulkActionLoading(false); } }, [selectedRowIds, refreshUniformities, bulkDeleteModal]); const handleCloseFab = useCallback(() => { setRowSelection({}); }, []); const handleBulkApprove = useCallback(() => { bulkApproveModal.openModal(); }, [bulkApproveModal]); const handleBulkReject = useCallback(() => { bulkRejectModal.openModal(); }, [bulkRejectModal]); const bulkApproveHandler = useCallback(async () => { setIsBulkActionLoading(true); try { await UniformityApi.approve(selectedRowIds); setRowSelection({}); refreshUniformities(); bulkApproveModal.closeModal(); toast.success( `Successfully approved ${selectedRowIds.length} Uniformity data!` ); } catch { toast.error('Failed to approve Uniformity data'); } finally { setIsBulkActionLoading(false); } }, [selectedRowIds, refreshUniformities, bulkApproveModal]); const bulkRejectHandler = useCallback(async () => { setIsBulkActionLoading(true); try { await UniformityApi.reject(selectedRowIds); setRowSelection({}); refreshUniformities(); bulkRejectModal.closeModal(); toast.success( `Successfully rejected ${selectedRowIds.length} Uniformity data!` ); } catch (error) { toast.error('Failed to reject Uniformity data'); } finally { setIsBulkActionLoading(false); } }, [selectedRowIds, refreshUniformities, bulkRejectModal]); useEffect(() => { if (isResponseSuccess(uniformities) && uniformities.data) { const newSelection: Record = {}; Object.entries(rowSelection).forEach(([rowId, isSelected]) => { if (isSelected) { const uniformity = uniformities.data.find( (r) => r.id === parseInt(rowId) ); if (uniformity && !isUniformityLocked(uniformity)) { newSelection[rowId] = true; } } }); if ( Object.keys(newSelection).length !== Object.keys(rowSelection).length ) { setRowSelection(newSelection); } } }, [uniformities, rowSelection]); // ===== TABLE COLUMNS DEFINITION ===== const uniformityColumns: ColumnDef[] = useMemo( () => [ { id: 'select', header: ({ table }) => { const allRows = table.getRowModel().rows; const selectableRows = allRows.filter((row) => { const uniformity = row.original; return !isUniformityLocked(uniformity); }); const hasNoSelectableRows = selectableRows.length === 0; const handleSelectAll = () => { const isAllSelected = selectableRows.every((row) => row.getIsSelected() ); selectableRows.forEach((row) => { row.toggleSelected(!isAllSelected); }); }; const isAllSelected = selectableRows.length > 0 && selectableRows.every((row) => row.getIsSelected()); const isSomeSelected = selectableRows.some((row) => row.getIsSelected() ); return (
); }, cell: ({ row }) => { const uniformity = row.original; const isDisabled = isUniformityLocked(uniformity); return (
); }, }, { accessorKey: 'location.name', header: 'Lokasi', cell: (props) => props.row.original.location_name || '-', }, { accessorKey: 'flock_name', header: 'Flock', cell: (props) => props.row.original.flock_name || '-', }, { accessorKey: 'kandang_name', header: 'Kandang', cell: (props) => props.row.original.kandang_name || '-', }, { accessorKey: 'week', header: 'Tanggal (Week)', cell: (props) => `${formatDate(props.row.original.applied_at, 'DD MMM YYYY')} (${props.row.original.week})`, }, { accessorKey: 'status', header: 'Status', cell: (props) => { const status = props.row.original.status; return (
{getStatusText(status)}
); }, }, { accessorKey: 'uniformity', header: 'Uniformity', cell: (props) => { const uniformity = props.row.original.uniformity; return {uniformity}%; }, }, ], [] ); return ( <>
{/*
*/}
data={isResponseSuccess(uniformities) ? uniformities?.data : []} columns={uniformityColumns} pageSize={tableFilterState.pageSize} page={isResponseSuccess(uniformities) ? uniformities?.meta?.page : 0} totalItems={ isResponseSuccess(uniformities) ? uniformities?.meta?.total_results : 0 } onPageChange={setPage} isLoading={isLoading} sorting={sorting} setSorting={setSorting} rowSelection={rowSelection} setRowSelection={setRowSelection} className={{ containerClassName: cn({ 'mb-20': isResponseSuccess(uniformities) && uniformities?.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 last:flex last:flex-row last:justify-end', bodyRowClassName: 'border-b border-b-gray-200', bodyColumnClassName: 'px-6 py-3 last:flex last:flex-row last:justify-end', }} emptyContent={} /> {' '}
props.row.original.label, }, { accessorKey: 'value', header: 'Value', cell: (props) => {props.row.original.value}, }, ]} pageSize={6} className={{ containerClassName: 'mb-0', paginationClassName: 'hidden', }} />
props.row.original.label, }, { accessorKey: 'value', header: 'Value', cell: (props) => {props.row.original.value}, }, ]} pageSize={6} className={{ containerClassName: 'mb-0', paginationClassName: 'hidden', }} />
props.row.original.label, }, { accessorKey: 'value', header: 'Value', cell: (props) => {props.row.original.value}, }, ]} pageSize={6} className={{ containerClassName: 'mb-0', paginationClassName: 'hidden', }} />
props.row.original.label, }, { accessorKey: 'value', header: 'Value', cell: (props) => {props.row.original.value}, }, ]} pageSize={6} className={{ containerClassName: 'mb-0', paginationClassName: 'hidden', }} />
props.row.original.label, }, { accessorKey: 'value', header: 'Value', cell: (props) => {props.row.original.value}, }, ]} pageSize={6} className={{ containerClassName: 'mb-0', paginationClassName: 'hidden', }} />
({ id: `bulk-approve-${index}`, label: `${index + 1}. ${u.location_name}`, value: `${u.flock_name} - ${u.kandang_name}`, }))} columns={[ { accessorKey: 'label', header: 'Label', cell: (props) => props.row.original.label, }, { accessorKey: 'value', header: 'Value', cell: (props) => {props.row.original.value}, }, ]} pageSize={selectedUniformities.length} className={{ containerClassName: 'mb-0', paginationClassName: 'hidden', }} />
({ id: `bulk-reject-${index}`, label: `${index + 1}. ${u.location_name}`, value: `${u.flock_name} - ${u.kandang_name}`, }))} columns={[ { accessorKey: 'label', header: 'Label', cell: (props) => props.row.original.label, }, { accessorKey: 'value', header: 'Value', cell: (props) => {props.row.original.value}, }, ]} pageSize={selectedUniformities.length} className={{ containerClassName: 'mb-0', paginationClassName: 'hidden', }} /> {/* Floating Actions Button */} ); }; export default UniformityTable;