feat(FE-170,174): refactor GradingForm to use grading form handlers and remove approval logic

This commit is contained in:
rstubryan
2025-10-31 17:26:56 +07:00
parent 9495742cb7
commit 19afb80597
5 changed files with 43 additions and 191 deletions
@@ -35,7 +35,7 @@ const AddGrading = () => {
{(!recordingId || {(!recordingId ||
recordingId === 'new' || recordingId === 'new' ||
(!isLoadingRecording && recording && isResponseSuccess(recording))) && ( (!isLoadingRecording && recording && isResponseSuccess(recording))) && (
<GradingForm type='add' recordingData={isResponseSuccess(recording) ? recording.data : undefined} /> <GradingForm type='add' initialValues={isResponseSuccess(recording) ? recording.data?.recording_eggs?.[0] : undefined} />
)} )}
</div> </div>
); );
@@ -41,8 +41,9 @@ const EditGrading = () => {
{!isLoadingRecording && recording && isResponseSuccess(recording) && ( {!isLoadingRecording && recording && isResponseSuccess(recording) && (
<GradingForm <GradingForm
type='edit' type='edit'
initialValues={recording.data.recording_eggs?.find(egg => egg.id === parseInt(gradingId || '0'))} initialValues={recording.data.recording_eggs?.find(
recordingData={recording.data} (egg) => egg.id === parseInt(gradingId || '0')
)}
/> />
)} )}
</div> </div>
@@ -40,12 +40,13 @@ const DetailGrading = () => {
{!isLoadingGrading && grading && isResponseSuccess(grading) && ( {!isLoadingGrading && grading && isResponseSuccess(grading) && (
<GradingForm <GradingForm
type='detail' type='detail'
initialValues={grading.data.recording_eggs?.find(egg => egg.id === parseInt(gradingId))} initialValues={grading.data.recording_eggs?.find(
recordingData={grading.data} (egg) => egg.id === parseInt(gradingId)
)}
/> />
)} )}
</div> </div>
); );
}; };
export default DetailGrading; export default DetailGrading;
@@ -15,39 +15,31 @@ import { FormHeader } from '@/components/helper/form/FormHeader';
import { RecordingApi } from '@/services/api/production'; import { RecordingApi } from '@/services/api/production';
import { import {
CreateGradingPayload, CreateGradingPayload,
Recording, UpdateGradingPayload,
RecordingEgg, RecordingEgg,
GradingEgg, GradingEgg,
} from '@/types/api/production/recording'; } from '@/types/api/production/recording';
import { type BaseApiResponse, FormStepStatus } from '@/types/api/api-general'; import { type FormStepStatus } from '@/types/api/api-general';
import { import {
RecordingGradingFormSchema, RecordingGradingFormSchema,
RecordingGradingFormValues, RecordingGradingFormValues,
UpdateRecordingGradingFormSchema, UpdateRecordingGradingFormSchema,
getRecordingGradingFormInitialValues, getRecordingGradingFormInitialValues,
} from '../../form/RecordingForm.schema'; } from '../../form/RecordingForm.schema';
import { useRecordingFormHandlers } from '../../form/useRecordingFormHandlers';
import { ProductWarehouseApi } from '@/services/api/inventory';
import { isResponseSuccess } from '@/lib/api-helper';
import { cn } from '@/lib/helper'; import { cn } from '@/lib/helper';
import { useModal } from '@/components/Modal';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import Card from '@/components/Card'; import Card from '@/components/Card';
import Badge from '@/components/Badge';
import StepItem from '@/components/steps/StepItem'; import StepItem from '@/components/steps/StepItem';
import { useModal } from '@/components/Modal';
import { useGradingFormHandlers } from './useGradingFormHandlers';
interface GradingFormProps { interface GradingFormProps {
type?: 'add' | 'edit' | 'detail'; type?: 'add' | 'edit' | 'detail';
initialValues?: RecordingEgg & { grading_eggs?: GradingEgg[] }; initialValues?: RecordingEgg & { grading_eggs?: GradingEgg[] };
recordingData?: Recording;
} }
const GradingForm = ({ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => {
type = 'add',
initialValues,
recordingData,
}: GradingFormProps) => {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const recordingId = searchParams.get('recording_id'); const recordingId = searchParams.get('recording_id');
@@ -55,64 +47,45 @@ const GradingForm = ({
[] []
); );
const [isApproveLoading, setIsApproveLoading] = useState(false);
const [isRejectLoading, setIsRejectLoading] = useState(false);
const [formSteps, setFormSteps] = useState<FormStepStatus[] | null>(null); const [formSteps, setFormSteps] = useState<FormStepStatus[] | null>(null);
const approveModal = useModal();
const rejectModal = useModal();
// ===== API DATA FETCHING ===== // ===== API DATA FETCHING =====
const eggProductsUrl = useMemo(() => { // Fetch existing gradings for the recording egg to show recorded data
if (!recordingData?.project_flock_kandangs_id) return null; // Optional: Fetch existing gradings if needed for display purposes
const params = new URLSearchParams({ // const existingGradingsUrl = useMemo(() => {
search: '', // const recordingEggIdToUse = recordingId || initialValues?.id?.toString();
location_id: recordingData.project_flock_kandangs_id.toString(), // if (!recordingEggIdToUse) return null;
}); // return `${RecordingApi.basePath}/gradings?recording_egg_id=${recordingEggIdToUse}`;
return `${ProductWarehouseApi.basePath}?${params.toString()}`; // }, [recordingId, initialValues]);
}, [recordingData]);
const { data: eggProductsData, isLoading: isLoadingEggProducts } = useSWR( // const { data: existingGradings } = useSWR(
eggProductsUrl, // existingGradingsUrl,
ProductWarehouseApi.getAllFetcher // existingGradingsUrl ? RecordingApi.getAllFetcher : null
); // );
// ===== DATA PROCESSING ===== // ===== DATA PROCESSING =====
const eggProducts = useMemo(() => { // No data processing needed - grading form only needs recording_egg_id and grading data
const options: OptionType[] = [];
if (isResponseSuccess(eggProductsData)) {
eggProductsData.data.forEach((product) => {
const productName = product.product.name;
if (
productName.toLowerCase().includes('telur') ||
productName.toLowerCase().includes('egg')
) {
options.push({
value: product.id,
label: product.product.name,
});
}
});
}
return options;
}, [eggProductsData]);
// ===== FORM HANDLERS ===== // ===== FORM HANDLERS =====
const { const {
deleteModal, deleteModal,
recordingFormErrorMessage, recordingFormErrorMessage,
isDeleteLoading, isDeleteLoading,
createRecordingHandler, createGradingHandler,
updateRecordingHandler, updateGradingHandler,
deleteRecordingClickHandler, deleteRecordingClickHandler,
confirmationModalDeleteClickHandler, confirmationModalDeleteClickHandler,
} = useRecordingFormHandlers(initialValues?.id); } = useGradingFormHandlers(initialValues?.id);
const formikInitialValues = useMemo(() => { const formikInitialValues = useMemo(() => {
let recordingEggId: number | undefined = initialValues?.id;
if (!recordingEggId) {
recordingEggId = parseInt(recordingId || '0') || 0;
}
return getRecordingGradingFormInitialValues({ return getRecordingGradingFormInitialValues({
recording_egg_id: initialValues?.id || parseInt(recordingId || '0') || 0, recording_egg_id: recordingEggId,
eggs_grading: eggs_grading:
initialValues?.grading_eggs?.map((grading: GradingEgg) => ({ initialValues?.grading_eggs?.map((grading: GradingEgg) => ({
grade: grading.grade, grade: grading.grade,
@@ -141,74 +114,19 @@ const GradingForm = ({
switch (type) { switch (type) {
case 'add': case 'add':
await createRecordingHandler(gradingPayload as CreateGradingPayload); await createGradingHandler(gradingPayload as CreateGradingPayload);
break; break;
case 'edit': case 'edit':
await updateRecordingHandler( await updateGradingHandler(
initialValues?.id as number, initialValues?.id as number,
gradingPayload as CreateGradingPayload gradingPayload as UpdateGradingPayload
); );
break; break;
} }
}, },
}); });
// ===== EVENT HANDLERS ===== // Grading form doesn't need approval/reject handlers
const approveHandler = async () => {
setIsApproveLoading(true);
const approveResponse = await RecordingApi.customRequest<
BaseApiResponse<Recording[]>
>('approvals', {
method: 'POST',
payload: {
action: 'APPROVED',
approvable_ids: [initialValues?.id as number],
notes: 'Approved via Grading Form',
},
});
if (isResponseSuccess(approveResponse)) {
toast.success('Grading berhasil disetujui!');
approveModal.closeModal();
router.push('/production/recording');
} else {
toast.error(
(approveResponse?.message as string) || 'Gagal menyetujui grading'
);
approveModal.closeModal();
}
setIsApproveLoading(false);
};
const rejectHandler = async () => {
setIsRejectLoading(true);
const rejectResponse = await RecordingApi.customRequest<
BaseApiResponse<Recording[]>
>('approvals', {
method: 'POST',
payload: {
action: 'REJECTED',
approvable_ids: [initialValues?.id as number],
notes: 'Rejected via Grading Form',
},
});
if (isResponseSuccess(rejectResponse)) {
toast.success('Grading berhasil ditolak!');
rejectModal.closeModal();
router.push('/production/recording');
} else {
toast.error(
(rejectResponse?.message as string) || 'Gagal menolak grading'
);
rejectModal.closeModal();
}
setIsRejectLoading(false);
};
// Grading Handlers // Grading Handlers
const addGrading = () => { const addGrading = () => {
@@ -309,6 +227,8 @@ const GradingForm = ({
} }
}, [formik]); }, [formik]);
// Grading form doesn't need loading state since it only depends on recording_egg_id
return ( return (
<> <>
<section className='w-full'> <section className='w-full'>
@@ -394,9 +314,9 @@ const GradingForm = ({
: 'grid grid-cols-3 gap-4' : 'grid grid-cols-3 gap-4'
} }
> >
{type === 'detail' && recordingData ? ( {type === 'detail' && initialValues ? (
<> <>
<span>Recording ID</span>#{recordingData.id} <span>Recording Egg ID</span>#{initialValues.id}
</> </>
) : ( ) : (
<> <>
@@ -641,40 +561,6 @@ const GradingForm = ({
Edit Edit
</Button> </Button>
)} )}
{type === 'detail' && (
<>
<Button
type='button'
color='success'
onClick={() => approveModal.openModal()}
className='px-4'
isLoading={isApproveLoading}
>
<Icon
icon='material-symbols:check-circle-outline'
width={24}
height={24}
className='justify-start text-sm'
/>
Approve
</Button>
<Button
type='button'
color='error'
onClick={() => rejectModal.openModal()}
className='px-4'
isLoading={isRejectLoading}
>
<Icon
icon='material-symbols:cancel-outline'
width={24}
height={24}
className='justify-start text-sm'
/>
Reject
</Button>
</>
)}
</div> </div>
)} )}
{type !== 'detail' && ( {type !== 'detail' && (
@@ -735,42 +621,6 @@ const GradingForm = ({
onClick: confirmationModalDeleteClickHandler, onClick: confirmationModalDeleteClickHandler,
}} }}
/> />
{/* Approve Confirmation Modal */}
{type === 'detail' && (
<ConfirmationModal
ref={approveModal.ref}
type='success'
text='Apakah anda yakin ingin menyetujui data Grading ini?'
secondaryButton={{
text: 'Tidak',
}}
primaryButton={{
text: 'Ya',
color: 'success',
isLoading: isApproveLoading,
onClick: approveHandler,
}}
/>
)}
{/* Reject Confirmation Modal */}
{type === 'detail' && (
<ConfirmationModal
ref={rejectModal.ref}
type='error'
text='Apakah anda yakin ingin menolak data Grading ini?'
secondaryButton={{
text: 'Tidak',
}}
primaryButton={{
text: 'Ya',
color: 'error',
isLoading: isRejectLoading,
onClick: rejectHandler,
}}
/>
)}
</> </>
)} )}
</> </>