'use client'; import Button from '@/components/Button'; import CheckboxInput from '@/components/input/CheckboxInput'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import SelectInput, { OptionType, useSelect, } from '@/components/input/SelectInput'; import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; import Table from '@/components/Table'; import Dropdown from '@/components/Dropdown'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { cn, formatDate } from '@/lib/helper'; import { AreaApi, KandangApi, LocationApi } from '@/services/api/master-data'; import { ProjectFlockApi } from '@/services/api/production/project-flock'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { Kandang } from '@/types/api/master-data/kandang'; import { ProjectFlock } from '@/types/api/production/project-flock'; import { Icon } from '@iconify/react'; import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import { useRouter, usePathname } from 'next/navigation'; import { ChangeEventHandler, useEffect, useMemo, useState } from 'react'; import { useUiStore } from '@/stores/ui/ui.store'; import toast from 'react-hot-toast'; import useSWR from 'swr'; import { useFormik } from 'formik'; import RequirePermission from '@/components/helper/RequirePermission'; import StatusBadge from '@/components/helper/StatusBadge'; import PopoverButton from '@/components/popover/PopoverButton'; import PopoverContent from '@/components/popover/PopoverContent'; import ProjectFlockConfirmationModal from './ProjectFlockConfirmationModal'; import ProjectFlockTableSkeleton from '@/components/pages/production/project-flock/skeleton/ProjectFlockTableSkeleton'; import { useProjectFlockStore } from '@/stores/production/project-flock/project-flock.store'; import { ProjectFlockFormValues } from './form/ProjectFlockForm.schema'; import { useChickinStore } from '@/stores/production/chickin/chickin.store'; import { useProjectFlockClosingStore } from '@/stores/production/project-flock-closing/project-flock-closing.store'; import { ProjectFlockFilterSchema, ProjectFlockFilterType, } from './filter/ProjectFlockFilter'; import Modal from '@/components/Modal'; import SelectInputRadio from '@/components/input/SelectInputRadio'; import ButtonFilter from '@/components/helper/ButtonFilter'; const RowOptionsMenu = ({ props, popoverPosition = 'bottom', editClickHandler, detailClickHandler, deleteClickHandler, }: { props: CellContext; popoverPosition: 'bottom' | 'top'; editClickHandler: (id: number) => void; detailClickHandler: (id: number) => void; deleteClickHandler: () => void; }) => { // TODO: change this to real condition const showEditButton = true; const showDeleteButton = showEditButton; const popoverId = `projectFlock#${props.row.original.id}`; const popoverAnchorName = `--anchor-projectFlock#${props.row.original.id}`; const closePopover = () => { document.getElementById(popoverId)?.hidePopover(); }; const detailClickHandlerWrapper = () => { detailClickHandler(props.row.original.id); closePopover(); }; const editClickHandlerWrapper = () => { editClickHandler(props.row.original.id); closePopover(); }; return (
{showEditButton && ( )} {showDeleteButton && (
)}
); }; const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { const { searchValue, setSearchValue, setTableState } = useUiStore(); const pathname = usePathname(); const isSuccess = useProjectFlockStore((s) => s.isSuccess); const setIsSuccess = useProjectFlockStore((s) => s.setIsSuccess); const createdProjectFlock = useProjectFlockStore( (s) => s.createdProjectFlock ); const setCreatedProjectFlock = useProjectFlockStore( (s) => s.setCreatedProjectFlock ); const { state: tableFilterState, updateFilter, setPage, setPageSize, toQueryString: getTableFilterQueryString, } = useTableFilter({ initial: { search: '', area_id: '', location_id: '', kandang_id: '', category: '', period: '', }, paramMap: { page: 'page', pageSize: 'limit', search: 'search', area_id: 'area_id', location_id: 'location_id', kandang_id: 'kandang_id', category: 'category', period: 'period', }, }); const router = useRouter(); // ===== State ===== const [rowSelection, setRowSelection] = useState>({}); const selectedRowIds = Object.keys(rowSelection) .filter((id) => rowSelection[id]) .map((id) => parseInt(id)); const [sorting, setSorting] = useState([]); const deleteModal = useModal(); const confirmModal = useModal(); const successModal = useModal(); const chickinApproveModal = useModal(); const chickinDeleteModal = useModal(); const closingModal = useModal(); const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>( 'APPROVED' ); const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isApproveLoading, setIsApproveLoading] = useState(false); const [isLoadingExportingToExcel, setIsLoadingExportingToExcel] = useState(false); const { isChickinApproveModalOpen, isChickinApproveLoading, chickinApproveCallback, closeChickinApproveModal, setChickinApproveLoading, isChickinDeleteModalOpen, isChickinDeleteLoading, chickinDeleteCallback, closeChickinDeleteModal, setChickinDeleteLoading, } = useChickinStore(); const { isClosingModalOpen, isKandangClosed, isClosingLoading, closingCallback, closeClosingModal, setClosingLoading, } = useProjectFlockClosingStore(); // ===== FILTER MODAL STATE ===== const filterModal = useModal(); // ===== FILTER DEPENDENCIES STATE ===== const [filterAreaId, setFilterAreaId] = useState( undefined ); const [filterLocationId, setFilterLocationId] = useState( undefined ); // ===== FORMIK SETUP FOR FILTER ===== const formik = useFormik({ initialValues: { area_id: null, location_id: null, kandang_id: null, category: null, period: null, }, validationSchema: ProjectFlockFilterSchema, onSubmit: (values, { setSubmitting }) => { updateFilter('area_id', values.area_id || ''); updateFilter('location_id', values.location_id || ''); updateFilter('kandang_id', values.kandang_id || ''); updateFilter('category', values.category || ''); updateFilter('period', values.period || ''); filterModal.closeModal(); setSubmitting(false); }, onReset: () => { updateFilter('area_id', ''); updateFilter('location_id', ''); updateFilter('kandang_id', ''); updateFilter('category', ''); updateFilter('period', ''); setFilterAreaId(undefined); setFilterLocationId(undefined); filterModal.closeModal(); }, }); // ===== FILTER OPTIONS ===== const { setInputValue: setAreaInputValue, options: areaOptions, isLoadingOptions: isLoadingAreaOptions, loadMore: loadMoreAreas, } = useSelect(AreaApi.basePath, 'id', 'name'); const { setInputValue: setLocationInputValue, options: locationOptions, isLoadingOptions: isLoadingLocationOptions, loadMore: loadMoreLocations, } = useSelect(LocationApi.basePath, 'id', 'name', 'search', { area_id: filterAreaId || '', }); const { setInputValue: setKandangInputValue, options: kandangOptions, isLoadingOptions: isLoadingKandangOptions, loadMore: loadMoreKandangs, } = useSelect(KandangApi.basePath, 'id', 'name', 'search', { area_id: filterAreaId || '', location_id: filterLocationId || '', }); const categoryOptions = useMemo( () => [ { value: 'GROWING', label: 'Growing' }, { value: 'LAYING', label: 'Laying' }, ], [] ); const periodOptions = useMemo( () => [ { value: '1', label: 'Periode 1' }, { value: '2', label: 'Periode 2' }, ], [] ); // ===== FILTER HELPERS ===== const areaValue = useMemo(() => { if (!formik.values.area_id) return null; return ( areaOptions.find((opt) => String(opt.value) === formik.values.area_id) || null ); }, [formik.values.area_id, areaOptions]); const locationValue = useMemo(() => { if (!formik.values.location_id) return null; return ( locationOptions.find( (opt) => String(opt.value) === formik.values.location_id ) || null ); }, [formik.values.location_id, locationOptions]); const kandangValue = useMemo(() => { if (!formik.values.kandang_id) return null; return ( kandangOptions.find( (opt) => String(opt.value) === formik.values.kandang_id ) || null ); }, [formik.values.kandang_id, kandangOptions]); const categoryValue = useMemo(() => { if (!formik.values.category) return null; return ( categoryOptions.find((opt) => opt.value === formik.values.category) || null ); }, [formik.values.category, categoryOptions]); const periodValue = useMemo(() => { if (!formik.values.period) return null; return ( periodOptions.find((opt) => opt.value === formik.values.period) || null ); }, [formik.values.period, periodOptions]); // ===== FILTER DEPENDENCY HANDLERS ===== const handleFilterAreaChange = (area: OptionType | null) => { const areaId = area?.value ? String(area.value) : undefined; setFilterAreaId(areaId); if (!areaId) { setFilterLocationId(undefined); formik.setFieldValue('location_id', null); formik.setFieldValue('kandang_id', null); } }; const handleFilterLocationChange = (location: OptionType | null) => { const locationId = location?.value ? String(location.value) : undefined; setFilterLocationId(locationId); if (!locationId) { formik.setFieldValue('kandang_id', null); } }; // ===== HANDLE FILTER MODAL OPEN ===== const handleFilterModalOpen = () => { const areaId = tableFilterState.area_id || null; const locationId = tableFilterState.location_id || null; formik.setValues({ area_id: areaId, location_id: locationId, kandang_id: tableFilterState.kandang_id || null, category: tableFilterState.category || null, period: tableFilterState.period || null, }); setFilterAreaId(areaId || undefined); setFilterLocationId(locationId || undefined); filterModal.openModal(); }; // ===== Fetch Data ===== const { data: projectFlocks, isLoading, mutate: refreshProjectFlocks, } = useSWR( `${ProjectFlockApi.basePath}${getTableFilterQueryString()}`, ProjectFlockApi.getAllFetcher, { revalidateOnMount: true } ); // ====== HANDLER ====== const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); const response = await ProjectFlockApi.delete( selectedSingleRow?.id as number ); if (isResponseSuccess(response)) { toast.success(response?.message as string); } if (isResponseError(response)) { toast.error(response?.message as string); } refreshProjectFlocks(); deleteModal.closeModal(); setIsDeleteLoading(false); setRowSelection({}); }; useEffect(() => { updateFilter('search', searchValue); }, [searchValue, updateFilter]); useEffect(() => { setTableState('project-flock-table', pathname); }, [pathname, setTableState]); const searchChangeHandler: ChangeEventHandler = (e) => { setSearchValue(e.target.value); updateFilter('search', e.target.value); }; const confirmApprovalHandler = async ( notes: string, approvalAction: 'APPROVED' | 'REJECTED' ) => { setIsApproveLoading(true); const approveProjectFlockRes = approvalAction === 'APPROVED' ? await ProjectFlockApi.bulkApprove( selectedRowIds.map((id) => id), notes ) : await ProjectFlockApi.bulkReject( selectedRowIds.map((id) => id), notes ); if (isResponseSuccess(approveProjectFlockRes)) { const successMessage = approvalAction === 'APPROVED' ? 'Project Flock berhasil di-approve!' : 'Project Flock berhasil di-reject!'; toast.success(successMessage); confirmModal.closeModal(); } if (isResponseError(approveProjectFlockRes)) { toast.error(approveProjectFlockRes?.message as string); confirmModal.closeModal(); } setRowSelection({}); refreshProjectFlocks(); setIsApproveLoading(false); }; // ====== EFFECT ====== useEffect(() => { refreshProjectFlocks(); }, [refresh]); useEffect(() => { if (isChickinApproveModalOpen) { chickinApproveModal.openModal(); } else { chickinApproveModal.closeModal(); } }, [isChickinApproveModalOpen, chickinApproveModal]); useEffect(() => { if (isChickinDeleteModalOpen) { chickinDeleteModal.openModal(); } else { chickinDeleteModal.closeModal(); } }, [isChickinDeleteModalOpen, chickinDeleteModal]); useEffect(() => { if (isClosingModalOpen) { closingModal.openModal(); } else { closingModal.closeModal(); } }, [isClosingModalOpen, closingModal]); useEffect(() => { if (isSuccess) { successModal.openModal(); } }, [isSuccess, successModal]); const handleSuccessModalClose = () => { successModal.closeModal(); setIsSuccess(false); setCreatedProjectFlock(null); }; const projectFlockFormValues = useMemo(() => { if (!createdProjectFlock) return undefined; return { flock: { value: 0, label: createdProjectFlock.flock_name || '', }, flock_name: createdProjectFlock.flock_name || '', area: { value: createdProjectFlock.area_id, label: createdProjectFlock.area?.name || '', }, area_id: createdProjectFlock.area_id, category_option: { value: createdProjectFlock.category, label: createdProjectFlock.category, }, category: createdProjectFlock.category, production_standard: { value: createdProjectFlock.production_standard_id, label: createdProjectFlock.production_standard?.name || '', }, production_standard_id: createdProjectFlock.production_standard_id, location: { value: createdProjectFlock.location_id, label: createdProjectFlock.location?.name || '', }, location_id: createdProjectFlock.location_id, kandang_ids: createdProjectFlock.kandangs?.map((k) => k.id) || [], project_budgets: createdProjectFlock.project_budgets?.map((budget) => ({ nonstock: budget.nonstock ? { value: budget.nonstock_id, label: budget.nonstock.name || '', } : null, nonstock_id: budget.nonstock_id, qty: budget.qty, price: budget.price, total_price: budget.qty * budget.price, })) || [], } as ProjectFlockFormValues; }, [createdProjectFlock]); // ====== MEMO ====== const selectedSingleRow: ProjectFlock | null | undefined = useMemo(() => { return selectedRowIds.length === 1 ? isResponseSuccess(projectFlocks) ? projectFlocks?.data.find((row) => row.id === selectedRowIds[0]) : null : null; }, [rowSelection]); // const canApprove = useMemo(() => { // if (!selectedSingleRow || isApproveLoading) return false; // const isPengajuan = selectedSingleRow.approval?.step_number == 1; // const isNotRejected = selectedSingleRow.approval?.action != 'REJECTED'; // return isPengajuan && isNotRejected; // }, [selectedSingleRow, isApproveLoading]); const canApprove = useMemo(() => { return selectedRowIds.every((id) => { const projectFlock = isResponseSuccess(projectFlocks) ? projectFlocks?.data.find((row) => row.id === id) : null; const isProjectFlockRequesting = projectFlock?.approval?.step_number == 1; const isProjectFlockNotRejected = projectFlock?.approval?.action != 'REJECTED'; return isProjectFlockRequesting && isProjectFlockNotRejected; }); }, [selectedRowIds, projectFlocks]); // ====== COLUMNS ====== const columns = useMemo[]>( () => [ { id: 'select', header: ({ table }) => { const allRows = table.getRowModel().rows; const selectableRows = allRows.filter((row) => { const projectFlock = row.original; return ( projectFlock.approval?.step_number === 1 && projectFlock.approval?.action !== 'REJECTED' ); }); const allSelected = selectableRows.every((row) => row.getIsSelected()) && selectableRows.length != 0; const someSelected = selectableRows.some((row) => row.getIsSelected()) && !allSelected; const toggleSelectableRows = () => { const shouldSelect = !allSelected; selectableRows.forEach((row) => row.toggleSelected(shouldSelect)); }; const hasNoSelectableRows = selectableRows.length === 0; return (
); }, cell: ({ row }) => { return ( ); }, }, { accessorKey: 'flock_name', header: 'Flock', }, { accessorKey: 'area.name', header: 'Area', }, { accessorKey: 'location.name', header: 'Lokasi', }, { accessorKey: 'category', header: 'Kategori', }, { accessorKey: 'approval.step_name', header: 'Status', cell: (props) => { const approval = props.row.original.approval; const isRejected = approval?.action == 'REJECTED'; const isApproved = approval?.action == 'APPROVED'; let latestApprovalStepName = approval.step_name; const badgeColor = isRejected ? 'error' : isApproved ? approval?.step_number == 1 ? 'neutral' : approval?.step_number == 2 ? 'success' : approval?.step_number == 3 ? 'error' : 'neutral' : 'neutral'; switch (approval.action.toLowerCase()) { case 'pengajuan': latestApprovalStepName = 'Pengajuan'; break; case 'aktif': latestApprovalStepName = 'Aktif'; break; case 'Selesai': latestApprovalStepName = 'Closing'; break; } if (isRejected) { latestApprovalStepName = 'Ditolak'; } return ( ); }, }, { header: 'Kandang', cell: (props) => { const kandang = props.row.original.kandangs; if (kandang) { const kandangNames = kandang.map((k: Kandang) => k.name); return (
{kandangNames.length > 0 ? kandangNames.join(', ') : 'Tidak ada'}
); } else { return '-'; } }, }, { accessorKey: 'period', header: 'Periode', }, { accessorKey: 'created_at', header: 'Dibuat pada', cell: (props) => formatDate(props.row.original.created_at, 'MMM DD, YYYY'), }, { id: 'actions', cell: (props) => { const currentPageSize = props.table.getPaginationRowModel().rows.length; const currentPageRows = props.table.getPaginationRowModel().flatRows; const currentRowRelativeIndex = currentPageRows.findIndex((r) => r.id === props.row.id) + 1; const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; const detailClickHandler = (id: number) => { router.push( `/production/project-flock/detail/?projectFlockId=${id}` ); }; const editClickHandler = (id: number) => { router.push( `/production/project-flock/detail/edit/?projectFlockId=${id}` ); }; const deleteClickHandler = () => { // Set row selection setRowSelection({ [String(props.row.original.id)]: true, }); deleteModal.openModal(); }; return ( ); }, }, ], [] ); const exportToExcelHandler = async () => { setIsLoadingExportingToExcel(true); toast.error('Not implemented yet!'); setIsLoadingExportingToExcel(false); }; const bulkApproveClickHandler = () => { setApprovalAction('APPROVED'); confirmModal.openModal(); }; const bulkRejectClickHandler = () => { setApprovalAction('REJECTED'); confirmModal.openModal(); }; return ( <>
{/*
{ setSelectedArea(val as OptionType); updateFilter( 'areaFilter', (val as OptionType)?.value.toString() ); }} onInputChange={setAreaSelectInputValue} onMenuScrollToBottom={loadMoreArea} isClearable /> { setSelectedLocation(val as OptionType); updateFilter( 'locationFilter', (val as OptionType)?.value.toString() ); }} onInputChange={setLocationSelectInputValue} onMenuScrollToBottom={loadMoreLocation} isClearable /> { setSelectedKandang(val as OptionType); updateFilter( 'kandangFilter', (val as OptionType)?.value.toString() ); }} onInputChange={setKandangSelectInputValue} onMenuScrollToBottom={loadMoreKandang} isClearable /> { setPeriodInputValue(parseInt(e.target.value)); updateFilter('periodFilter', e.target.value); }} />
*/}
{selectedRowIds.length > 0 && canApprove && ( <>
)}
} className={{ wrapper: 'w-full min-w-24 max-w-3xs', inputWrapper: 'rounded-xl! shadow-button-soft', input: 'placeholder:font-semibold placeholder:text-base-content/50', }} />
Export
} >
{isLoading ? (
) : !isResponseSuccess(projectFlocks) || projectFlocks.data?.length === 0 ? (
} />
) : ( data={ isResponseSuccess(projectFlocks) ? projectFlocks?.data : [] } columns={columns} pageSize={tableFilterState.pageSize} page={ isResponseSuccess(projectFlocks) ? projectFlocks?.meta?.page : 0 } totalItems={ isResponseSuccess(projectFlocks) ? projectFlocks?.meta?.total_results : 0 } onPageChange={(page) => { setPage(page); }} onPageSizeChange={(pageSize) => { setPageSize(pageSize); }} isLoading={isLoading} sorting={sorting} setSorting={setSorting} rowSelection={rowSelection} setRowSelection={setRowSelection} enableRowSelection={(row) => { const projectFlock = row.original; return ( projectFlock.approval?.step_number === 1 && projectFlock.approval?.action !== 'REJECTED' ); }} withCheckbox className={{ containerClassName: cn('p-3 mb-0'), headerColumnClassName: 'text-nowrap', }} /> )}
{/* { deleteModal.openModal(); }, permissions: 'lti.production.project_flocks.delete', }, ]} approvals={[ { icon: 'material-symbols:check', label: 'Approve', action: 'APPROVED', onClick: () => { setApprovalAction('APPROVED'); confirmModal.openModal(); }, disabled: !canApprove, permissions: 'lti.production.project_flocks.approve', }, { icon: 'mdi:times', label: 'Reject', action: 'REJECTED', onClick: () => { setApprovalAction('REJECTED'); confirmModal.openModal(); }, permissions: 'lti.production.project_flocks.approve', }, ]} selectedRowIds={selectedRowIds} onClose={() => { setRowSelection({}); }} /> */} { confirmApprovalHandler(notes, approvalAction); }, isLoading: isApproveLoading, }} /> {/* Chickin Approval Modal */} { closeChickinApproveModal(); chickinApproveModal.closeModal(); }, }} primaryButton={{ text: 'Ya', color: 'success', onClick: async (notes) => { if (chickinApproveCallback) { setChickinApproveLoading(true); try { await chickinApproveCallback(notes); } finally { setChickinApproveLoading(false); closeChickinApproveModal(); chickinApproveModal.closeModal(); } } }, isLoading: isChickinApproveLoading, }} /> {/* Chickin Delete Modal */} { closeChickinDeleteModal(); }, }} className={{ modal: 'z-9999', }} primaryButton={{ text: 'Ya', color: 'error', isLoading: isChickinDeleteLoading, onClick: async () => { if (chickinDeleteCallback) { setChickinDeleteLoading(true); try { await chickinDeleteCallback(); } finally { setChickinDeleteLoading(false); closeChickinDeleteModal(); } } }, }} /> {/* Filter Modal */} {/* Modal Header */}

