mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
1430 lines
46 KiB
TypeScript
1430 lines
46 KiB
TypeScript
'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<ProjectFlock, unknown>;
|
|
popoverPosition: 'bottom' | 'top';
|
|
editClickHandler: (id: number) => void;
|
|
detailClickHandler: (id: number) => void;
|
|
deleteClickHandler: () => void;
|
|
}) => {
|
|
const showEditButton = props.row.original.approval?.step_number !== 2;
|
|
|
|
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 (
|
|
<div className='relative'>
|
|
<PopoverButton
|
|
tabIndex={0}
|
|
variant='ghost'
|
|
color='none'
|
|
popoverTarget={popoverId}
|
|
anchorName={popoverAnchorName}
|
|
>
|
|
<Icon icon='material-symbols:more-vert' width={16} height={16} />
|
|
</PopoverButton>
|
|
|
|
<PopoverContent
|
|
id={popoverId}
|
|
anchorName={popoverAnchorName}
|
|
position={popoverPosition === 'bottom' ? 'bottom-start' : 'left'}
|
|
className='w-full max-w-40 rounded-xl border border-base-content/5 shadow-sm'
|
|
>
|
|
<div className='flex flex-col bg-base-100 rounded-xl'>
|
|
<RequirePermission permissions='lti.production.project_flocks.detail'>
|
|
<Button
|
|
// href={`/production/project-flock/detail/?projectFlockId=${props.row.original.id}`}
|
|
variant='ghost'
|
|
color='none'
|
|
onClick={detailClickHandlerWrapper}
|
|
className='p-3 justify-start text-sm font-semibold w-full'
|
|
>
|
|
<Icon icon='heroicons:eye' width={20} height={20} />
|
|
View Details
|
|
</Button>
|
|
</RequirePermission>
|
|
|
|
{showEditButton && (
|
|
<RequirePermission permissions='lti.production.project_flocks.update'>
|
|
<Button
|
|
// href={`/production/project-flock/detail/edit/?projectFlockId=${props.row.original.id}`}
|
|
variant='ghost'
|
|
color='none'
|
|
onClick={editClickHandlerWrapper}
|
|
className='p-3 justify-start text-sm font-semibold w-full'
|
|
>
|
|
<Icon icon='heroicons:pencil-square' width={20} height={20} />
|
|
Edit
|
|
</Button>
|
|
</RequirePermission>
|
|
)}
|
|
|
|
{showDeleteButton && (
|
|
<RequirePermission permissions='lti.production.project_flocks.delete'>
|
|
<hr className='mx-3 border-base-content/10 h-px' />
|
|
<Button
|
|
onClick={deleteClickHandler}
|
|
variant='ghost'
|
|
color='error'
|
|
className='p-3 justify-start text-sm font-semibold w-full'
|
|
>
|
|
<Icon icon='heroicons:trash' width={20} height={20} />
|
|
Delete
|
|
</Button>
|
|
</RequirePermission>
|
|
)}
|
|
</div>
|
|
</PopoverContent>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
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<Record<string, boolean>>({});
|
|
const selectedRowIds = Object.keys(rowSelection)
|
|
.filter((id) => rowSelection[id])
|
|
.map((id) => parseInt(id));
|
|
|
|
const [sorting, setSorting] = useState<SortingState>([]);
|
|
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<string | undefined>(
|
|
undefined
|
|
);
|
|
const [filterLocationId, setFilterLocationId] = useState<string | undefined>(
|
|
undefined
|
|
);
|
|
|
|
// ===== FORMIK SETUP FOR FILTER =====
|
|
const formik = useFormik<ProjectFlockFilterType>({
|
|
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<HTMLInputElement> = (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<ColumnDef<ProjectFlock>[]>(
|
|
() => [
|
|
{
|
|
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 (
|
|
<div className='w-full flex flex-row justify-center'>
|
|
<CheckboxInput
|
|
name='allRow'
|
|
checked={allSelected}
|
|
indeterminate={someSelected}
|
|
onChange={toggleSelectableRows}
|
|
disabled={hasNoSelectableRows}
|
|
/>
|
|
</div>
|
|
);
|
|
},
|
|
cell: ({ row }) => {
|
|
return (
|
|
<CheckboxInput
|
|
name='row'
|
|
checked={row.getIsSelected()}
|
|
disabled={!row.getCanSelect()}
|
|
indeterminate={row.getIsSomeSelected()}
|
|
onChange={row.getToggleSelectedHandler()}
|
|
/>
|
|
);
|
|
},
|
|
},
|
|
|
|
{
|
|
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 (
|
|
<StatusBadge color={badgeColor} text={latestApprovalStepName} />
|
|
);
|
|
},
|
|
},
|
|
{
|
|
header: 'Kandang',
|
|
cell: (props) => {
|
|
const kandang = props.row.original.kandangs;
|
|
if (kandang) {
|
|
const kandangNames = kandang.map((k: Kandang) => k.name);
|
|
return (
|
|
<div>
|
|
{kandangNames.length > 0
|
|
? kandangNames.join(', ')
|
|
: 'Tidak ada'}
|
|
</div>
|
|
);
|
|
} 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 (
|
|
<RowOptionsMenu
|
|
props={props}
|
|
detailClickHandler={detailClickHandler}
|
|
editClickHandler={editClickHandler}
|
|
deleteClickHandler={deleteClickHandler}
|
|
popoverPosition={isLast2Rows ? 'top' : 'bottom'}
|
|
/>
|
|
);
|
|
},
|
|
},
|
|
],
|
|
[]
|
|
);
|
|
|
|
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 (
|
|
<>
|
|
<div className='@container min-h-screen w-full'>
|
|
<div className='flex flex-col mb-4'>
|
|
{/* <div className='w-full flex flex-col justify-between items-end gap-2'>
|
|
<div className='flex flex-col sm:flex-row gap-3 w-full'>
|
|
<RequirePermission permissions='lti.production.project_flocks.create'>
|
|
<Button
|
|
color='primary'
|
|
onClick={() => {
|
|
setRowSelection({});
|
|
router.push('/production/project-flock/add');
|
|
}}
|
|
className='px-3 py-2.5 w-fit text-sm text-base-100 rounded-lg shadow-sm'
|
|
>
|
|
<Icon icon='heroicons:plus' width={20} height={20} />
|
|
Add Flock
|
|
</Button>
|
|
</RequirePermission>
|
|
<div className='ms-auto w-full sm:w-auto'>
|
|
<DebouncedTextInput
|
|
name='search'
|
|
placeholder='Cari Project Flock'
|
|
value={tableFilterState.search}
|
|
onChange={searchChangeHandler}
|
|
className={{
|
|
wrapper: 'w-full sm:max-w-3xs',
|
|
input: 'w-full',
|
|
inputWrapper: 'w-full',
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className='hidden sm:flex flex-row justify-end gap-6 w-full'>
|
|
<SelectInput
|
|
label='Area'
|
|
options={optionsArea}
|
|
isLoading={isLoadingArea}
|
|
value={selectedArea}
|
|
onChange={(val) => {
|
|
setSelectedArea(val as OptionType);
|
|
updateFilter(
|
|
'areaFilter',
|
|
(val as OptionType)?.value.toString()
|
|
);
|
|
}}
|
|
onInputChange={setAreaSelectInputValue}
|
|
onMenuScrollToBottom={loadMoreArea}
|
|
isClearable
|
|
/>
|
|
<SelectInput
|
|
label='Lokasi'
|
|
options={optionsLocation}
|
|
isLoading={isLoadingLocation}
|
|
value={selectedLocation}
|
|
onChange={(val) => {
|
|
setSelectedLocation(val as OptionType);
|
|
updateFilter(
|
|
'locationFilter',
|
|
(val as OptionType)?.value.toString()
|
|
);
|
|
}}
|
|
onInputChange={setLocationSelectInputValue}
|
|
onMenuScrollToBottom={loadMoreLocation}
|
|
isClearable
|
|
/>
|
|
<SelectInput
|
|
label='Kandang'
|
|
options={optionsKandang}
|
|
isLoading={isLoadingKandang}
|
|
value={selectedKandang}
|
|
onChange={(val) => {
|
|
setSelectedKandang(val as OptionType);
|
|
updateFilter(
|
|
'kandangFilter',
|
|
(val as OptionType)?.value.toString()
|
|
);
|
|
}}
|
|
onInputChange={setKandangSelectInputValue}
|
|
onMenuScrollToBottom={loadMoreKandang}
|
|
isClearable
|
|
/>
|
|
<DebouncedTextInput
|
|
name='period'
|
|
type='number'
|
|
label='Periode'
|
|
placeholder='Masukan periode'
|
|
value={periodInputValue?.toString() ?? ''}
|
|
onChange={(e) => {
|
|
setPeriodInputValue(parseInt(e.target.value));
|
|
updateFilter('periodFilter', e.target.value);
|
|
}}
|
|
/>
|
|
</div>
|
|
</div> */}
|
|
|
|
<div className='w-full p-3 flex flex-row justify-between gap-3 flex-wrap border-b border-base-content/10'>
|
|
<div className='w-fit flex flex-row gap-3 flex-wrap'>
|
|
<RequirePermission permissions='lti.production.project_flocks.create'>
|
|
<Button
|
|
color='primary'
|
|
onClick={() => {
|
|
setRowSelection({});
|
|
router.push('/production/project-flock/add');
|
|
}}
|
|
className='px-3 py-2.5 w-fit text-sm text-base-100 rounded-lg shadow-sm'
|
|
>
|
|
<Icon icon='heroicons:plus' width={20} height={20} />
|
|
Add Flock
|
|
</Button>
|
|
</RequirePermission>
|
|
|
|
{selectedRowIds.length > 0 && canApprove && (
|
|
<>
|
|
<hr className='w-px h-full border-none bg-base-content/10 hidden @sm:block' />
|
|
|
|
<RequirePermission permissions='lti.production.transfer_to_laying.approve'>
|
|
<Button
|
|
variant='outline'
|
|
color='none'
|
|
onClick={bulkRejectClickHandler}
|
|
disabled={selectedRowIds.length === 0}
|
|
className='px-3 py-2.5 gap-1.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft'
|
|
>
|
|
<Icon
|
|
icon='heroicons:x-mark'
|
|
width={20}
|
|
height={20}
|
|
className='text-error'
|
|
/>
|
|
Reject
|
|
</Button>
|
|
</RequirePermission>
|
|
|
|
<RequirePermission permissions='lti.production.transfer_to_laying.approve'>
|
|
<Button
|
|
variant='outline'
|
|
color='none'
|
|
onClick={bulkApproveClickHandler}
|
|
disabled={selectedRowIds.length === 0}
|
|
className='px-3 py-2.5 gap-1.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft'
|
|
>
|
|
<Icon
|
|
icon='heroicons:check'
|
|
width={20}
|
|
height={20}
|
|
className='text-success'
|
|
/>
|
|
Approve
|
|
</Button>
|
|
</RequirePermission>
|
|
</>
|
|
)}
|
|
</div>
|
|
|
|
<div className='flex flex-1 flex-row justify-start sm:justify-end items-center gap-3 flex-wrap'>
|
|
<DebouncedTextInput
|
|
name='search'
|
|
placeholder='Search'
|
|
value={tableFilterState.search ?? ''}
|
|
onChange={searchChangeHandler}
|
|
startAdornment={
|
|
<Icon
|
|
icon='heroicons:magnifying-glass'
|
|
width={20}
|
|
height={20}
|
|
/>
|
|
}
|
|
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',
|
|
}}
|
|
/>
|
|
|
|
<ButtonFilter
|
|
values={tableFilterState}
|
|
excludeFields={['page', 'pageSize', 'search']}
|
|
onClick={handleFilterModalOpen}
|
|
className='px-3 py-2.5'
|
|
/>
|
|
|
|
<Dropdown
|
|
align='end'
|
|
direction='bottom'
|
|
className={{
|
|
content:
|
|
'mt-1 rounded-xl border border-base-content/5 shadow-sm overflow-hidden',
|
|
}}
|
|
trigger={
|
|
<Button
|
|
variant='outline'
|
|
color='none'
|
|
className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft'
|
|
>
|
|
<div className='flex flex-row items-center gap-1.5'>
|
|
<Icon
|
|
icon='heroicons:cloud-arrow-down'
|
|
width={20}
|
|
height={20}
|
|
/>
|
|
|
|
<span>Export</span>
|
|
|
|
<div className='w-px self-stretch bg-base-content/10' />
|
|
|
|
<Icon
|
|
icon='heroicons:chevron-down'
|
|
width={14}
|
|
height={14}
|
|
/>
|
|
</div>
|
|
</Button>
|
|
}
|
|
>
|
|
<Button
|
|
variant='ghost'
|
|
color='none'
|
|
onClick={exportToExcelHandler}
|
|
isLoading={isLoadingExportingToExcel}
|
|
className='w-full p-3 justify-start text-sm text-base-content/50 font-semibold text-nowrap'
|
|
>
|
|
<Icon icon='heroicons:table-cells' width={20} height={20} />
|
|
Export to Excel
|
|
</Button>
|
|
</Dropdown>
|
|
</div>
|
|
</div>
|
|
|
|
<div className='flex flex-col mb-4'>
|
|
{isLoading ? (
|
|
<div className='w-full flex flex-row justify-center items-center p-4'>
|
|
<span className='loading loading-spinner loading-xl' />
|
|
</div>
|
|
) : !isResponseSuccess(projectFlocks) ||
|
|
projectFlocks.data?.length === 0 ? (
|
|
<div className='p-3'>
|
|
<ProjectFlockTableSkeleton
|
|
columns={columns}
|
|
icon={
|
|
<Icon
|
|
icon='heroicons:document-text'
|
|
className='text-white'
|
|
width={20}
|
|
height={20}
|
|
/>
|
|
}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<Table<ProjectFlock>
|
|
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',
|
|
}}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* <FloatingActionsButton
|
|
actions={[
|
|
{
|
|
action: 'DETAIL',
|
|
icon: 'mdi:eye-outline',
|
|
label: 'Lihat Detail',
|
|
hidden: selectedRowIds.length !== 1,
|
|
onClick() {
|
|
router.push(
|
|
`/production/project-flock/detail?projectFlockId=${selectedRowIds[0]}`
|
|
);
|
|
setRowSelection({});
|
|
},
|
|
permissions: 'lti.production.project_flocks.detail',
|
|
},
|
|
{
|
|
action: 'DELETE',
|
|
icon: 'material-symbols:delete-outline-rounded',
|
|
label: `Hapus data`,
|
|
hidden: selectedRowIds.length !== 1,
|
|
onClick: () => {
|
|
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({});
|
|
}}
|
|
/> */}
|
|
|
|
<ConfirmationModal
|
|
ref={deleteModal.ref}
|
|
type='error'
|
|
text={`Apakah anda yakin ingin menghapus data Project Flock ini (${selectedRowIds?.length} data)?`}
|
|
secondaryButton={{
|
|
text: 'Tidak',
|
|
}}
|
|
primaryButton={{
|
|
text: 'Ya',
|
|
color: 'error',
|
|
isLoading: isDeleteLoading,
|
|
onClick: confirmationModalDeleteClickHandler,
|
|
}}
|
|
/>
|
|
|
|
<ConfirmationModalWithNotes
|
|
ref={confirmModal.ref}
|
|
type={approvalAction == 'APPROVED' ? 'success' : 'error'}
|
|
text={`Apakah anda yakin ingin ${approvalAction == 'APPROVED' ? 'approve' : 'reject'} data Project Flock ini (${selectedRowIds?.length} data)?`}
|
|
secondaryButton={{
|
|
text: 'Tidak',
|
|
}}
|
|
primaryButton={{
|
|
text: 'Ya',
|
|
color: approvalAction == 'APPROVED' ? 'success' : 'error',
|
|
onClick: (notes) => {
|
|
confirmApprovalHandler(notes, approvalAction);
|
|
},
|
|
isLoading: isApproveLoading,
|
|
}}
|
|
/>
|
|
|
|
<ProjectFlockConfirmationModal
|
|
ref={successModal.ref}
|
|
type='success'
|
|
text='Data Berhasil Ditambahkan'
|
|
subtitleText='Data project flock telah berhasil disimpan.'
|
|
projectFlockForm={projectFlockFormValues}
|
|
onClose={handleSuccessModalClose}
|
|
secondaryButton={undefined}
|
|
/>
|
|
|
|
{/* Chickin Approval Modal */}
|
|
<ConfirmationModalWithNotes
|
|
ref={chickinApproveModal.ref}
|
|
type='success'
|
|
text={`Apakah anda yakin ingin approve data Chickin yang Pending?`}
|
|
className={{
|
|
modal: 'z-9999',
|
|
}}
|
|
secondaryButton={{
|
|
text: 'Tidak',
|
|
onClick: () => {
|
|
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 */}
|
|
<ConfirmationModal
|
|
ref={chickinDeleteModal.ref}
|
|
type='error'
|
|
text='Apakah anda yakin ingin menghapus data chick in ini?'
|
|
secondaryButton={{
|
|
text: 'Tidak',
|
|
onClick: () => {
|
|
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
|
|
ref={filterModal.ref}
|
|
className={{
|
|
modal: 'p-0',
|
|
modalBox: 'p-0 rounded-[0.875rem] xl:max-w-4/12 max-w-sm',
|
|
}}
|
|
>
|
|
{/* Modal Header */}
|
|
<div className='flex items-center justify-between gap-2 border-b border-base-content/10 p-4'>
|
|
<div className='flex items-center gap-2 text-primary'>
|
|
<Icon icon='heroicons:funnel' width={20} height={20} />
|
|
<h3 className='font-medium text-sm'>Filter Data</h3>
|
|
</div>
|
|
<Button
|
|
variant='link'
|
|
onClick={filterModal.closeModal}
|
|
className='text-base-content/50 hover:text-base-content transition-colors cursor-pointer'
|
|
>
|
|
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
|
</Button>
|
|
</div>
|
|
<form onSubmit={formik.handleSubmit} onReset={formik.handleReset}>
|
|
<div className='p-4 flex flex-col gap-1.5'>
|
|
<SelectInput
|
|
label='Area'
|
|
placeholder='Pilih Area'
|
|
options={areaOptions}
|
|
value={areaValue}
|
|
onChange={(val) => {
|
|
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' }}
|
|
/>
|
|
|
|
<SelectInput
|
|
label='Lokasi'
|
|
placeholder='Pilih Lokasi'
|
|
options={locationOptions}
|
|
value={locationValue}
|
|
onChange={(val) => {
|
|
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' }}
|
|
/>
|
|
|
|
<SelectInput
|
|
label='Kandang'
|
|
placeholder='Pilih Kandang'
|
|
options={kandangOptions}
|
|
value={kandangValue}
|
|
onChange={(val) => {
|
|
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' }}
|
|
/>
|
|
|
|
<SelectInputRadio
|
|
label='Kategori'
|
|
placeholder='Pilih Kategori'
|
|
options={categoryOptions}
|
|
value={categoryValue}
|
|
onChange={(val) => {
|
|
if (!Array.isArray(val)) {
|
|
formik.setFieldValue('category', val?.value || null);
|
|
}
|
|
}}
|
|
className={{ wrapper: 'w-full' }}
|
|
isClearable={true}
|
|
/>
|
|
|
|
<SelectInputRadio
|
|
label='Periode'
|
|
placeholder='Pilih Periode'
|
|
options={periodOptions}
|
|
value={periodValue}
|
|
onChange={(val) => {
|
|
if (!Array.isArray(val)) {
|
|
formik.setFieldValue('period', val?.value || null);
|
|
}
|
|
}}
|
|
className={{ wrapper: 'w-full' }}
|
|
isClearable
|
|
/>
|
|
</div>
|
|
|
|
{/* Modal Footer */}
|
|
<div className='flex justify-between items-center gap-4 p-4 border-t border-base-content/10 bg-gray-50'>
|
|
<Button
|
|
type='reset'
|
|
variant='soft'
|
|
className='rounded-lg text-base-content/65 bg-transparent border-none hover:bg-base-content/10 hover:text-base-content/65 transition-colors px-3 py-2'
|
|
>
|
|
Reset Filter
|
|
</Button>
|
|
<Button
|
|
type='submit'
|
|
className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold'
|
|
disabled={!formik.isValid || formik.isSubmitting}
|
|
>
|
|
Apply Filter
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</Modal>
|
|
|
|
{/* Project Flock Closing Modal */}
|
|
<ConfirmationModal
|
|
ref={closingModal.ref}
|
|
type='error'
|
|
text={
|
|
!isKandangClosed
|
|
? 'Apakah kamu yakin ingin mengakhiri project ini ? *Pastikan persediaan produk di gudang terkait sudah kosong, dan BOP sudah selesai'
|
|
: 'Apakah kamu yakin ingin membuka kembali project ini ? *Project ini akan kembali ke status aktif'
|
|
}
|
|
className={{
|
|
modal: 'z-9999',
|
|
}}
|
|
secondaryButton={{
|
|
text: 'Tidak',
|
|
onClick: () => {
|
|
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;
|