refactor(FE-239-238): Refactor UI & API Integration For Form Chickin & Chickin Details

This commit is contained in:
randy-ar
2025-11-10 06:07:02 +07:00
parent e0c347c3d5
commit 9f4f140018
10 changed files with 122 additions and 288 deletions
@@ -12,9 +12,12 @@ export default function AddChickinKandang() {
const projectFlockId = searchParams.get('projectFlockId');
const router = useRouter();
const { data: projectFlockKandang, isLoading: isLoading } = useSWR(
projectFlockKandangId,
(id: number) => ProjectFlockKandangApi.getSingle(id)
const {
data: projectFlockKandang,
isLoading: isLoading,
mutate: refreshProjectFlockKandang,
} = useSWR(projectFlockKandangId, (id: number) =>
ProjectFlockKandangApi.getSingle(id)
);
if (!projectFlockKandangId) {
@@ -31,6 +34,10 @@ export default function AddChickinKandang() {
return;
}
const handleAfterSubmit = () => {
refreshProjectFlockKandang();
};
return (
<>
<section className='w-full p-4'>
@@ -38,7 +45,10 @@ export default function AddChickinKandang() {
{!isLoading &&
isResponseSuccess(projectFlockKandang) &&
projectFlockId && (
<ChickinForm initialValues={projectFlockKandang.data} />
<ChickinForm
initialValues={projectFlockKandang.data}
afterSubmit={handleAfterSubmit}
/>
)}
</section>
</>
@@ -6,7 +6,7 @@ import Modal, { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import ChickinForm from '@/components/pages/production/chickin/form/ChickinForm';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { ChickinApi } from '@/services/api/production';
import { ChickinApi } from '@/services/api/production/chickin';
import { BaseApiResponse } from '@/types/api/api-general';
import {
Chickin,
@@ -57,7 +57,7 @@ const ProjectFlockDetail = () => {
flock: flock.data.find(
(flock) =>
flock.name ==
projectFlock.data.flock_name
projectFlock?.data?.flock_name
.trim()
.split(/\s+/)
.slice(0, -1)
@@ -13,7 +13,7 @@ import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector';
import { ROWS_OPTIONS } from '@/config/constant';
import { isResponseSuccess } from '@/lib/api-helper';
import { cn, formatNumber } from '@/lib/helper';
import { ChickinApi } from '@/services/api/production';
import { ChickinApi } from '@/services/api/production/chickin';
import { useTableFilter } from '@/services/hooks/useTableFilter';
import { Chickin } from '@/types/api/production/chickin';
import { Icon } from '@iconify/react';
@@ -1,33 +1,15 @@
'use client';
import Button from '@/components/Button';
import Card from '@/components/Card';
import { FormHeader } from '@/components/helper/form/FormHeader';
import DateInput from '@/components/input/DateInput';
import FileInput from '@/components/input/FileInput';
import NumberInput from '@/components/input/NumberInput';
import SelectInput, { OptionType } from '@/components/input/SelectInput';
import TextInput from '@/components/input/TextInput';
import Table from '@/components/Table';
import { formatNumber } from '@/lib/helper';
import { Kandang } from '@/types/api/master-data/kandang';
import {
AvailableQty,
ProjectFlockKandang,
} from '@/types/api/production/project-flock-kandang';
import { useFormik } from 'formik';
import {
ChickinFormValues,
ChickinRequestFormValues,
ChickinSchema,
} from './ChickinForm.schema';
import { useCallback, useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { CreateChickinPayload } from '@/types/api/production/chickin';
import { ChickinApi } from '@/services/api/production';
import { isResponseError } from '@/lib/api-helper';
import toast from 'react-hot-toast';
import { flushSync } from 'react-dom';
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
import Tabs from '@/components/Tabs';
import ChickinFormView from './tabs/ChickinFormView';
import ChickinLogsView from './tabs/ChickLogsView';
import { useState } from 'react';
const ChickinFormKandang = ({
formType = 'add',
initialValues,
@@ -37,91 +19,13 @@ const ChickinFormKandang = ({
initialValues: ProjectFlockKandang;
afterSubmit?: () => void;
}) => {
const router = useRouter();
const [chickinErrorMessage, setChickinErrorMessage] = useState('');
const [activeTabId, setActiveTabId] = useState<string>('formChickIn');
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}`
);
},
[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 afterSubmitFormChickin = () => {
setActiveTabId('logsChickIn');
afterSubmit && afterSubmit();
};
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 (
<div className='flex flex-col gap-4'>
<FormHeader
@@ -129,172 +33,91 @@ const ChickinFormKandang = ({
title='Chick In DOC'
backUrl={`/production/project-flock/chickin/add?projectFlockId=${initialValues?.project_flock?.id}`}
/>
<form
className='flex flex-col gap-4'
onReset={(e) => {
handleReset();
<Card
title='Informasi Kandang'
className={{
wrapper: 'w-full bg-white mt-4',
}}
onSubmit={formik.handleSubmit}
>
<Card
title='Informasi Kandang'
<Table<Kandang>
emptyContent={
<div className='w-full p-5 text-center'>
<span className='text-lg opacity-50'>
Informasi Kandang belum tersedia...
</span>
</div>
}
data={[initialValues?.kandang]}
columns={[
{
header: 'Area',
accessorFn: () => initialValues?.project_flock?.area.name || '-',
},
{
header: 'Lokasi',
accessorFn: () =>
initialValues?.project_flock?.location.name || '-',
},
{
header: 'Flock',
accessorFn: () => initialValues?.project_flock?.flock_name || '-',
},
{
header: 'Kandang',
accessorFn: (row) => row?.name || '-',
},
{
header: 'Kapasitas',
accessorFn: (row) =>
(row?.capacity && formatNumber(row?.capacity)) || '-',
},
{
header: 'Penanggung Jawab',
accessorFn: (row) => row?.pic?.name || '-',
},
]}
className={{
wrapper: 'w-full bg-white mt-4',
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',
}}
>
<Table<Kandang>
emptyContent={
<div className='w-full p-5 text-center'>
<span className='text-lg opacity-50'>
Informasi Kandang belum tersedia...
</span>
</div>
}
data={[initialValues?.kandang]}
columns={[
{
header: 'Area',
accessorFn: () =>
initialValues?.project_flock?.area.name || '-',
},
{
header: 'Lokasi',
accessorFn: () =>
initialValues?.project_flock?.location.name || '-',
},
{
header: 'Flock',
accessorFn: () =>
initialValues?.project_flock?.flock_name || '-',
},
{
header: 'Kandang',
accessorFn: (row) => row?.name || '-',
},
{
header: 'Kapasitas',
accessorFn: (row) =>
(row?.capacity && formatNumber(row?.capacity)) || '-',
},
{
header: 'Penanggung Jawab',
accessorFn: (row) => row?.pic?.name || '-',
},
]}
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-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',
}}
/>
</Card>
<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>
{JSON.stringify(formik.values)}
<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>
{JSON.stringify(formik.errors)}
</form>
/>
</Card>
<Tabs
className='bg-white p-2'
onTabChange={setActiveTabId}
activeTabId={activeTabId}
tabs={[
{
id: 'formChickIn',
label: 'Form Chick In',
content: (
<ChickinFormView
initialValues={initialValues}
formType={formType}
afterSubmit={afterSubmitFormChickin}
/>
),
},
{
content: (
<ChickinLogsView
initialValues={initialValues}
afterSubmit={afterSubmit}
/>
),
id: 'logsChickIn',
label: 'Riwayat Chick In',
},
]}
variant='lifted'
/>
</div>
);
};
@@ -573,7 +573,7 @@ const ProjectFlockTable = () => {
<ConfirmationModal
ref={confirmModal.ref}
type='success'
text={`Apakah anda yakin ingin reject data transfer ke laying ini (${selectedRowIds.length} data)?`}
text={`Apakah anda yakin ingin approve data Project Flock ini (${selectedRowIds.length} data)?`}
secondaryButton={{
text: 'Tidak',
}}
@@ -284,8 +284,8 @@ const ProjectFlockChickinDetail = ({
) {
case 'DISETUJUI':
return 'green';
case 'DITOLAK':
return 'red';
case 'PENGAJUAN':
return 'yellow';
default:
return 'gray';
}
@@ -688,7 +688,6 @@ const ProjectFlockForm = ({
</div>
)}
</div>
{JSON.stringify(formik.errors)}
</form>
{formType != 'add' && (
<div className='flex flex-row gap-2 mb-6'>
-10
View File
@@ -9,11 +9,6 @@ import {
Recording,
UpdateRecordingPayload,
} from '@/types/api/production/recording';
import {
Chickin,
CreateChickinPayload,
UpdateChickinPayload,
} from '@/types/api/production/chickin';
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
export const ProjectFlockApi = new BaseApiService<
@@ -31,8 +26,3 @@ export const RecordingApi = new BaseApiService<
CreateRecordingPayload,
UpdateRecordingPayload
>('/flock/recordings');
export const ChickinApi = new BaseApiService<
Chickin,
CreateChickinPayload,
UpdateChickinPayload
>('/production/chickins');
+12
View File
@@ -11,6 +11,7 @@ export type BaseProjectFlockKandang = {
kandang: Kandang;
project_flock: ProjectFlock;
available_qtys?: AvailableQty[];
chickins?: Chickin[];
approval: BaseApproval;
};
@@ -21,6 +22,17 @@ export type AvailableQty = {
note?: string;
};
export type Chickin = {
id: number;
project_flock_kandang_id: number;
chick_in_date: string;
product_warehouse_id: number;
product_warehouse: ProductWarehouse;
usage_qty: number;
pending_usage_qty: number;
note: string;
};
export type ProjectFlockKandang = BaseProjectFlockKandang;
export type LookupProjectFlockKandangPayload = {