refactor(FE-106-91-339-238): Slicing UI Chickin DOC Refactored

This commit is contained in:
randy-ar
2025-11-04 13:24:10 +07:00
parent 219cbedbcd
commit d8637923bd
24 changed files with 780 additions and 709 deletions
@@ -87,7 +87,7 @@ const ChickinTable = () => {
<div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<Button
href='/production/chickin/add?projectFlockId=1'
href='/production/project-flock/chickin/add?projectFlockId=1'
variant='outline'
color='primary'
className='w-full sm:w-fit'
@@ -260,14 +260,14 @@ const ChickinTable = () => {
/>
</Button>
</div>
<ChickinForm
{/* <ChickinForm
initialValues={selectedChickin}
formType='edit'
afterSubmit={() => {
refreshChickins();
chickinModal.closeModal();
}}
/>
/> */}
</Modal>
</>
);
@@ -287,7 +287,7 @@ const RowOptionsMenu = ({
return (
<RowOptionsMenuWrapper type={type}>
<Button
href={`/production/chickin/detail?chickinId=${props.row.original.id}`}
href={`/production/project-flock/chickin/detail?chickinId=${props.row.original.id}`}
variant='ghost'
color='primary'
className='justify-start text-sm'
@@ -1,13 +1,3 @@
import * as Yup from 'yup';
export const ChickinFormSchema = Yup.object({
chick_in_date: Yup.string().required('Tanggal masuk wajib diisi!'),
note: Yup.string().required('Catatan wajib diisi!'),
quantity: Yup.number()
.min(1, 'Jumlah wajib diisi!')
.required('Jumlah wajib diisi!'),
});
export type ChickinFormValues = Yup.InferType<typeof ChickinFormSchema>;
export const UpdateChickinFormSchema = ChickinFormSchema;
import { Product } from '@/types/api/master-data/product';
import { Supplier } from '@/types/api/master-data/supplier';
@@ -1,220 +1,227 @@
'use client';
import Button from '@/components/Button';
import {
Chickin,
CreateChickinPayload,
UpdateChickinPayload,
} from '@/types/api/production/chickin';
import {
ChickinFormSchema,
ChickinFormValues,
UpdateChickinFormSchema,
} from '@/components/pages/production/chickin/form/ChickinForm.schema';
import { use, useCallback, useEffect, useMemo, useState } from 'react';
import { useFormik } from 'formik';
import { ChickinApi } from '@/services/api/production';
import Card from '@/components/Card';
import { FormHeader } from '@/components/helper/form/FormHeader';
import DateInput from '@/components/input/DateInput';
import { isResponseError } from '@/lib/api-helper';
import toast from 'react-hot-toast';
import { Icon } from '@iconify/react';
import TextArea from '@/components/input/TextArea';
import TextInput from '@/components/input/TextInput';
import FileInput from '@/components/input/FileInput';
import NumberInput from '@/components/input/NumberInput';
import SelectInput 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 { useRouter } from 'next/navigation';
interface ChickinFormProps {
formType?: 'add' | 'detail' | 'edit';
initialValues?: Chickin;
afterSubmit?: () => void;
}
const ChickinForm = ({
const ChickinFormKandang = ({
formType = 'add',
initialValues,
afterSubmit,
}: ChickinFormProps) => {
// Helper Function
const formatDateForInput = (dateString?: string): string => {
if (!dateString) return '';
return new Date(dateString).toISOString().split('T')[0];
};
// State
const [chickinFormErrorMessage, setChickinFormErrorMessage] = useState('');
// Initial Value
const formikInitialValue = useMemo<ChickinFormValues>(() => {
return {
chick_in_date: formatDateForInput(initialValues?.chick_in_date) ?? '',
note: initialValues?.note ?? '',
quantity:
initialValues?.quantity ??
initialValues?.project_flock_kandang?.available_quantity ??
0,
};
}, [initialValues]);
// Handle Submit Function
const handleCreate = useCallback(
async (
payload: CreateChickinPayload,
afterSubmit: (() => void) | undefined
) => {
const res = await ChickinApi.create(payload);
if (isResponseError(res)) {
setChickinFormErrorMessage(res.message);
return;
}
toast.success(res?.message as string);
afterSubmit?.();
},
[]
);
const handleUpdate = useCallback(
async (
payload: UpdateChickinPayload,
afterSubmit: (() => void) | undefined
) => {
const res = await ChickinApi.update(payload.id, payload);
if (isResponseError(res)) {
setChickinFormErrorMessage(res.message);
return;
}
toast.success(res?.message as string);
afterSubmit?.();
},
[]
);
// Formik
const formik = useFormik<ChickinFormValues>({
initialValues: formikInitialValue,
enableReinitialize: true,
validationSchema:
formType === 'edit' ? UpdateChickinFormSchema : ChickinFormSchema,
onSubmit: async (values) => {
// reset error message
setChickinFormErrorMessage('');
if (
initialValues?.project_flock_kandang?.id == undefined ||
(formType == 'edit' && initialValues?.id == undefined)
) {
return;
}
// create payload
const payload = {
chick_in_date: values.chick_in_date,
project_flock_kandang_id: initialValues?.project_flock_kandang?.id,
note: values.note,
quantity: values.quantity,
id: initialValues.id ?? 0,
};
// cek type form yang disubmit
console.log(formType);
switch (formType) {
case 'add':
handleCreate(payload, afterSubmit);
break;
case 'edit':
handleUpdate(payload, afterSubmit);
break;
default:
break;
}
},
});
// Initialize Formik
const { setValues: formikSetValues } = formik;
useEffect(() => {
formikSetValues(formikInitialValue);
}, [formikSetValues, formikInitialValue]);
}: {
formType?: 'add' | 'detail' | 'edit';
initialValues: ProjectFlockKandang;
afterSubmit?: () => void;
}) => {
const router = useRouter();
return (
<>
<form
className='min-h-48 flex flex-col gap-4'
onSubmit={formik.handleSubmit}
onReset={formik.handleReset}
<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}`}
/>
<Card
title='Informasi Kandang'
className={{
wrapper: 'w-full bg-white mt-4',
}}
>
<DateInput
value={formik.values.chick_in_date}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
name='chick_in_date'
label='Tanggal Chickin'
required
isError={
formik.touched.chick_in_date && Boolean(formik.errors.chick_in_date)
<Table<Kandang>
emptyContent={
<div className='w-full p-5 text-center'>
<span className='text-lg opacity-50'>
Informasi Kandang belum tersedia...
</span>
</div>
}
errorMessage={formik.errors.chick_in_date}
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',
}}
/>
<NumberInput
value={formik.values.quantity}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
name='quantity'
label='Jumlah (Ekor)'
required
isError={
(formik.touched.quantity && Boolean(formik.errors.quantity)) ||
formik.values.quantity == 0
</Card>
<Card
title='Informasi Chick In DOC'
className={{
wrapper: 'w-full bg-white',
}}
>
<Table<AvailableQty>
data={initialValues.available_qtys || []}
columns={[
{
accessorFn: (row) => row.chick_in_date,
header: 'Tanggal Chick In',
cell(props) {
return (
<DateInput
name='chick_in_date[]'
value={props.row.original.chick_in_date}
onChange={(e) => {
props.row.original.chick_in_date = e.target.value;
}}
/>
);
},
},
{
accessorFn: (row) => row.po_number,
header: 'No. Surat Jalan',
cell(props) {
return (
<TextInput
name='po_number[]'
value={props.row.original.po_number}
onChange={(e) => {
props.row.original.po_number = e.target.value;
}}
/>
);
},
},
{
header: 'Dokumen Surat Jalan',
cell(props) {
return (
<FileInput
name='document_path[]'
onChange={(e) => {
props.row.original.document_path = e.target.value;
}}
/>
);
},
},
{
accessorFn: (row) => row.supplier?.name,
header: 'Supplier',
cell(props) {
return (
<SelectInput
value={
props.row.original.supplier?.name &&
props.row.original.supplier?.id
? {
label: props.row.original.supplier.name,
value: props.row.original.supplier.id,
}
: undefined
}
options={[]}
isDisabled
/>
);
},
},
{
accessorFn: (row) => row.product_warehouse.product.name,
header: 'Produk',
cell(props) {
return (
<SelectInput
value={{
label: props.row.original.product_warehouse.product.name,
value: props.row.original.product_warehouse.product.id,
}}
options={[]}
isDisabled
/>
);
},
},
{
accessorFn: (row) => row.product_warehouse.quantity,
header: 'Jumlah (ekor)',
cell(props) {
return (
<NumberInput
name='qty[]'
value={props.row.original.product_warehouse.quantity}
/>
);
},
},
]}
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>
}
errorMessage={
formik.values.quantity == 0
? 'Masukan Persediaan Day Old Chick terlebih dahulu.'
: formik.errors.quantity
}
readOnly
/>
<TextArea
required
label='Catatan'
name='note'
placeholder='Masukan catatan chickin'
value={formik.values.note}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
isError={formik.touched.note && Boolean(formik.errors.note)}
errorMessage={formik.errors.note}
/>
{initialValues?.project_flock_kandang?.id == undefined && (
<p className='text-error'>Project Flock Kandang tidak ditemukan.</p>
)}
{chickinFormErrorMessage && (
<div
role='alert'
className='alert alert-error'
onClick={() => {
setChickinFormErrorMessage('');
}}
>
<Icon icon='mdi:times' />
<span>{chickinFormErrorMessage}</span>
</div>
)}
<div className='flex justify-center mt-auto gap-2'>
<Button color='warning' type='reset'>
Reset
</Button>
<Button
type='submit'
isLoading={formik.isSubmitting}
disabled={
!formik.isValid ||
formik.isSubmitting ||
!initialValues?.project_flock_kandang?.id
}
>
Submit
</Button>
</div>
</form>
</>
</Card>
<div className='flex flex-row justify-center gap-3'>
<Button type='reset' color='warning'>
Reset
</Button>
<Button type='submit' color='primary'>
Submit
</Button>
</div>
</div>
);
};
export default ChickinForm;
export default ChickinFormKandang;