refactor(FE-84-87) refactor checkbox using reuseable component checkboxinput

This commit is contained in:
randy-ar
2025-10-25 13:58:46 +07:00
parent 4f3dfb4221
commit f0f6ec53cb
10 changed files with 476 additions and 275 deletions
@@ -14,7 +14,7 @@ import { Icon } from '@iconify/react';
import { useFormik } from 'formik';
import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import useSWR from 'swr';
import useSWR, { KeyedMutator } from 'swr';
import {
ProjectFlockFormSchema,
ProjectFlockFormValues,
@@ -35,15 +35,20 @@ import { BaseApiResponse } from '@/types/api/api-general';
import { FLOCK_CATEGORY_OPTIONS } from '@/config/constant';
import { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import ProjectFlockKandangTable from './ProjectFlockKandangTable';
interface ProjectFlockFormProps {
formType?: 'add' | 'edit' | 'detail';
initialValues?: ProjectFlock;
refreshProjectFlocks?: KeyedMutator<
BaseApiResponse<ProjectFlock> | undefined
>;
}
const ProjectFlockForm = ({
formType = 'add',
initialValues,
refreshProjectFlocks,
}: ProjectFlockFormProps) => {
// State
const router = useRouter();
@@ -71,18 +76,34 @@ const ProjectFlockForm = ({
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [isApproveLoading, setIsApproveLoading] = useState(false);
const [isApprovedDisabled, setIsApprovedDisabled] = useState(initialValues?.approval.step_name == 'Pengajuan' ? false : true);
const [isRejectedDisabled, setIsRejectedDisabled] = useState(!isApprovedDisabled);
const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(!isApprovedDisabled ? 'APPROVED' : 'REJECTED');
const [isApprovedDisabled, setIsApprovedDisabled] = useState(
initialValues?.approval.step_name == 'Pengajuan' ? false : true
);
const [isRejectedDisabled, setIsRejectedDisabled] = useState(
!isApprovedDisabled
);
const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
!isApprovedDisabled ? 'APPROVED' : 'REJECTED'
);
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>(
() =>
Object.fromEntries(
(initialValues?.kandangs ?? []).map((k: Kandang) => [
k.id.toString(),
true,
])
)
);
useEffect(() => {
if (initialValues?.approval?.step_name) {
const approvedDisabled = initialValues.approval.step_name !== 'Pengajuan';
setIsApprovedDisabled(approvedDisabled);
setIsRejectedDisabled(!approvedDisabled);
setApprovalAction(!approvedDisabled ? 'APPROVED' : 'REJECTED');
}
}, [initialValues]);
if (initialValues?.approval?.step_name) {
const approvedDisabled = initialValues.approval.step_name !== 'Pengajuan';
setIsApprovedDisabled(approvedDisabled);
setIsRejectedDisabled(!approvedDisabled);
setApprovalAction(!approvedDisabled ? 'APPROVED' : 'REJECTED');
}
}, [initialValues]);
// Fetch Data
const flockUrl = `${FlockApi.basePath}?${new URLSearchParams({
@@ -122,7 +143,7 @@ const ProjectFlockForm = ({
search: '',
location_id: selectedLocation == '' ? '0' : selectedLocation,
}).toString()}`;
const { data: kandang, isLoading: isLoadingKandang } = useSWR(
const { data: kandang, isLoading: isLoadingKandang, mutate: refreshKandang} = useSWR(
kandangUrl,
KandangApi.getAllFetcher
);
@@ -180,6 +201,20 @@ const ProjectFlockForm = ({
}
}
}, [kandang]);
useEffect(() => {
if (initialValues?.kandangs) {
refreshKandang();
setOpenSelectKandangs(true);
const newRowSelection = Object.fromEntries(
initialValues.kandangs.map((k: Kandang) => [
k.id.toString(),
true,
])
);
setRowSelection(newRowSelection);
}
}, [initialValues, refreshKandang]);
// Options Handler
const areaChangeHandler = (val: OptionType | OptionType[] | null) => {
@@ -224,38 +259,6 @@ const ProjectFlockForm = ({
formik.setFieldTouched('category', true);
};
const kandangChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value, checked } = event.target;
if (checked) {
formik.setFieldValue(
'kandang_ids',
formik.values.kandang_ids.concat(parseInt(value))
);
} else {
formik.setFieldValue(
'kandang_ids',
formik.values.kandang_ids.filter((id) => id !== parseInt(value))
);
}
};
const kandangCheckAll = (event: React.ChangeEvent<HTMLInputElement>) => {
const { checked } = event.target;
if (checked) {
formik.setFieldValue(
'kandang_ids',
optionsKandang
.filter(
(kandang) =>
kandang.status === 'NON_ACTIVE' ||
formik.values.kandang_ids.includes(kandang.id)
)
.map((kandang) => kandang.id)
);
} else {
formik.setFieldValue('kandang_ids', []);
}
};
// Submit Handler
const createProjectFlockHandler = async (
payload: CreateProjectFlockPayload
@@ -418,6 +421,16 @@ const ProjectFlockForm = ({
}
}, [periodFlocks]);
useEffect(() => {
const selectedRowIds = Object.keys(rowSelection)
.filter((id) => rowSelection[id])
.map((id) => parseInt(id));
formikSetValues({
...formik.values,
kandang_ids: selectedRowIds,
});
}, [rowSelection, formikSetValues]);
// Actions handler
const confirmationModalDeleteClickHandler = async () => {
setIsDeleteLoading(true);
@@ -454,14 +467,17 @@ const ProjectFlockForm = ({
});
if (isResponseSuccess(approveProjectFlockRes)) {
if(action == 'APPROVED'){
setIsApprovedDisabled(true);
setIsRejectedDisabled(false);
}
if(action == 'REJECTED'){
setIsRejectedDisabled(true);
setIsApprovedDisabled(false);
if (refreshProjectFlocks) {
await refreshProjectFlocks();
}
// if (action == 'APPROVED') {
// setIsApprovedDisabled(true);
// setIsRejectedDisabled(false);
// }
// if (action == 'REJECTED') {
// setIsRejectedDisabled(true);
// setIsApprovedDisabled(false);
// }
toast.success(approveProjectFlockRes.message as string);
}
if (isResponseError(approveProjectFlockRes)) {
@@ -520,10 +536,7 @@ const ProjectFlockForm = ({
confirmModal.openModal();
}
}}
disabled={
!initialValues?.id ||
isApprovedDisabled
}
disabled={!initialValues?.id || isApprovedDisabled}
className='w-full sm:w-fit'
>
<Icon icon='material-symbols:check' width={24} height={24} />
@@ -538,10 +551,7 @@ const ProjectFlockForm = ({
confirmModal.openModal();
}
}}
disabled={
!initialValues?.id ||
isRejectedDisabled
}
disabled={!initialValues?.id || isRejectedDisabled}
className='w-full sm:w-fit'
>
<Icon icon='mdi:times' width={24} height={24} />
@@ -682,100 +692,13 @@ const ProjectFlockForm = ({
{isLoadingKandang && (
<span className='loading loading-dots loading-xl'></span>
)}
<table className='table'>
{/* head */}
<thead>
<tr>
<th>
<label>
<input
type='checkbox'
checked={
optionsKandang
.filter(
(k) =>
k.status === 'NON_ACTIVE' ||
formik.values.kandang_ids.includes(k.id)
)
.every((k) =>
formik.values.kandang_ids.includes(k.id)
) &&
optionsKandang.filter(
(k) =>
k.status === 'NON_ACTIVE' ||
formik.values.kandang_ids.includes(k.id)
).length > 0
}
className='checkbox transition-none'
disabled={
formType === 'detail' ||
optionsKandang.filter(
(k) => k.status === 'NON_ACTIVE'
).length == 0
}
onChange={
formType === 'detail'
? () => {}
: kandangCheckAll
}
/>
</label>
</th>
<th>Kandang</th>
{/* <th>Status</th> */}
<th>Penanggung Jawab</th>
</tr>
</thead>
<tbody>
{/* rows */}
{selectedLocation != '' &&
optionsKandang.map((kandang) => (
<tr key={kandang.id}>
<th>
<label>
<input
value={kandang.id}
type='checkbox'
className='checkbox transition-none'
checked={formik.values.kandang_ids.includes(
kandang.id
)}
onChange={
formType === 'detail'
? () => {}
: kandangChangeHandler
}
disabled={
formType === 'detail' ||
kandang.status != 'NON_ACTIVE'
}
/>
</label>
</th>
<td>{kandang.name}</td>
{/* <td>{kandang.status}</td> */}
<td>{kandang.pic?.name}</td>
</tr>
))}
{selectedLocation == '' && (
<tr>
<td colSpan={3} className='text-center text-muted'>
Data tidak tersedia
</td>
</tr>
)}
</tbody>
{/* foot */}
{selectedLocation != '' && (
<tfoot>
<tr>
<th></th>
<th>Kandang</th>
<th>Penanggung Jawab</th>
</tr>
</tfoot>
)}
</table>
<ProjectFlockKandangTable
listKandang={optionsKandang}
rowSelection={rowSelection}
setRowSelection={setRowSelection}
selectedIds={formik.values.kandang_ids}
formType={formType}
/>
</div>
</Collapse>
</div>
@@ -845,7 +768,11 @@ const ProjectFlockForm = ({
<ConfirmationModal
ref={confirmModal.ref}
type={approvalAction == 'APPROVED' ? 'success' : 'error'}
text={`Apakah anda yakin ingin ${approvalAction == 'APPROVED' ? 'approve' : 'reject'} Project Flock berikut? (${initialValues?.flock?.name} - ${initialValues?.area?.name})?`}
text={`Apakah anda yakin ingin ${
approvalAction == 'APPROVED' ? 'approve' : 'reject'
} Project Flock berikut? (${initialValues?.flock?.name} - ${
initialValues?.area?.name
})?`}
secondaryButton={{
text: 'Tidak',
}}
@@ -0,0 +1,146 @@
'use client';
import CheckboxInput from '@/components/input/CheckboxInput';
import PillBadge from '@/components/PillBadge';
import Table from '@/components/Table';
import { cn } from '@/lib/helper';
import { Kandang } from '@/types/api/master-data/kandang';
import { OnChangeFn } from '@tanstack/react-table';
const ProjectFlockKandangTable = ({
listKandang,
rowSelection,
setRowSelection,
selectedIds,
formType = 'add',
}: {
listKandang: Kandang[];
rowSelection: Record<string, boolean>;
setRowSelection: OnChangeFn<Record<string, boolean>>;
selectedIds: (number | undefined)[];
formType: 'add' | 'edit' | 'detail';
}) => {
console.log('selectedIds');
console.log(selectedIds);
return (
<Table<Kandang>
data={listKandang}
columns={[
{
id: 'select',
header: ({ table }) => {
const allRows = table.getRowModel().rows;
const selectableRows = allRows.filter(
(row) =>
row.original.status == 'NON_ACTIVE' ||
row.original.status == 'PENGAJUAN'
);
const allSelected =
selectableRows.every((row) => row.getIsSelected()) &&
selectableRows.length != 0 && formType != 'detail';
const someSelected =
selectableRows.some((row) => row.getIsSelected()) && !allSelected && formType != 'detail';
const toggleSelectableRows = () => {
const shouldSelect = !allSelected;
selectableRows.forEach((row) => row.toggleSelected(shouldSelect));
};
return (
<div className='w-full flex flex-row justify-center'>
<CheckboxInput
name='allRow'
checked={allSelected}
indeterminate={someSelected}
onChange={toggleSelectableRows}
disabled={
listKandang.filter(
(kandang) =>
kandang.status == 'NON_ACTIVE' ||
kandang.status == 'PENGAJUAN'
).length == 0 || formType == 'detail'
}
/>
</div>
);
},
cell: ({ row }) => {
return (
<CheckboxInput
name='row'
checked={
(row.getIsSelected() &&
(row.original.status == 'NON_ACTIVE' ||
row.original.status == 'PENGAJUAN')) ||
selectedIds.includes(row.original.id)
}
disabled={
!row.getCanSelect() ||
(row.original.status != 'NON_ACTIVE' &&
row.original.status != 'PENGAJUAN') ||
formType == 'detail'
}
indeterminate={row.getIsSomeSelected()}
onChange={row.getToggleSelectedHandler()}
/>
);
},
},
{
accessorFn: (row) => row.name,
header: 'Kandang',
},
{
accessorFn: (row) => row.status,
header: 'Status',
cell: (props) => {
return (
<PillBadge
color={(() => {
switch (props.row.original.status) {
case 'ACTIVE':
return 'red';
case 'PENGAJUAN':
return 'green';
case 'NON_ACTIVE':
return 'blue';
default:
return 'gray';
}
})()}
content={props.row.original.status
.toLowerCase()
.replace(/_/g, ' ')
.replace(/\b\w/g, (char) => char.toUpperCase())}
/>
);
},
},
{
accessorFn: (row) => row.pic?.name,
header: 'Penanggung Jawab',
},
]}
className={{
containerClassName: cn({
'mb-20': listKandang?.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',
paginationClassName: 'hidden',
}}
rowSelection={rowSelection}
setRowSelection={setRowSelection}
/>
);
};
export default ProjectFlockKandangTable;