fix(FE): resolve merge conflict with branch development

This commit is contained in:
randy-ar
2025-12-17 14:26:03 +07:00
10 changed files with 156 additions and 47 deletions
@@ -14,7 +14,7 @@ const RecordingEdit = () => {
const { data: recording, isLoading: isLoadingRecording } = useSWR( const { data: recording, isLoading: isLoadingRecording } = useSWR(
recordingId, recordingId,
(id: number) => RecordingApi.getSingle(id) // Gunakan RecordingApi (id: string) => RecordingApi.getSingle(parseInt(id))
); );
if (!recordingId) { if (!recordingId) {
+1 -1
View File
@@ -14,7 +14,7 @@ const RecordingDetail = () => {
const { data: recording, isLoading: isLoadingRecording } = useSWR( const { data: recording, isLoading: isLoadingRecording } = useSWR(
recordingId, recordingId,
(id: number) => RecordingApi.getSingle(id) (id: string) => RecordingApi.getSingle(parseInt(id))
); );
if (!recordingId) { if (!recordingId) {
@@ -50,12 +50,17 @@ const RowOptionsMenu = ({
); );
}; };
const isRecordingRejected = (recording: Recording) => {
return recording.approval?.action === 'REJECTED';
};
const isApproved = isRecordingApproved(props.row.original); const isApproved = isRecordingApproved(props.row.original);
const isRejected = isRecordingRejected(props.row.original);
return ( return (
<RowOptionsMenuWrapper type={type}> <RowOptionsMenuWrapper type={type}>
<Button <Button
href={`recording/detail/?recordingId=${props.row.original.id}`} href={`/production/recording/detail/?recordingId=${props.row.original.id}`}
variant='ghost' variant='ghost'
color='primary' color='primary'
className='justify-start text-sm' className='justify-start text-sm'
@@ -64,7 +69,7 @@ const RowOptionsMenu = ({
Detail Detail
</Button> </Button>
<Button <Button
href={`recording/detail/edit/?recordingId=${props.row.original.id}`} href={`/production/recording/detail/edit/?recordingId=${props.row.original.id}`}
variant='ghost' variant='ghost'
color='warning' color='warning'
className='justify-start text-sm' className='justify-start text-sm'
@@ -72,7 +77,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>
{!isApproved && ( {!isApproved && !isRejected && (
<Button <Button
onClick={approveClickHandler} onClick={approveClickHandler}
variant='ghost' variant='ghost'
@@ -83,7 +88,7 @@ const RowOptionsMenu = ({
Approve Approve
</Button> </Button>
)} )}
{!isApproved && ( {!isApproved && !isRejected && (
<Button <Button
onClick={rejectClickHandler} onClick={rejectClickHandler}
variant='ghost' variant='ghost'
@@ -112,6 +112,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
); );
}, []); }, []);
const isRecordingRejected = useCallback((recording?: Recording) => {
return recording?.approval?.action === 'REJECTED';
}, []);
// ===== PAYLOAD CREATION HELPERS ===== // ===== PAYLOAD CREATION HELPERS =====
const createGrowingPayload = useCallback( const createGrowingPayload = useCallback(
(values: RecordingGrowingFormValues) => { (values: RecordingGrowingFormValues) => {
@@ -1483,37 +1487,48 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
Kembali Kembali
</Button> </Button>
{type === 'detail' && !isRecordingApproved(initialValues) && ( {type === 'detail' &&
<div className='flex flex-row gap-2'> initialValues?.approval &&
<Button !isRecordingApproved(initialValues) &&
variant='outline' !isRecordingRejected(initialValues) && (
color='success' <div className='flex flex-row gap-2'>
onClick={() => { <Button
setApprovalNotes(''); variant='outline'
approveModal.openModal(); color='success'
}} onClick={() => {
isLoading={isApproveLoading} setApprovalNotes('');
className='w-full sm:w-fit' approveModal.openModal();
> }}
<Icon icon='material-symbols:check' width={24} height={24} /> isLoading={isApproveLoading}
Approve className='w-full sm:w-fit'
</Button> >
<Icon
icon='material-symbols:check'
width={24}
height={24}
/>
Approve
</Button>
<Button <Button
variant='outline' variant='outline'
color='error' color='error'
onClick={() => { onClick={() => {
setApprovalNotes(''); setApprovalNotes('');
rejectModal.openModal(); rejectModal.openModal();
}} }}
isLoading={isRejectLoading} isLoading={isRejectLoading}
className='w-full sm:w-fit' className='w-full sm:w-fit'
> >
<Icon icon='material-symbols:close' width={24} height={24} /> <Icon
Reject icon='material-symbols:close'
</Button> width={24}
</div> height={24}
)} />
Reject
</Button>
</div>
)}
</div> </div>
<h1 className='text-2xl font-bold text-center'> <h1 className='text-2xl font-bold text-center'>
@@ -2803,7 +2818,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
{/* Approve Confirmation Modal */} {/* Approve Confirmation Modal */}
{(type as 'add' | 'edit' | 'detail') === 'detail' && {(type as 'add' | 'edit' | 'detail') === 'detail' &&
!isRecordingApproved(initialValues) && ( !isRecordingApproved(initialValues) &&
!isRecordingRejected(initialValues) && (
<ConfirmationModalWithNotes <ConfirmationModalWithNotes
ref={approveModal.ref} ref={approveModal.ref}
type='success' type='success'
@@ -314,7 +314,9 @@ const PurchaseOrderStaffApprovalForm = ({
const isNewItemForm = const isNewItemForm =
!formItem.purchase_item_id || formItem.purchase_item_id === 0; !formItem.purchase_item_id || formItem.purchase_item_id === 0;
let cleanPayload: UpdateStaffApprovalRequestPayload['items'][0]; let cleanPayload: NonNullable<
UpdateStaffApprovalRequestPayload['items']
>[0];
if (isNewItemForm) { if (isNewItemForm) {
cleanPayload = { cleanPayload = {
@@ -362,7 +364,9 @@ const PurchaseOrderStaffApprovalForm = ({
const isNewItemForm = const isNewItemForm =
!formItem.purchase_item_id || formItem.purchase_item_id === 0; !formItem.purchase_item_id || formItem.purchase_item_id === 0;
let cleanPayload: UpdateStaffApprovalRequestPayload['items'][0]; let cleanPayload: NonNullable<
UpdateStaffApprovalRequestPayload['items']
>[0];
if (isNewItemForm) { if (isNewItemForm) {
cleanPayload = { cleanPayload = {
@@ -7,6 +7,7 @@ type PurchaseRequestFormSchemaType = {
label: string; label: string;
} | null; } | null;
supplier_id: number; supplier_id: number;
credit_term: number;
area?: { area?: {
value: number; value: number;
label: string; label: string;
@@ -81,6 +82,10 @@ export const PurchaseRequestFormSchema: Yup.ObjectSchema<PurchaseRequestFormSche
.required('Supplier wajib dipilih!') .required('Supplier wajib dipilih!')
.min(1, 'Supplier wajib dipilih!') .min(1, 'Supplier wajib dipilih!')
.typeError('Supplier wajib dipilih!'), .typeError('Supplier wajib dipilih!'),
credit_term: Yup.number()
.required('Jangka waktu kredit wajib diisi!')
.min(0, 'Jangka waktu kredit tidak boleh kurang dari 0!')
.typeError('Jangka waktu kredit wajib diisi!'),
area: Yup.object({ area: Yup.object({
value: Yup.number().min(1).required(), value: Yup.number().min(1).required(),
label: Yup.string().required(), label: Yup.string().required(),
@@ -119,6 +124,7 @@ export const getPurchaseRequestFormInitialValues = (
} }
: null, : null,
supplier_id: initialValues?.supplier?.id ?? 0, supplier_id: initialValues?.supplier?.id ?? 0,
credit_term: initialValues?.credit_term ?? 0,
area: initialValues?.area area: initialValues?.area
? { ? {
value: initialValues.area.id, value: initialValues.area.id,
@@ -185,6 +185,10 @@ const PurchaseRequestForm = ({
typeof values.supplier_id === 'string' typeof values.supplier_id === 'string'
? parseInt(values.supplier_id) || 0 ? parseInt(values.supplier_id) || 0
: values.supplier_id || 0, : values.supplier_id || 0,
credit_term:
typeof values.credit_term === 'string'
? parseInt(values.credit_term) || 0
: values.credit_term || 0,
notes: values.notes || '', notes: values.notes || '',
items: (values.items || []).map((item) => ({ items: (values.items || []).map((item) => ({
warehouse_id: Number(item.warehouse_id) || 0, warehouse_id: Number(item.warehouse_id) || 0,
@@ -338,6 +342,27 @@ const PurchaseRequestForm = ({
}; };
// ===== UTILITY FUNCTIONS ===== // ===== UTILITY FUNCTIONS =====
const updateCreditTermBasedOnSupplier = useCallback(
(supplierId: number) => {
if (supplierId > 0 && isResponseSuccess(supplierRawData)) {
const supplierData = supplierRawData.data.find(
(s: Supplier) => s.id === supplierId
);
if (supplierData?.due_date) {
formik.setFieldTouched('credit_term', false);
formik.setFieldValue('credit_term', supplierData.due_date.toString());
} else {
formik.setFieldTouched('credit_term', false);
formik.setFieldValue('credit_term', '');
}
} else {
formik.setFieldTouched('credit_term', false);
formik.setFieldValue('credit_term', '');
}
},
[supplierRawData]
);
const resetPurchaseItems = useCallback(() => { const resetPurchaseItems = useCallback(() => {
if (formik.values.items) { if (formik.values.items) {
formik.values.items.forEach((_, idx) => { formik.values.items.forEach((_, idx) => {
@@ -352,6 +377,16 @@ const PurchaseRequestForm = ({
}, []); }, []);
// ===== SIDE EFFECTS ===== // ===== SIDE EFFECTS =====
useEffect(() => {
if (formik.values.supplier_id && Number(formik.values.supplier_id) > 0) {
updateCreditTermBasedOnSupplier(Number(formik.values.supplier_id));
resetPurchaseItems();
} else {
formik.setFieldTouched('credit_term', false);
formik.setFieldValue('credit_term', '');
resetPurchaseItems();
}
}, [formik.values.supplier_id]);
// ===== FORM HANDLERS ===== // ===== FORM HANDLERS =====
const handleSupplierChange = useCallback( const handleSupplierChange = useCallback(
@@ -367,6 +402,23 @@ const PurchaseRequestForm = ({
[] []
); );
const handleCreditTermChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
formik.setFieldTouched('credit_term', true);
formik.setFieldValue('credit_term', value);
},
[]
);
const handleCreditTermBlur = useCallback(
(e: React.FocusEvent<HTMLInputElement>) => {
formik.handleBlur(e);
},
[formik]
);
const handleAreaChange = useCallback( const handleAreaChange = useCallback(
(val: OptionType | OptionType[] | null) => { (val: OptionType | OptionType[] | null) => {
const area = val as OptionType | null; const area = val as OptionType | null;
@@ -447,7 +499,7 @@ const PurchaseRequestForm = ({
body: 'flex flex-col gap-6', body: 'flex flex-col gap-6',
}} }}
> >
<div className={'grid grid-cols-1 md:grid-cols-3 gap-6'}> <div className={'grid grid-cols-1 md:grid-cols-2 gap-6'}>
<SelectInput <SelectInput
required required
label='Vendor' label='Vendor'
@@ -466,6 +518,29 @@ const PurchaseRequestForm = ({
isClearable isClearable
/> />
<NumberInput
required={!!formik.values.supplier_id}
label='Jatuh tempo (hari)'
name='credit_term'
value={formik.values.credit_term || ''}
onChange={handleCreditTermChange}
onBlur={handleCreditTermBlur}
isError={
formik.touched.credit_term &&
Boolean(formik.errors.credit_term)
}
errorMessage={formik.errors.credit_term as string}
readOnly={type === 'detail' || !formik.values.supplier_id}
disabled={type === 'detail' || !formik.values.supplier_id}
allowNegative={false}
decimalScale={0}
placeholder={
!formik.values.supplier_id
? 'Pilih Vendor terlebih dahulu'
: 'Masukkan jumlah hari jatuh tempo'
}
/>
<SelectInput <SelectInput
label='Area' label='Area'
placeholder='Pilih Area...' placeholder='Pilih Area...'
@@ -490,7 +565,7 @@ const PurchaseRequestForm = ({
isClearable={type !== 'detail'} isClearable={type !== 'detail'}
/> />
<div className={'md:col-span-3'}> <div className={'col-span-2'}>
<TextInput <TextInput
label='Notes' label='Notes'
name='notes' name='notes'
@@ -1052,7 +1052,6 @@ const PurchaseOrderDetail = ({
const payload: CreateStaffApprovalRequestPayload = { const payload: CreateStaffApprovalRequestPayload = {
action: 'REJECTED', action: 'REJECTED',
notes: notes || null, notes: notes || null,
items: [],
}; };
await createStaffApprovalHandler(payload); await createStaffApprovalHandler(payload);
@@ -1080,7 +1079,6 @@ const PurchaseOrderDetail = ({
const payload: CreateAcceptApprovalRequestPayload = { const payload: CreateAcceptApprovalRequestPayload = {
action: 'REJECTED', action: 'REJECTED',
notes: notes || null, notes: notes || null,
items: [],
}; };
await createAcceptApprovalHandler(payload); await createAcceptApprovalHandler(payload);
@@ -309,6 +309,9 @@ const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => {
{purchaseData?.supplier?.alias || ''}) {purchaseData?.supplier?.alias || ''})
</Text> </Text>
<Text>{purchaseData?.supplier?.category || '-'}</Text> <Text>{purchaseData?.supplier?.category || '-'}</Text>
<Text>
Credit Term: {purchaseData?.credit_term || 0} hari
</Text>
<Text> <Text>
Due Date:{' '} Due Date:{' '}
{purchaseData?.due_date {purchaseData?.due_date
+6 -4
View File
@@ -51,6 +51,7 @@ export type BasePurchase = {
po_document_path?: string | null; po_document_path?: string | null;
po_date: string; po_date: string;
supplier: Supplier; supplier: Supplier;
credit_term?: number;
due_date: string; due_date: string;
notes?: string | null; notes?: string | null;
deleted_at?: string | null; deleted_at?: string | null;
@@ -66,8 +67,9 @@ export type Purchase = BaseMetadata & BasePurchase;
export type CreatePurchaseRequestPayload = { export type CreatePurchaseRequestPayload = {
supplier_id: number; supplier_id: number;
credit_term: number;
notes?: string | null; notes?: string | null;
items: { items?: {
warehouse_id: number; warehouse_id: number;
product_id: number; product_id: number;
qty: number; qty: number;
@@ -77,7 +79,7 @@ export type CreatePurchaseRequestPayload = {
export type CreateStaffApprovalRequestPayload = { export type CreateStaffApprovalRequestPayload = {
action: 'APPROVED' | 'REJECTED'; action: 'APPROVED' | 'REJECTED';
notes?: string | null; notes?: string | null;
items: { items?: {
purchase_item_id: number; purchase_item_id: number;
qty: number; qty: number;
price: number; price: number;
@@ -88,7 +90,7 @@ export type CreateStaffApprovalRequestPayload = {
export type UpdateStaffApprovalRequestPayload = { export type UpdateStaffApprovalRequestPayload = {
action: 'APPROVED' | 'REJECTED'; action: 'APPROVED' | 'REJECTED';
notes?: string | null; notes?: string | null;
items: Array<{ items?: Array<{
purchase_item_id?: number; purchase_item_id?: number;
product_id?: number; product_id?: number;
warehouse_id?: number; warehouse_id?: number;
@@ -106,7 +108,7 @@ export type CreateManagerApprovalRequestPayload = {
export type CreateAcceptApprovalRequestPayload = { export type CreateAcceptApprovalRequestPayload = {
action: 'APPROVED' | 'REJECTED'; action: 'APPROVED' | 'REJECTED';
notes?: string | null; notes?: string | null;
items: { items?: {
purchase_item_id: number; purchase_item_id: number;
received_date: string; received_date: string;
travel_number: string; travel_number: string;