feat(FE-149): integrate TransferToLayingsTable to API

This commit is contained in:
ValdiANS
2025-11-12 13:31:35 +07:00
parent 3c0bd647a8
commit 8e3282bb7d
@@ -2,7 +2,12 @@
import { ChangeEventHandler, useState } from 'react'; import { ChangeEventHandler, useState } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table'; import {
CellContext,
ColumnDef,
Row,
SortingState,
} from '@tanstack/react-table';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
@@ -20,6 +25,7 @@ import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import TextInput from '@/components/input/TextInput'; import TextInput from '@/components/input/TextInput';
import CheckboxInput from '@/components/input/CheckboxInput'; import CheckboxInput from '@/components/input/CheckboxInput';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
import { TransferToLaying } from '@/types/api/production/transfer-to-laying'; import { TransferToLaying } from '@/types/api/production/transfer-to-laying';
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying'; import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
@@ -29,6 +35,7 @@ import { useTableFilter } from '@/services/hooks/useTableFilter';
import { ROWS_OPTIONS } from '@/config/constant'; import { ROWS_OPTIONS } from '@/config/constant';
import { Flock } from '@/types/api/master-data/flock'; import { Flock } from '@/types/api/master-data/flock';
import { FlockApi } from '@/services/api/master-data'; import { FlockApi } from '@/services/api/master-data';
import PillBadge from '@/components/PillBadge';
const RowOptionsMenu = ({ const RowOptionsMenu = ({
type = 'dropdown', type = 'dropdown',
@@ -43,6 +50,16 @@ const RowOptionsMenu = ({
rejectClickHandler: () => void; rejectClickHandler: () => void;
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
const showEditButton =
props.row.original.approval.action !== 'APPROVED' &&
props.row.original.approval.action !== 'REJECTED';
const showDeleteButton = showEditButton;
// TODO: apply RBAC
const showApproveButton = showEditButton;
const showRejectButton = showEditButton;
return ( return (
<RowOptionsMenuWrapper type={type}> <RowOptionsMenuWrapper type={type}>
<Button <Button
@@ -55,50 +72,57 @@ const RowOptionsMenu = ({
Detail Detail
</Button> </Button>
<Button {showEditButton && (
href={`/production/transfer-to-laying/detail/edit/?transferToLayingId=${props.row.original.id}`} <Button
variant='ghost' href={`/production/transfer-to-laying/detail/edit/?transferToLayingId=${props.row.original.id}`}
color='warning' variant='ghost'
className='justify-start text-sm' color='warning'
>
<Icon icon='material-symbols:edit-outline' width={16} height={16} />
Edit
</Button>
<Button
variant='ghost'
color='success'
onClick={approveClickHandler}
className='justify-start text-sm'
>
<Icon icon='material-symbols:check' width={24} height={24} />
Approve
</Button>
<Button
variant='ghost'
color='error'
onClick={rejectClickHandler}
className='justify-start text-sm'
>
<Icon icon='material-symbols:close' width={24} height={24} />
Reject
</Button>
<Button
onClick={deleteClickHandler}
variant='ghost'
color='error'
className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
>
<Icon
icon='material-symbols:delete-outline-rounded'
width={16}
height={16}
className='justify-start text-sm' className='justify-start text-sm'
/> >
Delete <Icon icon='material-symbols:edit-outline' width={16} height={16} />
</Button> Edit
</Button>
)}
{/* TODO: apply RBAC */}
{showApproveButton && (
<Button
variant='ghost'
color='success'
onClick={approveClickHandler}
className='justify-start text-sm'
>
<Icon icon='material-symbols:check' width={24} height={24} />
Approve
</Button>
)}
{showRejectButton && (
<Button
variant='ghost'
color='error'
onClick={rejectClickHandler}
className='justify-start text-sm'
>
<Icon icon='material-symbols:close' width={24} height={24} />
Reject
</Button>
)}
{showDeleteButton && (
<Button
onClick={deleteClickHandler}
variant='ghost'
color='error'
className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
>
<Icon
icon='material-symbols:delete-outline-rounded'
width={16}
height={16}
className='justify-start text-sm'
/>
Delete
</Button>
)}
</RowOptionsMenuWrapper> </RowOptionsMenuWrapper>
); );
}; };
@@ -187,17 +211,24 @@ const TransferToLayingsTable = () => {
/> />
</div> </div>
), ),
cell: ({ row }) => ( cell: ({ row }) => {
<div> const isCheckboxDisabled =
<CheckboxInput !row.getCanSelect() ||
name='row' row.original.approval.action === 'APPROVED' ||
checked={row.getIsSelected()} row.original.approval.action === 'REJECTED';
disabled={!row.getCanSelect()}
indeterminate={row.getIsSomeSelected()} return (
onChange={row.getToggleSelectedHandler()} <div>
/> <CheckboxInput
</div> name='row'
), checked={row.getIsSelected()}
disabled={isCheckboxDisabled}
indeterminate={row.getIsSomeSelected()}
onChange={row.getToggleSelectedHandler()}
/>
</div>
);
},
}, },
{ {
header: '#', header: '#',
@@ -214,21 +245,55 @@ const TransferToLayingsTable = () => {
{ {
accessorKey: 'flock_source', accessorKey: 'flock_source',
header: 'Flock Asal', header: 'Flock Asal',
cell: (props) => props.row.original.flock_source.name, cell: (props) => props.row.original.from_project_flock.flock_name,
}, },
{ {
accessorKey: 'flock_destination', accessorKey: 'flock_destination',
header: 'Flock Tujuan', header: 'Flock Tujuan',
cell: (props) => props.row.original.flock_destination.name, cell: (props) => props.row.original.to_project_flock.flock_name,
}, },
{ {
accessorKey: 'quantity', accessorKey: 'usage_qty',
header: 'Kuantitas', header: 'Kuantitas',
cell: (props) => props.getValue() ?? props.row.original.pending_usage_qty,
}, },
{ {
accessorKey: 'reason', accessorKey: 'notes',
header: 'Alasan Transfer', header: 'Alasan Transfer',
}, },
{
header: 'Status',
cell: (props) => {
const isLatestApprovalRejected =
props.row.original.approval.action === 'REJECTED';
let latestApprovalStepName = props.row.original.approval.step_name;
let pillBadgeColor: 'yellow' | 'green' | 'gray' | 'red' = 'gray';
switch (latestApprovalStepName.toLowerCase()) {
case 'pengajuan':
pillBadgeColor = 'yellow';
break;
case 'disetujui':
pillBadgeColor = 'green';
break;
}
if (isLatestApprovalRejected) {
pillBadgeColor = 'red';
latestApprovalStepName = 'Ditolak';
}
return (
<PillBadge
content={latestApprovalStepName}
color={pillBadgeColor}
className='text-sm'
/>
);
},
},
{ {
header: 'Aksi', header: 'Aksi',
cell: (props) => { cell: (props) => {
@@ -237,7 +302,7 @@ const TransferToLayingsTable = () => {
const currentRowRelativeIndex = const currentRowRelativeIndex =
currentPageRows.findIndex((r) => r.id === props.row.id) + 1; currentPageRows.findIndex((r) => r.id === props.row.id) + 1;
const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; const isLast2Rows = currentRowRelativeIndex > currentPageSize - 3;
const approveClickHandler = () => { const approveClickHandler = () => {
setSelectedTransferToLaying(props.row.original); setSelectedTransferToLaying(props.row.original);
@@ -268,7 +333,7 @@ const TransferToLayingsTable = () => {
return ( return (
<> <>
{currentPageSize > 2 && ( {currentPageSize > 3 && (
<RowDropdownOptions isLast2Rows={isLast2Rows}> <RowDropdownOptions isLast2Rows={isLast2Rows}>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='dropdown'
@@ -280,7 +345,7 @@ const TransferToLayingsTable = () => {
</RowDropdownOptions> </RowDropdownOptions>
)} )}
{currentPageSize <= 2 && ( {currentPageSize <= 3 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='collapse' type='collapse'
@@ -297,6 +362,15 @@ const TransferToLayingsTable = () => {
}, },
]; ];
const tableEnableRowSelectionHandler: (
row: Row<TransferToLaying>
) => boolean = (row) => {
return (
row.original.approval.action !== 'APPROVED' &&
row.original.approval.action !== 'REJECTED'
);
};
const bulkApproveClickHandler = () => { const bulkApproveClickHandler = () => {
approveModal.openModal(); approveModal.openModal();
}; };
@@ -309,27 +383,31 @@ const TransferToLayingsTable = () => {
const confirmationModalDeleteClickHandler = async () => { const confirmationModalDeleteClickHandler = async () => {
setIsDeleteLoading(true); setIsDeleteLoading(true);
await TransferToLayingApi.delete(selectedTransferToLaying?.id as number); try {
refreshTransferToLayings(); await TransferToLayingApi.delete(selectedTransferToLaying?.id as number);
deleteModal.closeModal(); toast.success('Berhasil menghapus data transfer ke laying!');
toast.success('Berhasil menghapus data transfer ke laying!'); refreshTransferToLayings();
setIsDeleteLoading(false); } catch (error) {
toast.success('Gagal menghapus data transfer ke laying!');
} finally {
deleteModal.closeModal();
setIsDeleteLoading(false);
}
}; };
const confirmationModalApproveClickHandler = async () => { const confirmationModalApproveClickHandler = async (notes: string) => {
setIsApproveLoading(true); setIsApproveLoading(true);
const bulkApproveResponse = const bulkApproveResponse = await TransferToLayingApi.bulkApprove(
await TransferToLayingApi.bulkApprove(selectedRowIds); selectedRowIds,
notes
);
if (isResponseSuccess(bulkApproveResponse)) { if (isResponseSuccess(bulkApproveResponse)) {
refreshTransferToLayings(); refreshTransferToLayings();
approveModal.closeModal(); approveModal.closeModal();
// TODO: remove console.log
console.log('Approved data:', selectedRowIds);
toast.success( toast.success(
`Berhasil approve ${selectedRowIds.length} data transfer ke laying!` `Berhasil approve ${selectedRowIds.length} data transfer ke laying!`
); );
@@ -346,19 +424,18 @@ const TransferToLayingsTable = () => {
setIsApproveLoading(false); setIsApproveLoading(false);
}; };
const confirmationModalRejectClickHandler = async () => { const confirmationModalRejectClickHandler = async (notes: string) => {
setIsRejectLoading(true); setIsRejectLoading(true);
const bulkRejectResponse = const bulkRejectResponse = await TransferToLayingApi.bulkReject(
await TransferToLayingApi.bulkReject(selectedRowIds); selectedRowIds,
notes
);
if (isResponseSuccess(bulkRejectResponse)) { if (isResponseSuccess(bulkRejectResponse)) {
refreshTransferToLayings(); refreshTransferToLayings();
rejectModal.closeModal(); rejectModal.closeModal();
// TODO: remove console.log
console.log('Rejected data:', selectedRowIds);
toast.success( toast.success(
`Berhasil reject ${selectedRowIds.length} data transfer ke laying!` `Berhasil reject ${selectedRowIds.length} data transfer ke laying!`
); );
@@ -559,6 +636,7 @@ const TransferToLayingsTable = () => {
setSorting={setSorting} setSorting={setSorting}
rowSelection={rowSelection} rowSelection={rowSelection}
setRowSelection={setRowSelection} setRowSelection={setRowSelection}
enableRowSelection={tableEnableRowSelectionHandler}
className={{ className={{
containerClassName: cn({ containerClassName: cn({
'mb-20': 'mb-20':
@@ -592,7 +670,7 @@ const TransferToLayingsTable = () => {
}} }}
/> />
<ConfirmationModal <ConfirmationModalWithNotes
ref={approveModal.ref} ref={approveModal.ref}
type='success' type='success'
text={`Apakah anda yakin ingin approve data transfer ke laying ini (${selectedRowIds.length} data)?`} text={`Apakah anda yakin ingin approve data transfer ke laying ini (${selectedRowIds.length} data)?`}
@@ -607,7 +685,7 @@ const TransferToLayingsTable = () => {
}} }}
/> />
<ConfirmationModal <ConfirmationModalWithNotes
ref={rejectModal.ref} ref={rejectModal.ref}
type='error' type='error'
text={`Apakah anda yakin ingin reject data transfer ke laying ini (${selectedRowIds.length} data)?`} text={`Apakah anda yakin ingin reject data transfer ke laying ini (${selectedRowIds.length} data)?`}