Merge branch 'dev/restu' into 'development'

[HOTFIX/FE][US#281-391] Adjustment Uniformity, Purchase and Expense

See merge request mbugroup/lti-web-client!138
This commit is contained in:
Rivaldi A N S
2026-01-07 02:15:53 +00:00
12 changed files with 115 additions and 62 deletions
+8
View File
@@ -33,6 +33,7 @@ const FileInput = ({
isError, isError,
errorMessage, errorMessage,
disabled = false, disabled = false,
required = false,
onChange, onChange,
onBlur, onBlur,
readOnly = false, readOnly = false,
@@ -56,6 +57,13 @@ const FileInput = ({
)} )}
> >
{label} {label}
{required && (
<>
<span className='tooltip tooltip-error' data-tip='required'>
<span className='text-error'> *</span>
</span>
</>
)}
</label> </label>
)} )}
@@ -140,17 +140,17 @@ const ExpenseRequestContent = ({
const confirmationModalDeleteClickHandler = async () => { const confirmationModalDeleteClickHandler = async () => {
setIsDeleteLoading(true); setIsDeleteLoading(true);
try { const deleteResponse = await ExpenseApi.delete(initialValues?.id as number);
await ExpenseApi.delete(initialValues?.id as number);
if (isResponseSuccess(deleteResponse)) {
toast.success('Berhasil menghapus data biaya operasional!'); toast.success('Berhasil menghapus data biaya operasional!');
router.push('/expense'); router.push('/expense');
} catch (error) { } else {
toast.error('Gagal menghapus data biaya operasional!'); toast.error('Gagal menghapus data biaya operasional!');
} finally {
deleteModal.closeModal();
setIsDeleteLoading(false);
} }
deleteModal.closeModal();
setIsDeleteLoading(false);
}; };
const confirmationModalCompleteClickHandler = async () => { const confirmationModalCompleteClickHandler = async () => {
@@ -21,7 +21,7 @@ const ExpenseStatusBadge = ({ approval }: ExpenseStatusBadgeProps) => {
switch (latestApprovalStepNumber) { switch (latestApprovalStepNumber) {
case 1: case 1:
expenseStatusPillBadgeColor = 'yellow'; expenseStatusPillBadgeColor = 'gray';
break; break;
case 2: case 2:
@@ -33,7 +33,7 @@ const ExpenseStatusBadge = ({ approval }: ExpenseStatusBadgeProps) => {
break; break;
case 4: case 4:
expenseStatusPillBadgeColor = 'red'; expenseStatusPillBadgeColor = 'yellow';
break; break;
case 5: case 5:
+12 -4
View File
@@ -420,11 +420,19 @@ const ExpensesTable = () => {
const confirmationModalDeleteClickHandler = async () => { const confirmationModalDeleteClickHandler = async () => {
setIsDeleteLoading(true); setIsDeleteLoading(true);
await ExpenseApi.delete(selectedExpense?.id as number); const deleteResponse = await ExpenseApi.delete(
refreshExpenses(); selectedExpense?.id as number
);
if (isResponseSuccess(deleteResponse)) {
refreshExpenses();
deleteModal.closeModal();
toast.success('Berhasil menghapus biaya operasional!');
} else {
deleteModal.closeModal();
toast.error('Gagal menghapus biaya operasional!');
}
deleteModal.closeModal();
toast.success('Berhasil menghapus biaya operasional!');
setIsDeleteLoading(false); setIsDeleteLoading(false);
}; };
@@ -1562,7 +1562,9 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
width={20} width={20}
height={20} height={20}
/> />
{delivery.document.name} <span className='truncate max-w-[200px]'>
{delivery.document.name}
</span>
</Button> </Button>
) : ( ) : (
<Button <Button
@@ -1582,6 +1584,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
</> </>
) : ( ) : (
<FileInput <FileInput
accept='.pdf,.jpg,.jpeg,.png'
name={`deliveries.${idx}.document`} name={`deliveries.${idx}.document`}
onChange={(e) => { onChange={(e) => {
const file = e.target.files?.[0]; const file = e.target.files?.[0];
@@ -51,8 +51,10 @@ import MenuItem from '@/components/menu/MenuItem';
const UniformityConfirmationPreview = ({ const UniformityConfirmationPreview = ({
uniformity, uniformity,
uniformityDetail,
}: { }: {
uniformity?: Uniformity; uniformity?: Uniformity;
uniformityDetail?: UniformityDetail;
}) => { }) => {
const data: DetailOptionType[] = [ const data: DetailOptionType[] = [
{ {
@@ -60,32 +62,42 @@ const UniformityConfirmationPreview = ({
label: 'Tanggal', label: 'Tanggal',
value: uniformity value: uniformity
? formatDate(uniformity.applied_at, 'DD MMM YYYY') ? formatDate(uniformity.applied_at, 'DD MMM YYYY')
: '-', : uniformityDetail
? formatDate(uniformityDetail.info_umum.tanggal, 'DD MMM YYYY')
: '-',
}, },
{ {
id: 'lokasi-farm', id: 'lokasi-farm',
label: 'Lokasi Farm', label: 'Lokasi Farm',
value: uniformity?.location_name || '-', value:
uniformity?.location_name ||
uniformityDetail?.info_umum?.lokasi_farm ||
'-',
}, },
{ {
id: 'project-flock', id: 'project-flock',
label: 'Project Flock', label: 'Project Flock',
value: uniformity?.flock_name || '-', value:
uniformity?.flock_name ||
uniformityDetail?.info_umum?.project_flock ||
'-',
}, },
{ {
id: 'kandang', id: 'kandang',
label: 'Kandang', label: 'Kandang',
value: uniformity?.kandang_name || '-', value:
uniformity?.kandang_name || uniformityDetail?.info_umum?.kandang || '-',
}, },
{ {
id: 'file-uniformity', id: 'file-uniformity',
label: 'File Uniformity', label: 'File Uniformity',
value: '-', value:
uniformity?.file_name || uniformityDetail?.info_umum?.file_name || '-',
}, },
{ {
id: 'status', id: 'status',
label: 'Status', label: 'Status',
value: uniformity?.status || '-', value: uniformity?.status || (uniformityDetail ? 'CREATED' : '-'),
}, },
]; ];
@@ -448,9 +460,15 @@ const UniformityTable = () => {
const canApproveReject = useMemo(() => { const canApproveReject = useMemo(() => {
return ( return (
selectedUniformities.length > 0 && selectedUniformities.length > 0 &&
selectedUniformities.every( selectedUniformities.every((u) => {
(u) => u.status === 'CREATED' || u.status === 'Pengajuan' const approvalAction = u.latest_approval?.action;
) return (
approvalAction === 'CREATED' ||
approvalAction === 'Pengajuan' ||
(!approvalAction &&
(u.status === 'CREATED' || u.status === 'Pengajuan'))
);
})
); );
}, [selectedUniformities]); }, [selectedUniformities]);
@@ -805,7 +823,9 @@ const UniformityTable = () => {
accessorKey: 'status', accessorKey: 'status',
header: 'Status', header: 'Status',
cell: (props) => { cell: (props) => {
const status = props.row.original.status; const uniformity = props.row.original;
const status =
uniformity.latest_approval?.action ?? uniformity.status;
return ( return (
<div className='w-full'> <div className='w-full'>
<Badge <Badge
@@ -938,34 +958,7 @@ const UniformityTable = () => {
<div className='flex flex-col gap-4 mt-4'> <div className='flex flex-col gap-4 mt-4'>
{createdUniformity ? ( {createdUniformity ? (
<UniformityConfirmationPreview <UniformityConfirmationPreview
uniformity={{ uniformityDetail={createdUniformity}
id: createdUniformity.id,
location_name: createdUniformity.info_umum.lokasi_farm,
flock_name: createdUniformity.info_umum.project_flock,
kandang_name: createdUniformity.info_umum.kandang,
applied_at: createdUniformity.info_umum.tanggal,
week: 0,
status: 'Pengajuan',
uniformity: createdUniformity.result.uniformity,
cv: createdUniformity.result.cv,
chick_qty_of_weight:
createdUniformity.sampling.chick_qty_of_weight,
uniform_qty: createdUniformity.result.uniform_qty,
mean_up: createdUniformity.sampling.mean_up,
mean_down: createdUniformity.sampling.mean_down,
standard_mean_weight: null,
standard_uniformity: null,
created_at: '',
created_by: 0,
project_flock_kandang_id: 0,
created_user: {
id: 0,
id_user: 0,
email: '',
name: '',
},
updated_at: '',
}}
/> />
) : selectedRowIds.length === 1 ? ( ) : selectedRowIds.length === 1 ? (
<UniformityConfirmationPreview <UniformityConfirmationPreview
@@ -689,6 +689,16 @@ const PurchaseOrderAcceptApprovalForm = ({
accept='.pdf,.jpg,.jpeg,.png' accept='.pdf,.jpg,.jpeg,.png'
onChange={(e) => { onChange={(e) => {
const files = Array.from(e.target.files || []); const files = Array.from(e.target.files || []);
const invalidFiles = files.filter(
(file) => file.size > 2 * 1024 * 1024
);
if (invalidFiles.length > 0) {
toast.error('Ukuran dokumen maksimal 2 MB!');
e.target.value = '';
return;
}
formik.setFieldValue('travel_documents', files); formik.setFieldValue('travel_documents', files);
}} }}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -312,7 +312,8 @@ export const PurchaseRequestStaffApprovalFormInitialValues: PurchaseRequestStaff
}; };
export const PurchaseRequestStaffApprovalFormDefaultValues = ( export const PurchaseRequestStaffApprovalFormDefaultValues = (
purchase?: Purchase purchase?: Purchase,
type?: 'add' | 'edit'
): PurchaseRequestStaffApprovalFormSchemaType => { ): PurchaseRequestStaffApprovalFormSchemaType => {
return { return {
action: 'APPROVED', action: 'APPROVED',
@@ -331,8 +332,18 @@ export const PurchaseRequestStaffApprovalFormDefaultValues = (
label: item.warehouse?.name || '', label: item.warehouse?.name || '',
}, },
qty: item.sub_qty || item.qty || 0, qty: item.sub_qty || item.qty || 0,
price: item.price, price:
total_price: item.total_price, type === 'add'
? 'ProductPrice' in item.product
? item.product.ProductPrice || item.price || ''
: item.price
: item.price,
total_price:
type === 'add'
? ('ProductPrice' in item.product
? item.product.ProductPrice || item.price || 0
: item.price) * (item.sub_qty || item.qty || 0)
: item.total_price,
})) }))
: [ : [
{ {
@@ -381,7 +392,15 @@ export const PurchaseRequestAcceptApprovalFormSchema: Yup.ObjectSchema<PurchaseR
.required('Item pembelian wajib diisi!') .required('Item pembelian wajib diisi!')
.typeError('Item pembelian wajib diisi!'), .typeError('Item pembelian wajib diisi!'),
travel_documents: Yup.array() travel_documents: Yup.array()
.of(Yup.mixed<File>().required()) .of(
Yup.mixed<File>()
.required('Dokumen surat jalan wajib diupload!')
.test('fileSize', 'Ukuran dokumen maksimal 2 MB', (value) => {
if (!value) return true;
if (value instanceof File) return value.size <= 2 * 1024 * 1024;
return true;
})
)
.required('Dokumen surat jalan wajib diupload!') .required('Dokumen surat jalan wajib diupload!')
.min(1, 'Minimal upload 1 dokumen surat jalan!') .min(1, 'Minimal upload 1 dokumen surat jalan!')
.typeError('Dokumen surat jalan wajib diupload!'), .typeError('Dokumen surat jalan wajib diupload!'),
@@ -294,9 +294,9 @@ const PurchaseOrderStaffApprovalForm = ({
// ===== FORM CONFIGURATION ===== // ===== FORM CONFIGURATION =====
const formikInitialValues = useMemo(() => { const formikInitialValues = useMemo(() => {
return initialValues return initialValues
? PurchaseRequestStaffApprovalFormDefaultValues(initialValues) ? PurchaseRequestStaffApprovalFormDefaultValues(initialValues, type)
: PurchaseRequestStaffApprovalFormInitialValues; : PurchaseRequestStaffApprovalFormInitialValues;
}, [initialValues]); }, [initialValues, type]);
const formik = useFormik({ const formik = useFormik({
initialValues: formikInitialValues, initialValues: formikInitialValues,
@@ -485,9 +485,18 @@ const PurchaseOrderStaffApprovalForm = ({
}, },
warehouse_id: purchaseItem.warehouse_id || 0, warehouse_id: purchaseItem.warehouse_id || 0,
qty: originalItem?.qty || purchaseItem.quantity || 0, qty: originalItem?.qty || purchaseItem.quantity || 0,
price: type === 'edit' && originalItem ? originalItem.price : '', price:
type === 'edit' && originalItem
? originalItem.price
: originalItem?.product && 'ProductPrice' in originalItem.product
? originalItem.product.ProductPrice || ''
: '',
total_price: total_price:
type === 'edit' && originalItem ? originalItem.total_price : '', type === 'edit' && originalItem
? originalItem.total_price
: (originalItem?.product && 'ProductPrice' in originalItem.product
? originalItem.product.ProductPrice || 0
: 0) * (originalItem?.qty || purchaseItem.quantity || 0),
}; };
return itemData; return itemData;
}); });
@@ -1140,6 +1149,7 @@ const PurchaseOrderStaffApprovalForm = ({
color='warning' color='warning'
className='px-4' className='px-4'
onClick={() => { onClick={() => {
formik.setValues(formikInitialValues);
formik.resetForm(); formik.resetForm();
setPurchaseOrderFormErrorMessage(''); setPurchaseOrderFormErrorMessage('');
onCancel?.(); onCancel?.();
+2
View File
@@ -10,6 +10,8 @@ export type BaseInventoryProduct = {
name: string; name: string;
brand: string; brand: string;
sku: string; sku: string;
ProductPrice: number;
SellingPrice?: number;
product_price: number; product_price: number;
selling_price?: number; selling_price?: number;
tax?: number; tax?: number;
+1 -3
View File
@@ -1,7 +1,4 @@
import { BaseMetadata } from '@/types/api/api-general'; import { BaseMetadata } from '@/types/api/api-general';
import { Location } from '@/types/api/location/location';
import { ProjectFlock } from '@/types/api/project-flock/project-flock';
import { Kandang } from '@/types/api/kandang/kandang';
import { BaseApproval } from '@/types/api/approval/approval'; import { BaseApproval } from '@/types/api/approval/approval';
// ==================== GET ALL RESPONSE ==================== // ==================== GET ALL RESPONSE ====================
@@ -11,6 +8,7 @@ export type Uniformity = BaseMetadata & {
location_name: string; location_name: string;
flock_name: string; flock_name: string;
kandang_name: string; kandang_name: string;
file_name: string;
applied_at: string; applied_at: string;
week: number; week: number;
status: string; status: string;
+2
View File
@@ -10,6 +10,8 @@ export type PurchaseItemProduct = {
id: number; id: number;
name: string; name: string;
flags?: string[]; flags?: string[];
ProductPrice?: number;
SellingPrice?: number;
uom?: { uom?: {
name: string; name: string;
}; };