diff --git a/src/components/pages/production/recording/form/RecordingForm.schema.ts b/src/components/pages/production/recording/form/RecordingForm.schema.ts index 4abee337..ccb4ddd5 100644 --- a/src/components/pages/production/recording/form/RecordingForm.schema.ts +++ b/src/components/pages/production/recording/form/RecordingForm.schema.ts @@ -6,6 +6,91 @@ import { } from '@/types/api/production/recording'; export const RecordingFormSchema = Yup.object({ + project_flock_kandang: Yup.object({ + value: Yup.number().min(1).required(), + label: Yup.string().required(), + }).nullable(), + project_flock_kandang_id: Yup.number() + .default(0) + .typeError('Project Flock Kandang wajib diisi!') + .test( + 'is-valid-project-flock-kandang', + 'Project Flock Kandang wajib diisi!', + (value) => value !== undefined && value !== null && value > 0 + ) + .required('Project Flock Kandang wajib diisi!') + .test( + 'not-already-recorded', + 'Project Flock ini sudah direcord hari ini!', + function(value) { + const recordedProjectFlockIds = this.options.context?.recordedProjectFlockIds as Set; + const formType = this.options.context?.type as 'add' | 'edit' | 'detail'; + if (formType !== 'add') return true; + if (value && recordedProjectFlockIds?.has(value)) { + return false; + } + return true; + } + ), + body_weights: Yup.array() + .of( + Yup.object({ + weight: Yup.number() + .required('Berat ayam wajib diisi!') + .min(1, 'Berat ayam minimal 1 gram!') + .typeError('Berat ayam harus berupa angka!'), + qty: Yup.number() + .required('Jumlah ayam wajib diisi!') + .min(1, 'Jumlah ayam minimal 1 ekor!') + .typeError('Jumlah ayam harus berupa angka!') + .default(1), + average_weight: Yup.number() + .optional() + .min(0, 'Rata-rata berat tidak boleh negatif!') + .typeError('Rata-rata berat harus berupa angka!') + .default(0), + }) + ) + .min(1, 'Minimal harus ada 1 data bobot badan!') + .required('Data bobot badan wajib diisi!'), + stocks: Yup.array() + .of( + Yup.object({ + product_warehouse_id: Yup.number() + .required('Produk wajib diisi!') + .min(1, 'Produk wajib diisi!') + .typeError('Produk harus berupa angka!'), + usage_amount: Yup.number() + .required('Jumlah penggunaan wajib diisi!') + .min(0, 'Jumlah penggunaan tidak boleh negatif!') + .typeError('Jumlah penggunaan harus berupa angka!'), + notes: Yup.string().optional(), + }) + ) + .min(1, 'Minimal harus ada 1 data stok!') + .required('Data stok wajib diisi!'), + depletions: Yup.array() + .of( + Yup.object({ + total: Yup.number() + .required('Jumlah depletions wajib diisi!') + .min(1, 'Jumlah depletions minimal 1!') + .typeError('Jumlah depletions harus berupa angka!'), + notes: Yup.string() + .required('Kondisi depletions wajib diisi!') + .oneOf( + RECORDING_FLAG_OPTIONS.map((option) => option.value), + 'Kondisi depletions tidak valid!' + ) + .typeError('Kondisi depletions harus berupa teks!') + .min(1, 'Kondisi depletions wajib diisi!'), + }) + ) + .min(1, 'Minimal harus ada 1 data depletions!') + .required('Data depletions wajib diisi!'), +}); + +export const UpdateRecordingFormSchema = Yup.object({ project_flock_kandang: Yup.object({ value: Yup.number().min(1).required(), label: Yup.string().required(), @@ -77,8 +162,6 @@ export const RecordingFormSchema = Yup.object({ .required('Data depletions wajib diisi!'), }); -export const UpdateRecordingFormSchema = RecordingFormSchema; - export type RecordingFormValues = Yup.InferType; type RecordingFormData = Partial & { diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index ffee70ae..ada0f019 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -86,6 +86,21 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { return `${ProductWarehouseApi.basePath}?${params.toString()}`; }, [selectedLocation]); + const today = new Date().toISOString().split('T')[0]; + const existingRecordingsUrl = useMemo(() => { + return `${RecordingApi.basePath}?record_date=${today}`; + }, []); + + const { data: existingRecordings } = useSWR( + existingRecordingsUrl, + RecordingApi.getAllFetcher + ); + + const recordedProjectFlockIds = useMemo(() => { + if (!isResponseSuccess(existingRecordings)) return new Set(); + return new Set(existingRecordings?.data.map(rec => rec.project_flock_kandang_id) || []); + }, [existingRecordings]); + const { data: stockProducts, isLoading: isLoadingStockProducts } = useSWR( stockProductsUrl, ProductWarehouseApi.getAllFetcher @@ -106,14 +121,19 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const options: OptionType[] = []; projectFlocks?.data.forEach((projectFlock) => { projectFlock.kandangs.forEach((kandang) => { + const isAlreadyRecorded = recordedProjectFlockIds.has(projectFlock.id); + const label = isAlreadyRecorded + ? `${projectFlock.flock.name} - ${projectFlock.area.name} - ${kandang.name} (Sudah Direcord)` + : `${projectFlock.flock.name} - ${projectFlock.area.name} - ${kandang.name}`; + options.push({ value: projectFlock.id, - label: `${projectFlock.flock.name} - ${projectFlock.area.name} - ${kandang.name}`, + label: label, }); }); }); return options; - }, [projectFlocks]); + }, [projectFlocks, recordedProjectFlockIds, type]); const unifiedStockProducts = useMemo(() => { const options: OptionType[] = []; @@ -359,6 +379,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }; const projectFlockKandangChangeHandler = (val: OptionType | OptionType[] | null) => { + if (type === 'add' && val && recordedProjectFlockIds.has((val as OptionType).value as number)) { + toast.error('Project Flock ini sudah direcord hari ini!'); + return; + } + formik.setFieldTouched('project_flock_kandang', true); formik.setFieldValue('project_flock_kandang', val); formik.setFieldTouched('project_flock_kandang_id', true); @@ -665,28 +690,30 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { isSearchable /> - +
+ +
@@ -1044,38 +1071,38 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { isDisabled={type === 'detail'} /> - +
- - {type !== 'detail' && getStockUsageAdornment(idx)} -
+ + {type !== 'detail' && getStockUsageAdornment(idx)} + - {type !== 'detail' && ( + {type !== 'detail' && (