feat(FE-170,175): enhance RecordingTable with grading completion checks and approval logic

This commit is contained in:
rstubryan
2025-11-06 22:28:31 +07:00
parent c012668340
commit 62c16bb9d1
@@ -41,12 +41,14 @@ const RowOptionsMenu = ({
deleteClickHandler, deleteClickHandler,
approveClickHandler, approveClickHandler,
rejectClickHandler, rejectClickHandler,
isGradingCompleted,
}: { }: {
type: 'dropdown' | 'collapse'; type: 'dropdown' | 'collapse';
props: CellContext<Recording, unknown>; props: CellContext<Recording, unknown>;
deleteClickHandler: () => void; deleteClickHandler: () => void;
approveClickHandler: () => void; approveClickHandler: () => void;
rejectClickHandler: () => void; rejectClickHandler: () => void;
isGradingCompleted: (recording: Recording) => boolean;
}) => { }) => {
const isLayingCategory = const isLayingCategory =
props.row.original.project_flock_category === 'LAYING'; props.row.original.project_flock_category === 'LAYING';
@@ -60,6 +62,7 @@ const RowOptionsMenu = ({
}; };
const isApproved = isRecordingApproved(props.row.original); const isApproved = isRecordingApproved(props.row.original);
const isGradingDone = isGradingCompleted(props.row.original);
return ( return (
<RowOptionsMenuWrapper type={type}> <RowOptionsMenuWrapper type={type}>
@@ -81,7 +84,7 @@ const RowOptionsMenu = ({
<Icon icon='mdi:pencil-outline' width={16} height={16} /> <Icon icon='mdi:pencil-outline' width={16} height={16} />
Edit Edit
</Button> </Button>
{isLayingCategory && ( {isLayingCategory && !isGradingDone && (
<Button <Button
href={`recording/grading/add?recording_id=${props.row.original.id}`} href={`recording/grading/add?recording_id=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -98,6 +101,11 @@ const RowOptionsMenu = ({
variant='ghost' variant='ghost'
color='success' color='success'
className='justify-start text-sm' className='justify-start text-sm'
title={
isGradingDone
? 'Recording bisa disetujui karena sudah grading'
: 'Recording bisa disetujui'
}
> >
<Icon icon='material-symbols:check' width={16} height={16} /> <Icon icon='material-symbols:check' width={16} height={16} />
Approve Approve
@@ -109,6 +117,11 @@ const RowOptionsMenu = ({
variant='ghost' variant='ghost'
color='error' color='error'
className='justify-start text-sm' className='justify-start text-sm'
title={
isGradingDone
? 'Recording bisa ditolak karena sudah grading'
: 'Recording bisa ditolak'
}
> >
<Icon icon='material-symbols:close' width={16} height={16} /> <Icon icon='material-symbols:close' width={16} height={16} />
Reject Reject
@@ -431,11 +444,28 @@ const RecordingTable = () => {
isLoadingOptions: isLoadingKandang, isLoadingOptions: isLoadingKandang,
} = useSelect<Kandang>(KandangApi.basePath, 'id', 'name'); } = useSelect<Kandang>(KandangApi.basePath, 'id', 'name');
const isRecordingApproved = useCallback((recording: Recording) => { const isRecordingFullyApproved = useCallback(
return ( (recording: Recording): boolean => {
recording.approval?.action === 'APPROVED' && return (
recording.approval?.step_name === 'Disetujui' && recording.approval?.action === 'APPROVED' &&
recording.approval?.step_number === 3 recording.approval?.step_name === 'Disetujui' &&
recording.approval?.step_number === 3
);
},
[]
);
const isRecordingApproved = useCallback(
(recording: Recording) => {
return isRecordingFullyApproved(recording);
},
[isRecordingFullyApproved]
);
const isGradingCompleted = useCallback((recording: Recording): boolean => {
return !!(
recording.egg_grading_status === 'COMPLETED' ||
(recording.approval?.step_number && recording.approval.step_number >= 1)
); );
}, []); }, []);
@@ -472,7 +502,7 @@ const RecordingTable = () => {
if (eligibleRowIds.length === 0) { if (eligibleRowIds.length === 0) {
toast.error( toast.error(
'Tidak ada recording yang bisa disetujui (sudah disetujui sebelumnya)' 'Tidak ada recording yang bisa disetujui (sudah disetujui final)'
); );
setIsApproveLoading(false); setIsApproveLoading(false);
return; return;
@@ -505,7 +535,7 @@ const RecordingTable = () => {
if (eligibleRowIds.length === 0) { if (eligibleRowIds.length === 0) {
toast.error( toast.error(
'Tidak ada recording yang bisa ditolak (sudah disetujui sebelumnya)' 'Tidak ada recording yang bisa ditolak (sudah disetujui final)'
); );
setIsRejectLoading(false); setIsRejectLoading(false);
return; return;
@@ -531,7 +561,6 @@ const RecordingTable = () => {
setIsRejectLoading(false); setIsRejectLoading(false);
}; };
// Filter out already approved recordings for bulk actions
const eligibleRowIds = useMemo(() => { const eligibleRowIds = useMemo(() => {
if (!isResponseSuccess(recordings) || !recordings.data) return []; if (!isResponseSuccess(recordings) || !recordings.data) return [];
return selectedRowIds.filter((id) => { return selectedRowIds.filter((id) => {
@@ -564,8 +593,15 @@ const RecordingTable = () => {
setApprovalNotes(''); setApprovalNotes('');
approveModal.openModal(); approveModal.openModal();
}} }}
disabled={selectedRowIds.length === 0} disabled={
selectedRowIds.length === 0 || eligibleRowIds.length === 0
}
className='w-full sm:w-fit' className='w-full sm:w-fit'
title={
eligibleRowIds.length === 0
? 'Tidak ada Recording yang bisa disetujui (sudah disetujui final)'
: ''
}
> >
<Icon icon='material-symbols:check' width={24} height={24} /> <Icon icon='material-symbols:check' width={24} height={24} />
Approve Approve
@@ -578,8 +614,15 @@ const RecordingTable = () => {
setApprovalNotes(''); setApprovalNotes('');
rejectModal.openModal(); rejectModal.openModal();
}} }}
disabled={selectedRowIds.length === 0} disabled={
selectedRowIds.length === 0 || eligibleRowIds.length === 0
}
className='w-full sm:w-fit' className='w-full sm:w-fit'
title={
eligibleRowIds.length === 0
? 'Tidak ada Recording yang bisa ditolak (sudah disetujui final)'
: ''
}
> >
<Icon icon='material-symbols:close' width={24} height={24} /> <Icon icon='material-symbols:close' width={24} height={24} />
Reject Reject
@@ -692,27 +735,46 @@ const RecordingTable = () => {
columns={[ columns={[
{ {
id: 'select', id: 'select',
header: ({ table }) => ( header: ({ table }) => {
<div className='w-full flex flex-row justify-center'> const allRows = table.getRowModel().rows;
<CheckboxInput const selectableRows = allRows.filter((row) => {
name='allRow' const recording = row.original;
checked={table.getIsAllRowsSelected()} return !isRecordingApproved(recording);
indeterminate={table.getIsSomeRowsSelected()} });
onChange={table.getToggleAllRowsSelectedHandler()} const hasOnlyUnselectableRows = selectableRows.length === 0;
/>
</div> return (
), <div className='w-full flex flex-row justify-center'>
<CheckboxInput
name='allRow'
checked={table.getIsAllRowsSelected()}
indeterminate={table.getIsSomeRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
disabled={hasOnlyUnselectableRows}
title={
hasOnlyUnselectableRows
? 'Tidak ada Recording yang bisa dipilih (sudah disetujui final)'
: ''
}
/>
</div>
);
},
cell: ({ row }) => { cell: ({ row }) => {
const isApproved = isRecordingApproved(row.original); const isApproved = isRecordingApproved(row.original);
const isDisabled = !row.getCanSelect() || isApproved;
let tooltip = '';
if (isApproved) tooltip = 'Recording sudah disetujui final';
return ( return (
<div> <div>
<CheckboxInput <CheckboxInput
name='row' name='row'
checked={row.getIsSelected()} checked={row.getIsSelected()}
disabled={!row.getCanSelect() || isApproved} disabled={isDisabled}
indeterminate={row.getIsSomeSelected()} indeterminate={row.getIsSomeSelected()}
onChange={row.getToggleSelectedHandler()} onChange={row.getToggleSelectedHandler()}
title={isApproved ? 'Recording sudah disetujui' : ''} title={tooltip}
/> />
</div> </div>
); );
@@ -887,6 +949,7 @@ const RecordingTable = () => {
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
approveClickHandler={approveClickHandler} approveClickHandler={approveClickHandler}
rejectClickHandler={rejectClickHandler} rejectClickHandler={rejectClickHandler}
isGradingCompleted={isGradingCompleted}
/> />
</RowDropdownOptions> </RowDropdownOptions>
)} )}
@@ -899,6 +962,7 @@ const RecordingTable = () => {
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
approveClickHandler={approveClickHandler} approveClickHandler={approveClickHandler}
rejectClickHandler={rejectClickHandler} rejectClickHandler={rejectClickHandler}
isGradingCompleted={isGradingCompleted}
/> />
</RowCollapseOptions> </RowCollapseOptions>
)} )}