mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
feat(FE): Add travel document upload to purchase form
This commit is contained in:
@@ -8,6 +8,7 @@ import { useSearchParams } from 'next/navigation';
|
|||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import TextInput from '@/components/input/TextInput';
|
import TextInput from '@/components/input/TextInput';
|
||||||
import NumberInput from '@/components/input/NumberInput';
|
import NumberInput from '@/components/input/NumberInput';
|
||||||
|
import FileInput from '@/components/input/FileInput';
|
||||||
import SelectInput, {
|
import SelectInput, {
|
||||||
OptionType,
|
OptionType,
|
||||||
useSelect,
|
useSelect,
|
||||||
@@ -66,6 +67,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
| 'expedition_vendor_id'
|
| 'expedition_vendor_id'
|
||||||
| 'received_qty'
|
| 'received_qty'
|
||||||
| 'transport_per_item'
|
| 'transport_per_item'
|
||||||
|
| 'travel_documents'
|
||||||
): { isError: boolean; errorMessage: string } => {
|
): { isError: boolean; errorMessage: string } => {
|
||||||
const touchedItem = formik.touched.items?.[idx];
|
const touchedItem = formik.touched.items?.[idx];
|
||||||
const errorItem = formik.errors.items?.[idx] as
|
const errorItem = formik.errors.items?.[idx] as
|
||||||
@@ -185,6 +187,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
: formItem.transport_per_item || 0,
|
: formItem.transport_per_item || 0,
|
||||||
};
|
};
|
||||||
}) || [],
|
}) || [],
|
||||||
|
travel_documents: values.travel_documents || [],
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -236,15 +239,29 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
travel_document_path: item.travel_document_path || '',
|
travel_document_path: item.travel_document_path || '',
|
||||||
vehicle_number: item.vehicle_number || '',
|
vehicle_number: item.vehicle_number || '',
|
||||||
expedition_vendor: null,
|
expedition_vendor: null,
|
||||||
expedition_vendor_id: 0,
|
expedition_vendor_id: item.expedition_vendor_id || 0,
|
||||||
received_qty: item.total_qty || '',
|
received_qty: item.total_qty || '',
|
||||||
transport_per_item: '',
|
transport_per_item: item.transport_per_item || '',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
formik.setFieldValue('items', updatedItems);
|
formik.setFieldValue('items', updatedItems);
|
||||||
}
|
}
|
||||||
}, [purchaseItems, initialValues]);
|
}, [purchaseItems, initialValues]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
formik.values.travel_documents &&
|
||||||
|
formik.values.travel_documents.length > 0
|
||||||
|
) {
|
||||||
|
const fileNames = formik.values.travel_documents
|
||||||
|
.map((file) => file.name)
|
||||||
|
.join(', ');
|
||||||
|
formik.values.items?.forEach((item, idx) => {
|
||||||
|
formik.setFieldValue(`items.${idx}.travel_document_path`, fileNames);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [formik.values.travel_documents]);
|
||||||
|
|
||||||
// ===== HELPER FUNCTIONS =====
|
// ===== HELPER FUNCTIONS =====
|
||||||
const getQuantityExceededError = useCallback(
|
const getQuantityExceededError = useCallback(
|
||||||
(idx: number, receivedQty: number) => {
|
(idx: number, receivedQty: number) => {
|
||||||
@@ -343,7 +360,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
No. Surat Jalan
|
No. Surat Jalan
|
||||||
<span className='text-error'>*</span>
|
<span className='text-error'>*</span>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th className='hidden'>
|
||||||
Dokumen Surat Jalan
|
Dokumen Surat Jalan
|
||||||
<span className='text-error'>*</span>
|
<span className='text-error'>*</span>
|
||||||
</th>
|
</th>
|
||||||
@@ -478,7 +495,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td className='hidden'>
|
||||||
<TextInput
|
<TextInput
|
||||||
required
|
required
|
||||||
name={`items.${idx}.travel_document_path`}
|
name={`items.${idx}.travel_document_path`}
|
||||||
@@ -636,7 +653,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div className={'col-span-2'}>
|
<div className={'col-span-2 my-2'}>
|
||||||
<TextInput
|
<TextInput
|
||||||
label='Notes'
|
label='Notes'
|
||||||
name='notes'
|
name='notes'
|
||||||
@@ -649,6 +666,31 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className={'col-span-2 my-2'}>
|
||||||
|
<FileInput
|
||||||
|
required
|
||||||
|
name='travel_documents'
|
||||||
|
label='Dokumen Surat Jalan'
|
||||||
|
accept='.pdf,.jpg,.jpeg,.png'
|
||||||
|
onChange={(e) => {
|
||||||
|
const files = Array.from(e.target.files || []);
|
||||||
|
formik.setFieldValue('travel_documents', files);
|
||||||
|
}}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
bottomLabel={
|
||||||
|
formik.values.travel_documents &&
|
||||||
|
formik.values.travel_documents.length > 0
|
||||||
|
? `${formik.values.travel_documents.length} file(s) dipilih`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
isError={
|
||||||
|
formik.touched.travel_documents &&
|
||||||
|
Boolean(formik.errors.travel_documents)
|
||||||
|
}
|
||||||
|
errorMessage={formik.errors.travel_documents as string}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Action buttons */}
|
{/* Action buttons */}
|
||||||
<div className='flex flex-row justify-between gap-2 flex-wrap mt-5'>
|
<div className='flex flex-row justify-between gap-2 flex-wrap mt-5'>
|
||||||
<div className='flex flex-row justify-end gap-2 w-full'>
|
<div className='flex flex-row justify-end gap-2 w-full'>
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ type PurchaseRequestAcceptApprovalFormSchemaType = {
|
|||||||
received_qty: number | string;
|
received_qty: number | string;
|
||||||
transport_per_item: number | string;
|
transport_per_item: number | string;
|
||||||
}[];
|
}[];
|
||||||
|
travel_documents: File[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PurchaseStaffApprovalItemSchema = {
|
export type PurchaseStaffApprovalItemSchema = {
|
||||||
@@ -379,6 +380,11 @@ export const PurchaseRequestAcceptApprovalFormSchema: Yup.ObjectSchema<PurchaseR
|
|||||||
.min(1, 'Minimal harus ada 1 item pembelian!')
|
.min(1, 'Minimal harus ada 1 item pembelian!')
|
||||||
.required('Item pembelian wajib diisi!')
|
.required('Item pembelian wajib diisi!')
|
||||||
.typeError('Item pembelian wajib diisi!'),
|
.typeError('Item pembelian wajib diisi!'),
|
||||||
|
travel_documents: Yup.array()
|
||||||
|
.of(Yup.mixed<File>().required())
|
||||||
|
.required('Dokumen surat jalan wajib diupload!')
|
||||||
|
.min(1, 'Minimal upload 1 dokumen surat jalan!')
|
||||||
|
.typeError('Dokumen surat jalan wajib diupload!'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const PurchaseRequestAcceptApprovalFormInitialValues: PurchaseRequestAcceptApprovalFormSchemaType =
|
export const PurchaseRequestAcceptApprovalFormInitialValues: PurchaseRequestAcceptApprovalFormSchemaType =
|
||||||
@@ -397,6 +403,7 @@ export const PurchaseRequestAcceptApprovalFormInitialValues: PurchaseRequestAcce
|
|||||||
transport_per_item: '',
|
transport_per_item: '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
travel_documents: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PurchaseRequestAcceptApprovalFormDefaultValues = (
|
export const PurchaseRequestAcceptApprovalFormDefaultValues = (
|
||||||
@@ -428,6 +435,7 @@ export const PurchaseRequestAcceptApprovalFormDefaultValues = (
|
|||||||
transport_per_item: '',
|
transport_per_item: '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
travel_documents: [],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -72,11 +72,29 @@ export const PurchaseApi = {
|
|||||||
purchaseRequestId: number,
|
purchaseRequestId: number,
|
||||||
payload: CreateAcceptApprovalRequestPayload
|
payload: CreateAcceptApprovalRequestPayload
|
||||||
): Promise<BaseApiResponse<{ message: string }> | undefined> => {
|
): Promise<BaseApiResponse<{ message: string }> | undefined> => {
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
formData.append('action', payload.action);
|
||||||
|
|
||||||
|
if (payload.notes) {
|
||||||
|
formData.append('notes', payload.notes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.items) {
|
||||||
|
formData.append('items', JSON.stringify(payload.items));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.travel_documents) {
|
||||||
|
payload.travel_documents.forEach((file) => {
|
||||||
|
formData.append('travel_documents', file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return await basePurchaseApi.customRequest<
|
return await basePurchaseApi.customRequest<
|
||||||
BaseApiResponse<{ message: string }>
|
BaseApiResponse<{ message: string }>
|
||||||
>(`${purchaseRequestId}/receipts`, {
|
>(`${purchaseRequestId}/receipts`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
payload,
|
payload: formData as unknown as Record<string, unknown>,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Vendored
+1
@@ -118,6 +118,7 @@ export type CreateAcceptApprovalRequestPayload = {
|
|||||||
received_qty: number;
|
received_qty: number;
|
||||||
transport_per_item: number;
|
transport_per_item: number;
|
||||||
}[];
|
}[];
|
||||||
|
travel_documents?: File[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeletePurchaseRequestItemPayload = {
|
export type DeletePurchaseRequestItemPayload = {
|
||||||
|
|||||||
Reference in New Issue
Block a user