refactor(FE-88): memisahkan file api project flock & penyesuaian tipe data dan paylod dengan BE

This commit is contained in:
randy-ar
2025-11-12 09:04:23 +07:00
parent f63d3d3870
commit 5dccaf40cb
13 changed files with 888 additions and 211 deletions
@@ -29,7 +29,6 @@ const ChickinFormKandang = ({
return (
<div className='flex flex-col gap-4'>
<FormHeader
type='add'
title='Chick In DOC'
backUrl={`/production/project-flock/chickin/add?projectFlockId=${initialValues?.project_flock?.id}`}
/>
@@ -96,7 +95,7 @@ const ChickinFormKandang = ({
tabs={[
{
id: 'formChickIn',
label: 'Form Chick In',
label: 'Tambah Chick In',
content: (
<ChickinFormView
initialValues={initialValues}
@@ -0,0 +1,172 @@
import Alert from '@/components/Alert';
import Button from '@/components/Button';
import Card from '@/components/Card';
import { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import PillBadge from '@/components/PillBadge';
import Table from '@/components/Table';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { cn, formatDate, formatNumber } from '@/lib/helper';
import { ChickinApi } from '@/services/api/production/chickin';
import {
Chickin,
ProjectFlockKandang,
} from '@/types/api/production/project-flock-kandang';
import { Icon } from '@iconify/react';
import { useState } from 'react';
import toast from 'react-hot-toast';
const ChickinLogsView = ({
initialValues,
afterSubmit,
}: {
initialValues: ProjectFlockKandang;
afterSubmit?: () => void;
}) => {
const confirmModal = useModal();
const [isApproveLoading, setIsApproveLoading] = useState(false);
const [chickinErrorMessage, setChickinErrorMessage] = useState('');
const handleClickApprove = () => {
confirmModal.openModal();
};
const confirmationModalApproveClickHandler = async () => {
setChickinErrorMessage('');
setIsApproveLoading(true);
const approveChickinRes = await ChickinApi.singleApproval(
initialValues?.id as number,
'APPROVED'
);
if (isResponseSuccess(approveChickinRes)) {
toast.success(approveChickinRes?.message as string);
}
if (isResponseError(approveChickinRes)) {
toast.error(approveChickinRes?.message as string);
setChickinErrorMessage(approveChickinRes?.message as string);
}
confirmModal.closeModal();
setIsApproveLoading(false);
if (afterSubmit) {
afterSubmit();
}
};
return (
<>
<Card
title='Riwayat Chick In'
className={{
wrapper: 'w-full bg-white',
}}
>
<div className='flex flex-row justify-start gap-3 mt-3'>
<Button
color='success'
variant='outline'
onClick={handleClickApprove}
>
<Icon width={24} height={24} icon='material-symbols:check' />
Approve
</Button>
</div>
<Table<Chickin>
data={initialValues?.chickins || []}
columns={[
{
header: '#',
cell: (props) => props.row.index + 1,
},
{
accessorFn: (row) => row.chick_in_date,
header: 'Tanggal Chick In',
cell: (props) => {
return formatDate(props.getValue() as string, 'DD MMM YYYY');
},
},
{
accessorFn: (row) => row.product_warehouse?.warehouse?.name,
header: 'Kandang',
},
{
accessorFn: (row) => row.product_warehouse?.product?.name,
header: 'Produk',
},
{
accessorFn: (row) => row.usage_qty ?? row.pending_usage_qty,
header: 'Jumlah Chick In',
cell: (props) => {
if (props.row.original.usage_qty != 0) {
return formatNumber(props.row.original.usage_qty);
} else if (props.row.original.pending_usage_qty != 0) {
return formatNumber(props.row.original.pending_usage_qty);
} else {
return '-';
}
},
},
{
accessorFn: (row) => row.pending_usage_qty,
header: 'Status',
cell: (props) => {
return (
<PillBadge
content={
props.row.original.usage_qty !== 0
? 'Disetujui'
: props.row.original.pending_usage_qty !== 0
? 'Pending'
: '-'
}
color={
props.row.original.usage_qty !== 0
? 'green'
: props.row.original.pending_usage_qty !== 0
? 'yellow'
: 'gray'
}
/>
);
},
},
]}
className={{
containerClassName: cn({
'mb-20': initialValues?.chickins?.length === 0,
}),
tableWrapperClassName: 'overflow-x-auto min-h-full!',
tableClassName: 'font-inter w-full table-auto min-h-full!',
headerRowClassName: 'border-b border-b-gray-200',
headerColumnClassName:
'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
bodyRowClassName: 'border-b border-b-gray-200',
bodyColumnClassName:
'px-6 py-3 last:flex last:flex-row last:justify-end',
paginationClassName: 'hidden',
}}
/>
{chickinErrorMessage && (
<div className='w-full' onClick={() => setChickinErrorMessage('')}>
<Alert color='error'>{chickinErrorMessage}</Alert>
</div>
)}
</Card>
<ConfirmationModal
ref={confirmModal.ref}
type='success'
text={`Apakah anda yakin ingin approve data Chickin yang Pending?`}
secondaryButton={{
text: 'Tidak',
}}
primaryButton={{
text: 'Ya',
color: 'success',
onClick: confirmationModalApproveClickHandler,
isLoading: isApproveLoading,
}}
/>
</>
);
};
export default ChickinLogsView;
@@ -0,0 +1,236 @@
'use client';
import Card from '@/components/Card';
import Table from '@/components/Table';
import {
ChickinFormValues,
ChickinRequestFormValues,
ChickinSchema,
} from '../ChickinForm.schema';
import DateInput from '@/components/input/DateInput';
import SelectInput, { OptionType } from '@/components/input/SelectInput';
import NumberInput from '@/components/input/NumberInput';
import Button from '@/components/Button';
import { useCallback, useEffect, useState } from 'react';
import { useFormik } from 'formik';
import { flushSync } from 'react-dom';
import { CreateChickinPayload } from '@/types/api/production/chickin';
import { ChickinApi } from '@/services/api/production/chickin';
import { isResponseError } from '@/lib/api-helper';
import toast from 'react-hot-toast';
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
import { useRouter } from 'next/navigation';
import Alert from '@/components/Alert';
const ChickinFormView = ({
formType = 'add',
initialValues,
afterSubmit,
}: {
formType?: 'add' | 'detail' | 'edit';
initialValues: ProjectFlockKandang;
afterSubmit?: () => void;
}) => {
const router = useRouter();
const [chickinErrorMessage, setChickinErrorMessage] = useState('');
const createChickin = useCallback(
async (payload: CreateChickinPayload) => {
const createChickinRes = await ChickinApi.create(payload);
if (isResponseError(createChickinRes)) {
setChickinErrorMessage(createChickinRes.message);
return;
}
toast.success(createChickinRes?.message as string);
// router.push(
// `/production/project-flock/chickin/add?projectFlockId=${initialValues?.project_flock?.id}`
// );
if (afterSubmit) {
afterSubmit();
}
},
[router]
);
const handleReset = async () => {
flushSync(() => {
formik.resetForm({
values: {
project_flock_kandang_id: initialValues?.id,
chickin_requests: initialValues?.available_qtys
? initialValues.available_qtys.map((availableQty) => ({
chick_in_date: '',
product_warehouse_id: availableQty.product_warehouse.id,
available_qty: availableQty.available_qty,
note: `Chickin project-flock-kandang-${initialValues?.id} product-warehouse-${availableQty.product_warehouse.id}`,
}))
: [],
},
});
});
formik.setTouched({
chickin_requests: initialValues?.available_qtys?.map(() => ({
chick_in_date: true,
})),
});
const errors = await formik.validateForm();
formik.setErrors(errors);
};
const formik = useFormik<ChickinFormValues>({
enableReinitialize: true,
validationSchema: ChickinSchema,
initialValues: {
project_flock_kandang_id: initialValues?.id,
chickin_requests: initialValues?.available_qtys
? initialValues.available_qtys.map((availableQty) => ({
chick_in_date: '',
product_warehouse_id: availableQty.product_warehouse.id,
available_qty: availableQty.available_qty,
note: `Chickin project-flock-kandang-${initialValues?.id} product-warehouse-${availableQty.product_warehouse.id}`,
}))
: [],
},
onSubmit: (values) => {
setChickinErrorMessage('');
createChickin(values as CreateChickinPayload);
if (afterSubmit) {
afterSubmit();
}
},
});
const { setValues: formikSetValues } = formik;
useEffect(() => {
formikSetValues({
project_flock_kandang_id: initialValues?.id,
chickin_requests: initialValues?.available_qtys
? initialValues.available_qtys.map((availableQty) => ({
chick_in_date: '',
product_warehouse_id: availableQty.product_warehouse.id,
available_qty: availableQty.available_qty,
note: `Chickin project-flock-kandang-${initialValues?.id} product-warehouse-${availableQty.product_warehouse.id}`,
}))
: [],
});
}, [formikSetValues, initialValues]);
return (
<form
className='flex flex-col gap-4'
onReset={(e) => {
handleReset();
}}
onSubmit={formik.handleSubmit}
>
<Card
title='Informasi Chick In DOC'
className={{
wrapper: 'w-full bg-white',
}}
>
<Table<ChickinRequestFormValues>
data={formik.values.chickin_requests || []}
columns={[
{
accessorFn: (row) => row.chick_in_date,
header: 'Tanggal Chick In',
cell(props) {
return (
<DateInput
name={`chickin_requests[${props.row.index}].chick_in_date`}
value={
formik.values.chickin_requests[props.row.index]
?.chick_in_date as string
}
onChange={formik.handleChange}
/>
);
},
},
{
accessorFn: (row) => row.product_warehouse_id,
header: 'Produk',
cell(props) {
const availableQty = initialValues?.available_qtys?.find(
(availableQty) =>
availableQty.product_warehouse.id ===
props.row.original.product_warehouse_id
);
return (
<SelectInput
value={
{
label: availableQty?.product_warehouse?.product?.name,
value: availableQty?.product_warehouse?.product?.id,
} as OptionType
}
options={[]}
isDisabled
/>
);
},
},
{
accessorFn: (row) => row.product_warehouse_id,
header: 'Jumlah (ekor)',
cell(props) {
const availableQty = initialValues?.available_qtys?.find(
(availableQty) =>
availableQty.product_warehouse.id ===
props.row.original.product_warehouse_id
);
return (
<NumberInput
name='qty[]'
value={availableQty?.available_qty}
/>
);
},
},
]}
className={{
tableWrapperClassName: 'overflow-x-auto min-h-full!',
tableClassName: 'font-inter w-full table-auto min-h-full!',
headerRowClassName: 'border-b border-b-gray-200',
headerColumnClassName:
'px-2 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
bodyRowClassName: 'border-b border-b-gray-200',
bodyColumnClassName:
'px-2 py-2 last:flex last:flex-row last:justify-end',
paginationClassName: 'hidden',
}}
emptyContent={
<div className='w-full p-5 text-center'>
<span className='text-lg opacity-50'>
Isi persediaan DOC untuk kandang belum tersedia...
</span>
</div>
}
/>
</Card>
<div className='flex flex-row justify-center gap-3'>
<Button type='reset' color='warning' disabled={formik.isSubmitting}>
Reset
</Button>
<Button
type='submit'
color='primary'
disabled={!formik.isValid || formik.isSubmitting}
>
Submit
</Button>
</div>
{chickinErrorMessage && (
<div className='w-full' onClick={() => setChickinErrorMessage('')}>
<Alert color='error'>{chickinErrorMessage}</Alert>
</div>
)}
</form>
);
};
export default ChickinFormView;