mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 05:22:02 +00:00
Merge branch 'development' into fix/project-flock-form
This commit is contained in:
@@ -305,6 +305,14 @@ const PurchaseTable = () => {
|
|||||||
? formatDate(props.row.original.po_date, 'DD MMM YYYY')
|
? formatDate(props.row.original.po_date, 'DD MMM YYYY')
|
||||||
: '-',
|
: '-',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'received_date',
|
||||||
|
header: 'Tgl. Terima',
|
||||||
|
cell: (props) =>
|
||||||
|
props.row.original.received_date
|
||||||
|
? formatDate(props.row.original.received_date, 'DD MMM YYYY')
|
||||||
|
: '-',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'due_date',
|
accessorKey: 'due_date',
|
||||||
header: 'Jatuh Tempo',
|
header: 'Jatuh Tempo',
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import PurchaseOrderAcceptApprovalForm from '@/components/pages/purchase/form/or
|
|||||||
import PurchaseOrderInvoice from '@/components/pages/purchase/order/PurchaseOrderInvoice';
|
import PurchaseOrderInvoice from '@/components/pages/purchase/order/PurchaseOrderInvoice';
|
||||||
|
|
||||||
import Card from '@/components/Card';
|
import Card from '@/components/Card';
|
||||||
|
import DateInput from '@/components/input/DateInput';
|
||||||
|
import TextArea from '@/components/input/TextArea';
|
||||||
import {
|
import {
|
||||||
CreateAcceptApprovalRequestPayload,
|
CreateAcceptApprovalRequestPayload,
|
||||||
CreateManagerApprovalRequestPayload,
|
CreateManagerApprovalRequestPayload,
|
||||||
@@ -96,6 +98,7 @@ const PurchaseOrderDetail = ({
|
|||||||
const acceptRejectionModal = useModal();
|
const acceptRejectionModal = useModal();
|
||||||
const managerRejectionModal = useModal();
|
const managerRejectionModal = useModal();
|
||||||
const editModal = useModal();
|
const editModal = useModal();
|
||||||
|
const editPoDateModal = useModal();
|
||||||
const penerimaanBarangModal = useModal();
|
const penerimaanBarangModal = useModal();
|
||||||
const deleteModal = useModal();
|
const deleteModal = useModal();
|
||||||
|
|
||||||
@@ -105,6 +108,9 @@ const PurchaseOrderDetail = ({
|
|||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
const [selectedItem, setSelectedItem] = useState<PurchaseItem | null>(null);
|
const [selectedItem, setSelectedItem] = useState<PurchaseItem | null>(null);
|
||||||
const [, setApprovalNotes] = useState('');
|
const [, setApprovalNotes] = useState('');
|
||||||
|
const [managerApprovalNotes, setManagerApprovalNotes] = useState('');
|
||||||
|
const [managerApprovalPoDate, setManagerApprovalPoDate] = useState('');
|
||||||
|
const [editPoDate, setEditPoDate] = useState('');
|
||||||
|
|
||||||
const selectedRowIds = Object.keys(rowSelection).map((item) =>
|
const selectedRowIds = Object.keys(rowSelection).map((item) =>
|
||||||
parseInt(item)
|
parseInt(item)
|
||||||
@@ -212,6 +218,8 @@ const PurchaseOrderDetail = ({
|
|||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
setApprovalNotes('');
|
setApprovalNotes('');
|
||||||
|
setManagerApprovalNotes('');
|
||||||
|
setManagerApprovalPoDate('');
|
||||||
confirmationModalWithNotes.openModal();
|
confirmationModalWithNotes.openModal();
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
@@ -414,17 +422,44 @@ const PurchaseOrderDetail = ({
|
|||||||
deleteModal,
|
deleteModal,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const updatePoDateHandler = useCallback(async () => {
|
||||||
|
const purchaseRequestId = searchParams.get('purchaseId')
|
||||||
|
? parseInt(searchParams.get('purchaseId')!)
|
||||||
|
: initialValues?.id || 1;
|
||||||
|
|
||||||
|
if (!purchaseRequestId) {
|
||||||
|
toast.error('Purchase Request ID is required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await PurchaseApi.updatePoDate(purchaseRequestId, {
|
||||||
|
po_date: editPoDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isResponseError(res)) {
|
||||||
|
toast.error(res.message || 'Gagal mengubah tanggal PO');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.success('Tanggal PO berhasil diubah');
|
||||||
|
setEditPoDate('');
|
||||||
|
editPoDateModal.closeModal();
|
||||||
|
refetchData?.();
|
||||||
|
}, [initialValues?.id, searchParams, editPoDate, editPoDateModal, refetchData]);
|
||||||
|
|
||||||
// ===== APPROVAL/REJECTION HANDLERS =====
|
// ===== APPROVAL/REJECTION HANDLERS =====
|
||||||
const managerApprovalHandler = async (notes: string) => {
|
const managerApprovalHandler = async () => {
|
||||||
const payload: CreateManagerApprovalRequestPayload = {
|
const payload: CreateManagerApprovalRequestPayload = {
|
||||||
action: 'APPROVED',
|
action: 'APPROVED',
|
||||||
notes: notes || null,
|
notes: managerApprovalNotes || null,
|
||||||
|
po_date: managerApprovalPoDate || null,
|
||||||
};
|
};
|
||||||
|
|
||||||
await createManagerApprovalHandler(payload);
|
await createManagerApprovalHandler(payload);
|
||||||
await refreshApprovals();
|
await refreshApprovals();
|
||||||
await refetchData?.();
|
await refetchData?.();
|
||||||
setApprovalNotes('');
|
setManagerApprovalNotes('');
|
||||||
|
setManagerApprovalPoDate('');
|
||||||
confirmationModalWithNotes.closeModal();
|
confirmationModalWithNotes.closeModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -829,6 +864,45 @@ const PurchaseOrderDetail = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{purchaseData.po_date &&
|
||||||
|
!purchaseData.po_date.startsWith('0001') && (
|
||||||
|
<div className='group'>
|
||||||
|
<div className='flex items-start'>
|
||||||
|
<span className='font-medium text-gray-600 min-w-[140px] shrink-0'>
|
||||||
|
Tanggal PO
|
||||||
|
</span>
|
||||||
|
<div className='ml-3 flex items-center gap-1'>
|
||||||
|
<span className='text-gray-900'>
|
||||||
|
:{' '}
|
||||||
|
{formatDate(purchaseData.po_date, 'DD MMM YYYY')}
|
||||||
|
</span>
|
||||||
|
<RequirePermission permissions='lti.purchase.update'>
|
||||||
|
<Button
|
||||||
|
type='button'
|
||||||
|
variant='ghost'
|
||||||
|
color='warning'
|
||||||
|
className='p-1 min-h-0 h-auto'
|
||||||
|
onClick={() => {
|
||||||
|
setEditPoDate(
|
||||||
|
formatDate(
|
||||||
|
purchaseData.po_date,
|
||||||
|
'YYYY-MM-DD'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
editPoDateModal.openModal();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon='material-symbols:edit-outline'
|
||||||
|
width={14}
|
||||||
|
height={14}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</RequirePermission>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1016,27 +1090,79 @@ const PurchaseOrderDetail = ({
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Confirmation Modal with Notes */}
|
{/* Manager Approval Modal */}
|
||||||
<ConfirmationModalWithNotes
|
<Modal
|
||||||
ref={confirmationModalWithNotes.ref}
|
ref={confirmationModalWithNotes.ref}
|
||||||
type='success'
|
|
||||||
text='Apakah Anda yakin ingin melanjutkan approval ini?'
|
|
||||||
placeholder='(Opsional) Tambahkan catatan untuk approval ini...'
|
|
||||||
rows={4}
|
|
||||||
closeOnBackdrop
|
closeOnBackdrop
|
||||||
primaryButton={{
|
className={{
|
||||||
text: 'Ya, Lanjutkan',
|
modalBox: 'max-w-lg rounded-lg p-0',
|
||||||
color: 'success',
|
|
||||||
onClick: managerApprovalHandler,
|
|
||||||
}}
|
}}
|
||||||
secondaryButton={{
|
>
|
||||||
text: 'Batal',
|
<div className='flex flex-col'>
|
||||||
onClick: () => {
|
<div className='flex items-center justify-between border-b border-base-content/10 p-4'>
|
||||||
setApprovalNotes('');
|
<h4 className='text-sm font-semibold text-base-content'>
|
||||||
confirmationModalWithNotes.closeModal();
|
Konfirmasi Approval Manager
|
||||||
},
|
</h4>
|
||||||
}}
|
<Button
|
||||||
/>
|
variant='ghost'
|
||||||
|
color='none'
|
||||||
|
onClick={() => {
|
||||||
|
setManagerApprovalNotes('');
|
||||||
|
setManagerApprovalPoDate('');
|
||||||
|
confirmationModalWithNotes.closeModal();
|
||||||
|
}}
|
||||||
|
className='p-1'
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:close' width={20} height={20} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex flex-col gap-4 p-4'>
|
||||||
|
<p className='text-sm text-base-content/70'>
|
||||||
|
Apakah Anda yakin ingin melanjutkan approval ini?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<DateInput
|
||||||
|
name='manager_approval_po_date'
|
||||||
|
label='Tanggal PO'
|
||||||
|
value={managerApprovalPoDate}
|
||||||
|
onChange={(e) => setManagerApprovalPoDate(e.target.value)}
|
||||||
|
isNestedModal
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextArea
|
||||||
|
name='manager_approval_notes'
|
||||||
|
label='Catatan (Opsional)'
|
||||||
|
placeholder='Tambahkan catatan untuk approval ini...'
|
||||||
|
value={managerApprovalNotes}
|
||||||
|
onChange={(e) => setManagerApprovalNotes(e.target.value)}
|
||||||
|
rows={4}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex justify-end gap-3 border-t border-base-content/10 p-4'>
|
||||||
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
color='none'
|
||||||
|
onClick={() => {
|
||||||
|
setManagerApprovalNotes('');
|
||||||
|
setManagerApprovalPoDate('');
|
||||||
|
confirmationModalWithNotes.closeModal();
|
||||||
|
}}
|
||||||
|
className='px-3 py-2.5'
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color='success'
|
||||||
|
onClick={managerApprovalHandler}
|
||||||
|
className='px-3 py-2.5'
|
||||||
|
>
|
||||||
|
Ya, Lanjutkan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
{/* Staff Approval Modal */}
|
{/* Staff Approval Modal */}
|
||||||
<Modal
|
<Modal
|
||||||
@@ -1112,6 +1238,66 @@ const PurchaseOrderDetail = ({
|
|||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
{/* Edit PO Date Modal */}
|
||||||
|
<Modal
|
||||||
|
ref={editPoDateModal.ref}
|
||||||
|
closeOnBackdrop
|
||||||
|
className={{
|
||||||
|
modalBox: 'max-w-sm rounded-lg p-0',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex flex-col'>
|
||||||
|
<div className='flex items-center justify-between border-b border-base-content/10 p-4'>
|
||||||
|
<h4 className='text-sm font-semibold text-base-content'>
|
||||||
|
Edit Tanggal PO
|
||||||
|
</h4>
|
||||||
|
<Button
|
||||||
|
variant='ghost'
|
||||||
|
color='none'
|
||||||
|
onClick={() => {
|
||||||
|
setEditPoDate('');
|
||||||
|
editPoDateModal.closeModal();
|
||||||
|
}}
|
||||||
|
className='p-1'
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:close' width={20} height={20} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex flex-col gap-4 p-4'>
|
||||||
|
<DateInput
|
||||||
|
name='edit_po_date'
|
||||||
|
label='Tanggal PO'
|
||||||
|
value={editPoDate}
|
||||||
|
onChange={(e) => setEditPoDate(e.target.value)}
|
||||||
|
isNestedModal
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex justify-end gap-3 border-t border-base-content/10 p-4'>
|
||||||
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
color='none'
|
||||||
|
onClick={() => {
|
||||||
|
setEditPoDate('');
|
||||||
|
editPoDateModal.closeModal();
|
||||||
|
}}
|
||||||
|
className='px-3 py-2.5'
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color='primary'
|
||||||
|
onClick={updatePoDateHandler}
|
||||||
|
className='px-3 py-2.5'
|
||||||
|
disabled={!editPoDate}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
{/* Staff Rejection Modal */}
|
{/* Staff Rejection Modal */}
|
||||||
<ConfirmationModalWithNotes
|
<ConfirmationModalWithNotes
|
||||||
ref={staffRejectionModal.ref}
|
ref={staffRejectionModal.ref}
|
||||||
|
|||||||
@@ -846,6 +846,27 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
|
|||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{!isLoading && data.length > 0 && meta && (
|
||||||
|
<div className='mt-5 px-3'>
|
||||||
|
<Pagination
|
||||||
|
totalItems={meta.total_results || 0}
|
||||||
|
itemsPerPage={meta.limit || 0}
|
||||||
|
currentPage={meta.page || 0}
|
||||||
|
onPrevPage={() =>
|
||||||
|
setCurrentPage((curr) => (curr > 1 ? curr - 1 : curr))
|
||||||
|
}
|
||||||
|
onNextPage={() =>
|
||||||
|
setCurrentPage((curr) =>
|
||||||
|
meta.total_pages && curr < meta.total_pages ? curr + 1 : curr
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onPageChange={(pageNumber) => setCurrentPage(pageNumber)}
|
||||||
|
rowOptions={[10, 20, 50, 100]}
|
||||||
|
onRowChange={setPageSize}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Filter Modal */}
|
{/* Filter Modal */}
|
||||||
|
|||||||
@@ -771,6 +771,27 @@ const DebtSupplierTab = ({ tabId }: DebtSupplierTabProps) => {
|
|||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{!isLoading && data.length > 0 && meta && (
|
||||||
|
<div className='mt-5 px-3'>
|
||||||
|
<Pagination
|
||||||
|
totalItems={meta.total_results || 0}
|
||||||
|
itemsPerPage={meta.limit || 0}
|
||||||
|
currentPage={meta.page || 0}
|
||||||
|
onPrevPage={() =>
|
||||||
|
setCurrentPage((curr) => (curr > 1 ? curr - 1 : curr))
|
||||||
|
}
|
||||||
|
onNextPage={() =>
|
||||||
|
setCurrentPage((curr) =>
|
||||||
|
meta.total_pages && curr < meta.total_pages ? curr + 1 : curr
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onPageChange={(pageNumber) => setCurrentPage(pageNumber)}
|
||||||
|
rowOptions={[10, 20, 50, 100]}
|
||||||
|
onRowChange={setPageSize}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Filter Modal */}
|
{/* Filter Modal */}
|
||||||
|
|||||||
@@ -115,6 +115,19 @@ export const PurchaseApi = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updatePoDate: async (
|
||||||
|
purchaseRequestId: number,
|
||||||
|
payload: { po_date: string }
|
||||||
|
): Promise<BaseApiResponse<Purchase> | undefined> => {
|
||||||
|
return await basePurchaseApi.customRequest<BaseApiResponse<Purchase>>(
|
||||||
|
`${purchaseRequestId}/po-date`,
|
||||||
|
{
|
||||||
|
method: 'PATCH',
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
async exportToExcel(initialQueryString: string) {
|
async exportToExcel(initialQueryString: string) {
|
||||||
const params = new URLSearchParams(initialQueryString);
|
const params = new URLSearchParams(initialQueryString);
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
@@ -68,6 +68,7 @@ export type BasePurchase = {
|
|||||||
supplier: Supplier;
|
supplier: Supplier;
|
||||||
credit_term?: number;
|
credit_term?: number;
|
||||||
due_date: string;
|
due_date: string;
|
||||||
|
received_date?: string | null;
|
||||||
notes?: string | null;
|
notes?: string | null;
|
||||||
deleted_at?: string | null;
|
deleted_at?: string | null;
|
||||||
created_by: number;
|
created_by: number;
|
||||||
@@ -122,6 +123,7 @@ export type UpdateStaffApprovalRequestPayload = {
|
|||||||
export type CreateManagerApprovalRequestPayload = {
|
export type CreateManagerApprovalRequestPayload = {
|
||||||
action: 'APPROVED' | 'REJECTED';
|
action: 'APPROVED' | 'REJECTED';
|
||||||
notes?: string | null;
|
notes?: string | null;
|
||||||
|
po_date?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateAcceptApprovalRequestPayload = {
|
export type CreateAcceptApprovalRequestPayload = {
|
||||||
|
|||||||
Reference in New Issue
Block a user