From b24fb548566af7df32f847d8ec945bd7b0d7aa44 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Sun, 28 Dec 2025 11:50:14 +0700 Subject: [PATCH] refactor(FE-316): Update uniformity payload/fields and file handling --- .../pages/uniformity/UniformityTable.tsx | 12 +-- .../uniformity/form/UniformityForm.schema.ts | 12 ++- .../pages/uniformity/form/UniformityForm.tsx | 43 +++++++---- .../uniformity/form/UniformityPreviewForm.tsx | 4 +- .../uniformity/form/UniformityResultForm.tsx | 12 +-- src/services/api/uniformity.ts | 13 ++-- src/stores/uniformity/uniformity.store.ts | 3 +- src/types/api/uniformity/uniformity.d.ts | 77 ++++++++++++++++--- 8 files changed, 132 insertions(+), 44 deletions(-) diff --git a/src/components/pages/uniformity/UniformityTable.tsx b/src/components/pages/uniformity/UniformityTable.tsx index c6935bf8..fee811d0 100644 --- a/src/components/pages/uniformity/UniformityTable.tsx +++ b/src/components/pages/uniformity/UniformityTable.tsx @@ -269,23 +269,23 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => { { accessorKey: 'location.name', header: 'Lokasi', - cell: (props) => props.row.original.location.name || '-', + cell: (props) => props.row.original.location_name || '-', }, { - accessorKey: 'project_flock_kandang_id', + accessorKey: 'flock_name', header: 'Flock', - cell: (props) => `Flock ${props.row.original.project_flock_kandang_id}`, + cell: (props) => props.row.original.flock_name || '-', }, { - accessorKey: 'kandang.name', + accessorKey: 'kandang_name', header: 'Kandang', - cell: (props) => props.row.original.kandang.name || '-', + cell: (props) => props.row.original.kandang_name || '-', }, { accessorKey: 'week', header: 'Tanggal (Week)', cell: (props) => - `${formatDate(props.row.original.date, 'DD MMM YYYY')} (${props.row.original.week})`, + `${formatDate(props.row.original.applied_at, 'DD MMM YYYY')} (${props.row.original.week})`, }, { accessorKey: 'status', diff --git a/src/components/pages/uniformity/form/UniformityForm.schema.ts b/src/components/pages/uniformity/form/UniformityForm.schema.ts index 0625c2cf..cb891965 100644 --- a/src/components/pages/uniformity/form/UniformityForm.schema.ts +++ b/src/components/pages/uniformity/form/UniformityForm.schema.ts @@ -3,6 +3,7 @@ import { Uniformity } from '@/types/api/uniformity/uniformity'; type UniformityFormSchemaType = { date: string; + week: number; location?: { value: number; label: string; @@ -19,7 +20,7 @@ type UniformityFormSchemaType = { label: string; } | null; kandang_id: number; - files: File | undefined; + file: File | undefined; }; const FileSchema = Yup.mixed() @@ -44,6 +45,10 @@ const FileSchema = Yup.mixed() export const UniformityFormSchema: Yup.ObjectSchema = Yup.object({ date: Yup.string().required('Tanggal wajib diisi!'), + week: Yup.number() + .min(1, 'Minggu ke wajib diisi!') + .required('Minggu ke wajib diisi!') + .typeError('Minggu ke wajib diisi!'), location: Yup.object({ value: Yup.number().min(1).required(), label: Yup.string().required(), @@ -69,7 +74,7 @@ export const UniformityFormSchema: Yup.ObjectSchema = .min(1, 'Kandang wajib diisi!') .required('Kandang wajib diisi!') .typeError('Kandang wajib diisi!'), - files: FileSchema.required('File wajib diisi!'), + file: FileSchema.required('File wajib diisi!'), }); export type UniformityFormValues = Yup.InferType; @@ -79,6 +84,7 @@ export const getUniformityFormInitialValues = ( ): UniformityFormValues => { return { date: initialValues?.week ? '' : '', + week: initialValues?.week ?? 0, location: initialValues?.location ? { value: initialValues.location.id, @@ -101,6 +107,6 @@ export const getUniformityFormInitialValues = ( } : null, kandang_id: initialValues?.kandang?.id ?? 0, - files: undefined, + file: undefined, }; }; diff --git a/src/components/pages/uniformity/form/UniformityForm.tsx b/src/components/pages/uniformity/form/UniformityForm.tsx index 40587e6f..40e4fa22 100644 --- a/src/components/pages/uniformity/form/UniformityForm.tsx +++ b/src/components/pages/uniformity/form/UniformityForm.tsx @@ -5,6 +5,7 @@ import { useFormik } from 'formik'; import { useRouter } from 'next/navigation'; import { Icon } from '@iconify/react'; import { toast } from 'react-hot-toast'; +import moment from 'moment'; import DrawerHeader from '@/components/helper/drawer/DrawerHeader'; import { useUiStore } from '@/stores/ui/ui.store'; import { useUniformityStore } from '@/stores/uniformity/uniformity.store'; @@ -240,14 +241,17 @@ const UniformityForm = ({ setUniformityFormData({ date: values.date, + week: values.week, project_flock_kandang_id: projectFlockKandangId, - files: values.files as File, - fileName: (values.files as File).name, + file: values.file as File, + fileName: (values.file as File).name, }); const payload: VerifyUniformityPayload = { + date: values.date, + week: values.week, project_flock_kandang_id: projectFlockKandangId, - files: values.files as File, + file: values.file as File, }; const res = await UniformityApi.verifyUniformity(payload); @@ -323,10 +327,10 @@ const UniformityForm = ({ (e: React.ChangeEvent) => { const file = e.target.files?.[0]; - formik.setFieldTouched('files', true); + formik.setFieldTouched('file', true); if (!file) { - formik.setFieldValue('files', undefined); + formik.setFieldValue('file', undefined); return; } @@ -346,7 +350,7 @@ const UniformityForm = ({ return; } - formik.setFieldValue('files', file); + formik.setFieldValue('file', file); }, [formik] ); @@ -363,6 +367,19 @@ const UniformityForm = ({ }, [formik]); // ===== SIDE EFFECTS ===== + useEffect(() => { + // Calculate week from date whenever date changes (week of the month) + if (formik.values.date) { + const date = moment(formik.values.date); + const weekNumber = date.week() - moment(date).startOf('month').week() + 1; + + // Handle edge case for end of year + const adjustedWeekNumber = weekNumber <= 0 ? weekNumber + 52 : weekNumber; + + formik.setFieldValue('week', adjustedWeekNumber); + } + }, [formik.values.date]); + useEffect(() => { const unsub = subscribeValidate(() => { setIsValid(true); @@ -486,7 +503,7 @@ const UniformityForm = ({ htmlFor='file-upload-input' className={cn( "w-full text-sm font-normal leading-5 after:content-['*'] after:ml-0.5 after:text-red-500", - formik.touched.files && formik.errors.files && 'text-red-500' + formik.touched.file && formik.errors.file && 'text-red-500' )} > Upload File @@ -495,7 +512,7 @@ const UniformityForm = ({
- {formik.values.files ? ( + {formik.values.file ? (
- {formik.values.files.name} + {formik.values.file.name}
) : ( @@ -585,15 +602,15 @@ const UniformityForm = ({ ref={fileInputRef} type='file' id='file-upload-input' - name='files' + name='file' accept='application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/csv' onChange={handleFileChange} className='hidden' /> - {formik.touched.files && formik.errors.files && ( + {formik.touched.file && formik.errors.file && (

- {formik.errors.files as string} + {formik.errors.file as string}

)} diff --git a/src/components/pages/uniformity/form/UniformityPreviewForm.tsx b/src/components/pages/uniformity/form/UniformityPreviewForm.tsx index edd94a9a..ff5ffacb 100644 --- a/src/components/pages/uniformity/form/UniformityPreviewForm.tsx +++ b/src/components/pages/uniformity/form/UniformityPreviewForm.tsx @@ -39,10 +39,10 @@ const UniformityPreviewForm = () => { const tableData = useMemo(() => { if (!verifyUniformityResult) return []; - return verifyUniformityResult.body_weights.map((weight, index) => ({ + return verifyUniformityResult.uniformity_details.map((detail, index) => ({ id: `weight-${index}`, number: index + 1, - weight: weight, + weight: detail.weight, })); }, [verifyUniformityResult]); diff --git a/src/components/pages/uniformity/form/UniformityResultForm.tsx b/src/components/pages/uniformity/form/UniformityResultForm.tsx index aa12409b..7b8466fb 100644 --- a/src/components/pages/uniformity/form/UniformityResultForm.tsx +++ b/src/components/pages/uniformity/form/UniformityResultForm.tsx @@ -90,7 +90,7 @@ const UniformityResultForm = () => { }; const handleSubmit = async () => { - if (!uniformityFormData || !uniformityFormData.files) { + if (!uniformityFormData || !uniformityFormData.file) { toast.error('Form data is missing. Please try again.'); return; } @@ -100,8 +100,9 @@ const UniformityResultForm = () => { try { const payload = { date: uniformityFormData.date, + week: uniformityFormData.week, project_flock_kandang_id: uniformityFormData.project_flock_kandang_id, - files: uniformityFormData.files, + file: uniformityFormData.file, }; const res = await UniformityApi.createUniformity(payload); @@ -206,10 +207,11 @@ const UniformityResultForm = () => { const tableData = useMemo(() => { if (!verifyUniformityResult) return []; - return verifyUniformityResult.body_weights.map((weight, index) => ({ - id: `weight-${index}`, + return verifyUniformityResult.uniformity_details.map((detail, index) => ({ + id: `body-weight-${index + 1}`, number: index + 1, - weight: weight, + weight: detail.weight, + status: detail.range.toLowerCase() as 'ideal' | 'outside', })); }, [verifyUniformityResult]); diff --git a/src/services/api/uniformity.ts b/src/services/api/uniformity.ts index 2ccd38fd..7ef16e91 100644 --- a/src/services/api/uniformity.ts +++ b/src/services/api/uniformity.ts @@ -25,13 +25,14 @@ export class UniformityApiService extends BaseApiService< ): Promise | undefined> { const formData = new FormData(); formData.append('date', payload.date); + formData.append('week', payload.week.toString()); formData.append( 'project_flock_kandang_id', payload.project_flock_kandang_id.toString() ); - if (payload.files) { - formData.append('file', payload.files); + if (payload.file) { + formData.append('file', payload.file); } return await this.create(formData as unknown as CreateUniformityPayload); @@ -41,13 +42,15 @@ export class UniformityApiService extends BaseApiService< payload: VerifyUniformityPayload ): Promise | undefined> { const formData = new FormData(); + formData.append('date', payload.date); + formData.append('week', payload.week.toString()); formData.append( 'project_flock_kandang_id', payload.project_flock_kandang_id.toString() ); - if (payload.files) { - formData.append('file', payload.files); + if (payload.file) { + formData.append('file', payload.file); } return await this.customRequest>( @@ -61,5 +64,5 @@ export class UniformityApiService extends BaseApiService< } export const UniformityApi = new UniformityApiService( - 'http://localhost:4010/api/uniformity' + 'http://localhost:4010/api/production/uniformities' ); diff --git a/src/stores/uniformity/uniformity.store.ts b/src/stores/uniformity/uniformity.store.ts index 97162836..082a2d5b 100644 --- a/src/stores/uniformity/uniformity.store.ts +++ b/src/stores/uniformity/uniformity.store.ts @@ -6,8 +6,9 @@ export type UniformityStep = 'preview' | 'result'; export type UniformityFormData = { date: string; + week: number; project_flock_kandang_id: number; - files: File | null; + file: File | null; fileName: string; }; diff --git a/src/types/api/uniformity/uniformity.d.ts b/src/types/api/uniformity/uniformity.d.ts index b8d1b144..1cdefadb 100644 --- a/src/types/api/uniformity/uniformity.d.ts +++ b/src/types/api/uniformity/uniformity.d.ts @@ -1,29 +1,88 @@ +import { BaseMetadata } from '@/types/api/api-general'; import { Location } from '@/types/api/location/location'; +import { ProjectFlock } from '@/types/api/project-flock/project-flock'; import { Kandang } from '@/types/api/kandang/kandang'; -import { BaseMetadata } from '@/types/common/base-metadata'; +import { BaseApproval } from '@/types/api/approval/approval'; +// ==================== GET ALL RESPONSE ==================== export type Uniformity = BaseMetadata & { id: number; - location: Location; project_flock_kandang_id: number; + location: Location; + project_flock: ProjectFlock; + location_name: string; + flock_name: string; kandang: Kandang; + kandang_name: string; + applied_at: string; week: number; status: 'CREATED' | 'APPROVED' | 'REJECTED'; uniformity: number; - date?: string; + cv: number; + chick_qty_of_weight: number; + uniform_qty: number; + mean_up: number; + mean_down: number; + created_at: string; + created_by: number; + latest_approval?: BaseApproval; }; +// ==================== GET ONE RESPONSE ==================== +export type UniformityInfoUmum = { + tanggal: string; + lokasi_farm: string; + project_flock: string; + kandang: string; + file_name: string; +}; + +export type UniformitySampling = { + chick_qty_of_weight: number; + mean_weight: number; + mean_down: number; + mean_up: number; +}; + +export type UniformityResult = { + uniform_qty: number; + outside_qty: number; + uniformity: number; + cv: number; +}; + +export type UniformityDetailItem = { + id: number; + weight: number; + range: 'Ideal' | 'Outside'; +}; + +export type UniformityDetail = BaseMetadata & { + id: number; + info_umum: UniformityInfoUmum; + sampling: UniformitySampling; + result: UniformityResult; + uniformity_details: UniformityDetailItem[]; +}; + +// ==================== VERIFY RESPONSE ==================== +export type VerifyUniformityResponse = { + sampling: UniformitySampling; + result: UniformityResult; + uniformity_details: UniformityDetailItem[]; +}; + +// ==================== PAYLOADS ==================== export type CreateUniformityPayload = { date: string; project_flock_kandang_id: number; - files: File; + file: File; + week: number; }; export type VerifyUniformityPayload = { + date: string; project_flock_kandang_id: number; - files: File; -}; - -export type VerifyUniformityResponse = { - body_weights: number[]; + file: File; + week: number; };