From 90dd26064d964f75e1f9e14fb934cb918590b81a Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 6 Nov 2025 15:12:04 +0700 Subject: [PATCH] feat(FE-170,175): enhance GradingForm with additional recording details and improve UI for grading information --- .../recording/grading/form/GradingForm.tsx | 216 ++++++++++++++---- 1 file changed, 166 insertions(+), 50 deletions(-) diff --git a/src/components/pages/production/recording/grading/form/GradingForm.tsx b/src/components/pages/production/recording/grading/form/GradingForm.tsx index b986a555..5cb371ad 100644 --- a/src/components/pages/production/recording/grading/form/GradingForm.tsx +++ b/src/components/pages/production/recording/grading/form/GradingForm.tsx @@ -4,12 +4,16 @@ import { useMemo, useState, useEffect, useCallback } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { useFormik } from 'formik'; import { Icon } from '@iconify/react'; -import Button from '@/components/Button'; +import Button from '@/components/Button'; import NumberInput from '@/components/input/NumberInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput'; import CheckboxInput from '@/components/input/CheckboxInput'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; +import Card from '@/components/Card'; +import StepItem from '@/components/steps/StepItem'; +import Badge from '@/components/Badge'; + import { CreateGradingPayload, UpdateGradingPayload, @@ -17,45 +21,53 @@ import { GradingEgg, Recording, } from '@/types/api/production/recording'; +import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; import { type FormStepStatus, type BaseApiResponse, } from '@/types/api/api-general'; + import { RecordingGradingFormSchema, RecordingGradingFormValues, UpdateRecordingGradingFormSchema, getRecordingGradingFormInitialValues, } from '../../form/RecordingForm.schema'; -import { cn } from '@/lib/helper'; + +import { cn, formatDate } from '@/lib/helper'; import toast from 'react-hot-toast'; -import { RecordingApi } from '@/services/api/production'; import { isResponseError } from '@/lib/api-helper'; + +import { + RecordingApi, + ProjectFlockKandangApi, +} from '@/services/api/production'; + import { useModal } from '@/components/Modal'; import useSWR from 'swr'; -import Card from '@/components/Card'; -import StepItem from '@/components/steps/StepItem'; - +// INTERFACES & PROPS interface GradingFormProps { type?: 'add' | 'edit' | 'detail'; initialValues?: RecordingEgg & { grading_eggs?: GradingEgg[] }; } const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { + // HOOKS & ROUTER const router = useRouter(); const searchParams = useSearchParams(); const recordingId = searchParams.get('recording_id'); + + // STATE MANAGEMENT const [selectedGradingItems, setSelectedGradingItems] = useState( [] ); - const [formSteps, setFormSteps] = useState(null); const [gradingFormErrorMessage, setGradingFormErrorMessage] = useState(''); const [isDeleteLoading, setIsDeleteLoading] = useState(false); const deleteModal = useModal(); - // ===== API DATA FETCHING ===== + // API DATA FETCHING const recordingUrl = useMemo(() => { const recordingIdToUse = recordingId; if (!recordingIdToUse) return null; @@ -67,12 +79,27 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { recordingUrl ? RecordingApi.getAllFetcher : null ); - // ===== DATA PROCESSING ===== + // DATA PROCESSING const recording = recordingData?.status === 'success' ? (recordingData.data as unknown as Recording) : undefined; + const projectFlockKandangUrl = useMemo(() => { + if (!recording?.project_flock_kandang_id) return null; + return `${ProjectFlockKandangApi.basePath}/${recording.project_flock_kandang_id}`; + }, [recording?.project_flock_kandang_id]); + + const { data: projectFlockKandangData } = useSWR( + projectFlockKandangUrl, + projectFlockKandangUrl ? ProjectFlockKandangApi.getAllFetcher : null + ); + + const projectFlockKandang = + projectFlockKandangData?.status === 'success' + ? (projectFlockKandangData.data as unknown as ProjectFlockKandang) + : undefined; + const konsumsiBaikEggData = useMemo(() => { if (!recording?.eggs) return null; @@ -87,7 +114,7 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { const totalKonsumsiBaikEggs = konsumsiBaikEggData?.qty || 0; - // ===== FORM HANDLERS ===== + // FORM HANDLERS const createGradingHandler = useCallback( async (payload: CreateGradingPayload) => { const res = (await RecordingApi.createGrading(payload)) as @@ -150,6 +177,7 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { } }, [deleteModal, initialValues?.id, router]); + // FORMIK SETUP const formikInitialValues = useMemo(() => { let recordingEggId: number | undefined = initialValues?.id; @@ -208,6 +236,7 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { const isGradingExceedsAvailable = currentGradingTotal > totalKonsumsiBaikEggs; + // GRADING HANDLERS const addGrading = () => { let recordingEggId: number | undefined = initialValues?.id; @@ -257,6 +286,7 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { setSelectedGradingItems([]); }; + // VALIDATION HELPERS const isRepeaterInputError = ( arrayName: 'eggs_grading', column: string, @@ -290,7 +320,7 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { }; }; - // ===== EFFECTS ===== + // EFFECTS useEffect(() => { const steps: FormStepStatus[] = [ { @@ -307,7 +337,6 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { setFormSteps(steps); }, []); - // Show toast when grading exceeds available eggs useEffect(() => { if (isGradingExceedsAvailable && currentGradingTotal > 0) { toast.error( @@ -334,7 +363,7 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { { recording_egg_id: recordingEggId, grade: '', qty: '' }, ]); } - }, [formik]); + }, []); return ( <> @@ -354,6 +383,7 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { {type === 'detail' && 'Detail Grading'} + {/* Project Flock Info Card */}
{/* Form Steps */} @@ -417,12 +447,81 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { > {/* Basic Info Card */} +
+ {/* Recording Info */} +
+ Recording ID +

#{recording?.id || '-'}

+
+
+ Lokasi +

+ {projectFlockKandang?.project_flock?.location?.name || '-'} +

+
+
+ Project Flock +

+ {projectFlockKandang?.project_flock?.flock_name || '-'} +

+
+
+ Kandang +

+ {projectFlockKandang?.kandang?.name || '-'} +

+
+
+ Tanggal Recording +

+ {recording + ? formatDate(recording.record_datetime, 'DD MMMM YYYY') + : '-'} +

+
+
+ Hari +

Hari ke-{recording?.day || '-'}

+
+
+ Kategori +

+ + {recording?.project_flock_category || '-'} + +

+
+
+ Periode +

+ + Periode {projectFlockKandang?.project_flock?.period || '-'} + +

+
+
+
{ : 'grid grid-cols-1 md:grid-cols-2 gap-6' } > - {/* Recording Egg ID */} -
-
-
-

Recording ID

-

- #{type === 'detail' && initialValues ? - initialValues.id : - (formik.values.eggs_grading?.[0]?.recording_egg_id || '-') - } -

-
+ {/* Additional Recording Info */} +
+
{ className='text-blue-600' />
+ + Detail Recording + +
+
+
+

Area

+

+ {projectFlockKandang?.project_flock?.area?.name || '-'} +

+
+
+

Status Kandang

+

+ {projectFlockKandang?.kandang?.status || '-'} +

+
{/* Total Telur Konsumsi Baik Info */} -
+

@@ -466,28 +575,30 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => {

- {totalKonsumsiBaikEggs} + {totalKonsumsiBaikEggs}{' '} + + telur +

- - telur -
-
+
@@ -497,9 +608,13 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => {
Total yang digrading: - + {currentGradingTotal} / {totalKonsumsiBaikEggs}
@@ -511,7 +626,7 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { : 'bg-green-500' }`} style={{ - width: `${Math.min((currentGradingTotal / totalKonsumsiBaikEggs) * 100, 100)}%` + width: `${Math.min((currentGradingTotal / totalKonsumsiBaikEggs) * 100, 100)}%`, }} />
@@ -815,6 +930,7 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { + {/* ===== MODALS ===== */} {type !== 'add' && ( <>