refactor(FE): change project flock form, detail and chickin view using drawer

This commit is contained in:
randy-ar
2025-11-28 16:41:01 +07:00
parent 22ce1b1142
commit 892bb19dfd
11 changed files with 325 additions and 196 deletions
@@ -16,15 +16,11 @@ import { cn } from '@/lib/helper';
import { AreaApi, KandangApi, LocationApi } from '@/services/api/master-data';
import { ProjectFlockApi } from '@/services/api/production/project-flock';
import { useTableFilter } from '@/services/hooks/useTableFilter';
import { BaseApiResponse } from '@/types/api/api-general';
import { Kandang } from '@/types/api/master-data/kandang';
import {
ProjectFlockApprovalPayload,
ProjectFlock,
} from '@/types/api/production/project-flock';
import { ProjectFlock } from '@/types/api/production/project-flock';
import { Icon } from '@iconify/react';
import { CellContext, SortingState } from '@tanstack/react-table';
import { ChangeEventHandler, useState } from 'react';
import { ChangeEventHandler, useRef, useState } from 'react';
import toast from 'react-hot-toast';
import useSWR from 'swr';
@@ -266,10 +262,10 @@ const ProjectFlockTable = () => {
<div className='w-full flex flex-col justify-between items-end gap-2'>
<div className='flex flex-col sm:flex-row gap-3 w-full'>
<Button
href='/production/project-flock/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
href='/production/project-flock/add'
>
<Icon icon='ic:round-plus' width={24} height={24} />
Tambah
@@ -24,7 +24,6 @@ import {
UpdateProjectFlockFormSchema,
} from '@/components/pages/production/project-flock/form/ProjectFlockForm.schema';
import {
ProjectFlockApprovalPayload,
CreateProjectFlockPayload,
ProjectFlock,
} from '@/types/api/production/project-flock';
@@ -43,6 +42,7 @@ import ApprovalSteps, {
import { PROJECT_FLOCK_APPROVAL_LINE } from '@/config/approval-line';
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
import NumberInput from '@/components/input/NumberInput';
import Card from '@/components/Card';
interface ProjectFlockFormProps {
formType?: 'add' | 'edit' | 'detail';
@@ -259,6 +259,7 @@ const ProjectFlockForm = ({
if (isResponseSuccess(createProjectFlockRes)) {
toast.success(createProjectFlockRes?.message as string);
handleReset();
router.push('/production/project-flock');
}
if (isResponseError(createProjectFlockRes)) {
@@ -276,6 +277,7 @@ const ProjectFlockForm = ({
if (isResponseSuccess(updateProjectFlockRes)) {
toast.success(updateProjectFlockRes?.message as string);
handleReset();
router.push('/production/project-flock');
}
if (isResponseError(updateProjectFlockRes)) {
@@ -283,6 +285,15 @@ const ProjectFlockForm = ({
toast.error(updateProjectFlockRes?.message as string);
}
};
const handleReset = () => {
formik.resetForm();
setSelectedArea('');
setSelectedLocation('');
setDisabledLocation(true);
setOpenSelectKandangs(false);
setOptionsKandang([]);
formikSetValues(formikInitialValues);
};
// Formik InitialValue
const formikInitialValues = useMemo<ProjectFlockFormValues>(() => {
@@ -557,6 +568,16 @@ const ProjectFlockForm = ({
const inputPeriod =
(initialValues?.period ?? selectedPeriod == 0) ? 1 : selectedPeriod;
// expose method validate() ke parent
// TODO: Buat Store untuk kirim props formik.isValid ke parent (layout.tsx)
// useImperativeHandle(ref, () => ({
// validate() {
// formik.validateForm();
// const isValid = formik.isValid;
// return isValid;
// },
// }));
return (
<>
<section className='w-full'>
@@ -653,158 +674,139 @@ const ProjectFlockForm = ({
onSubmit={formik.handleSubmit}
onReset={formik.handleReset}
>
<div className='card bg-base-100 shadow w-full mb-6'>
<div className='card-body'>
<div className='card-title mb-4'>Informasi Umum</div>
<div className='grid sm:grid-cols-2 gap-4'>
<SelectInput
required
label='Area'
value={formik.values.area as OptionType}
onChange={areaChangeHandler}
options={optionsArea}
isLoading={isLoadingAreas}
isError={
formik.touched.area_id && Boolean(formik.errors.area_id)
}
errorMessage={formik.errors.area_id as string}
isClearable
isDisabled={formType === 'detail'}
/>
<SelectInput
required
label='Flock'
value={
formik.values.flock_name
? ({
label: formik.values.flock_name,
value: optionsFlock.find((flock) => {
return flock.label === formik.values.flock_name;
})?.value,
} as OptionType)
: undefined
}
onChange={(val) => {
optionChangeHandler(val, 'flock');
setSelectedFlock((val as OptionType)?.label);
formik.setFieldValue(
'flock_name',
(val as OptionType)?.label
);
}}
options={optionsFlock}
isLoading={isLoadingFlocks}
isError={
formik.touched.flock_name &&
Boolean(formik.errors.flock_name)
}
errorMessage={formik.errors.flock_name as string}
isClearable
isDisabled={formType === 'detail'}
/>
<SelectInput
required
label='Lokasi'
value={formik.values.location as OptionType}
onChange={locationChangeHandler}
options={
selectedArea != '' || initialValues?.area?.id
? optionsLocation
: []
}
isLoading={isLoadingLocations}
isError={
formik.touched.location_id &&
Boolean(formik.errors.location_id)
}
errorMessage={formik.errors.location_id as string}
isClearable
isDisabled={formType === 'detail' || disabledLocation}
/>
<SelectInput
required
label='FCR'
value={formik.values.fcr as OptionType}
onChange={(val) => {
optionChangeHandler(val, 'fcr');
}}
options={optionsFcr}
isLoading={isLoadingFcrs}
isError={
formik.touched.fcr_id && Boolean(formik.errors.fcr_id)
}
errorMessage={formik.errors.fcr_id as string}
isClearable
isDisabled={formType === 'detail'}
/>
<SelectInput
required
label='Kategori'
value={formik.values.category_option as OptionType}
onChange={categoryChangeHandler}
options={FLOCK_CATEGORY_OPTIONS}
isError={
formik.touched.category && Boolean(formik.errors.category)
}
errorMessage={formik.errors.category as string}
isClearable
isDisabled={formType === 'detail'}
/>
<NumberInput
name='period'
label='Periode'
disabled
readOnly
placeholder='Period'
value={selectedLocation ? inputPeriod : ''}
/>
</div>
</div>
</div>
<div className='card bg-base-100 shadow w-full'>
<div className='card-body'>
<Collapse
title={
<div className='card-actions justify-between w-full'>
<div className='card-title'>Pilih Kandang</div>
<Button
variant='link'
className={`text-primary rotate-${
openSelectKandangs ? '180' : '0'
} transition-transform hover:text-inherit me-3`}
>
<Icon
icon='material-symbols:keyboard-arrow-down'
width={24}
height={24}
/>
</Button>
</div>
<Card
title='Informasi Umum'
variant='bordered'
className={{
wrapper: 'w-full mb-4',
}}
>
<div className='grid sm:grid-cols-2 gap-4'>
<SelectInput
required
label='Area'
value={formik.values.area as OptionType}
onChange={areaChangeHandler}
options={optionsArea}
isLoading={isLoadingAreas}
isError={
formik.touched.area_id && Boolean(formik.errors.area_id)
}
className='sm:w-full'
titleClassName='w-full p-0!'
onOpenChange={setOpenSelectKandangs}
open={openSelectKandangs}
>
<div className='overflow-x-auto'>
{isLoadingKandang && (
<span className='loading loading-dots loading-xl'></span>
)}
<ProjectFlockKandangTable
listPeriods={
isResponseSuccess(periodFlocks) ? periodFlocks.data : []
}
listKandang={optionsKandang}
rowSelection={rowSelection}
setRowSelection={setRowSelection}
selectedIds={formik.values.kandang_ids}
formType={formType}
initialValues={initialValues}
/>
</div>
</Collapse>
errorMessage={formik.errors.area_id as string}
isClearable
isDisabled={formType === 'detail'}
/>
<SelectInput
required
label='Flock'
value={
formik.values.flock_name
? ({
label: formik.values.flock_name,
value: optionsFlock.find((flock) => {
return flock.label === formik.values.flock_name;
})?.value,
} as OptionType)
: undefined
}
onChange={(val) => {
optionChangeHandler(val, 'flock');
setSelectedFlock((val as OptionType)?.label);
formik.setFieldValue(
'flock_name',
(val as OptionType)?.label
);
}}
options={optionsFlock}
isLoading={isLoadingFlocks}
isError={
formik.touched.flock_name && Boolean(formik.errors.flock_name)
}
errorMessage={formik.errors.flock_name as string}
isClearable
isDisabled={formType === 'detail'}
/>
<SelectInput
required
label='Lokasi'
value={formik.values.location as OptionType}
onChange={locationChangeHandler}
options={
selectedArea != '' || initialValues?.area?.id
? optionsLocation
: []
}
isLoading={isLoadingLocations}
isError={
formik.touched.location_id &&
Boolean(formik.errors.location_id)
}
errorMessage={formik.errors.location_id as string}
isClearable
isDisabled={formType === 'detail' || disabledLocation}
/>
<SelectInput
required
label='FCR'
value={formik.values.fcr as OptionType}
onChange={(val) => {
optionChangeHandler(val, 'fcr');
}}
options={optionsFcr}
isLoading={isLoadingFcrs}
isError={formik.touched.fcr_id && Boolean(formik.errors.fcr_id)}
errorMessage={formik.errors.fcr_id as string}
isClearable
isDisabled={formType === 'detail'}
/>
<SelectInput
required
label='Kategori'
value={formik.values.category_option as OptionType}
onChange={categoryChangeHandler}
options={FLOCK_CATEGORY_OPTIONS}
isError={
formik.touched.category && Boolean(formik.errors.category)
}
errorMessage={formik.errors.category as string}
isClearable
isDisabled={formType === 'detail'}
/>
<NumberInput
name='period'
label='Periode'
disabled
readOnly
placeholder='Period'
value={selectedLocation ? inputPeriod : ''}
/>
</div>
</div>
</Card>
<Card
collapsible
title='Pilih Kandang'
variant='bordered'
className={{
wrapper: 'w-full',
}}
>
<div className='overflow-x-auto duration-300 ease-in-out'>
{isLoadingKandang && (
<span className='loading loading-dots loading-xl'></span>
)}
<ProjectFlockKandangTable
listPeriods={
isResponseSuccess(periodFlocks) ? periodFlocks.data : []
}
listKandang={optionsKandang}
rowSelection={rowSelection}
setRowSelection={setRowSelection}
selectedIds={formik.values.kandang_ids}
formType={formType}
initialValues={initialValues}
/>
</div>
</Card>
<div className='flex flex-row justify-center gap-2 flex-wrap my-6'>
{formType !== 'detail' && (
@@ -912,4 +914,6 @@ const ProjectFlockForm = ({
);
};
ProjectFlockForm.displayName = 'ProjectFlockForm';
export default ProjectFlockForm;
@@ -144,8 +144,6 @@ const ProjectFlockKandangTable = ({
accessorFn: (row) => row.location?.name,
header: 'Periode',
cell: (props) => {
console.log('listPeriods');
console.log(listPeriods);
const period =
listPeriods.length > 0
? listPeriods.find((p) => p.id == props.row.original.id)