Filter Data

{ if (!Array.isArray(val)) { const areaValue = val?.value ? String(val.value) : null; formik.setFieldValue('area_id', areaValue); handleFilterAreaChange(val || null); } }} onInputChange={setAreaInputValue} isLoading={isLoadingAreaOptions} isClearable onMenuScrollToBottom={loadMoreAreas} className={{ wrapper: 'w-full' }} /> { if (!Array.isArray(val)) { const locationValue = val?.value ? String(val.value) : null; formik.setFieldValue('location_id', locationValue); handleFilterLocationChange(val || null); } }} onInputChange={setLocationInputValue} isLoading={isLoadingLocationOptions} isClearable onMenuScrollToBottom={loadMoreLocations} className={{ wrapper: 'w-full' }} /> { if (!Array.isArray(val)) { formik.setFieldValue( 'kandang_id', val?.value ? String(val.value) : null ); } }} onInputChange={setKandangInputValue} isLoading={isLoadingKandangOptions} isClearable onMenuScrollToBottom={loadMoreKandangs} className={{ wrapper: 'w-full' }} /> { if (!Array.isArray(val)) { formik.setFieldValue('category', val?.value || null); } }} className={{ wrapper: 'w-full' }} isClearable={true} /> { if (!Array.isArray(val)) { formik.setFieldValue('period', val?.value || null); } }} className={{ wrapper: 'w-full' }} isClearable />
{/* Modal Footer */}
{/* Project Flock Closing Modal */} { closeClosingModal(); closingModal.closeModal(); }, }} primaryButton={{ text: 'Ya', color: 'error', isLoading: isClosingLoading, onClick: async () => { if (closingCallback) { setClosingLoading(true); try { await closingCallback(!isKandangClosed ? 'close' : 'unclose'); } finally { setClosingLoading(false); closeClosingModal(); closingModal.closeModal(); refreshProjectFlocks(); } } }, }} /> ); }; export default ProjectFlockTable;