diff --git a/src/components/pages/production/recording/form/RecordingForm.schema.ts b/src/components/pages/production/recording/form/RecordingForm.schema.ts index 8ebd4aa2..82b59036 100644 --- a/src/components/pages/production/recording/form/RecordingForm.schema.ts +++ b/src/components/pages/production/recording/form/RecordingForm.schema.ts @@ -8,6 +8,21 @@ import { type RecordingGrowingFormSchemaType = { record_date: string; + location?: { + value: number; + label: string; + } | null; + location_id: number; + project_flock?: { + value: number; + label: string; + } | null; + project_flock_id: number; + kandang?: { + value: number; + label: string; + } | null; + kandang_id: number; project_flock_kandang: { value: number; label: string; @@ -88,7 +103,32 @@ export const RecordingGrowingFormSchema: Yup.ObjectSchema; @@ -186,6 +226,12 @@ export const getRecordingGrowingFormInitialValues = ( record_date: initialValues?.record_datetime ? new Date(initialValues.record_datetime).toISOString().split('T')[0] : new Date().toISOString().split('T')[0], + location: null, + location_id: 0, + project_flock: null, + project_flock_id: 0, + kandang: null, + kandang_id: 0, project_flock_kandang: initialValues?.project_flock_kandang_id ? { value: initialValues.project_flock_kandang_id, diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index b45f72d0..d7f913e8 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -56,6 +56,7 @@ import { import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; import { Kandang } from '@/types/api/master-data/kandang'; +import * as Yup from 'yup'; import { RecordingGrowingFormSchema, RecordingLayingFormSchema, @@ -231,6 +232,16 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const [, setNewRecordingData] = useState(null); const [nextDayRecording, setNextDayRecording] = useState(null); + const [currentRecordDate, setCurrentRecordDate] = useState( + new Date().toISOString().split('T')[0] + ); + const [duplicateErrorShown, setDuplicateErrorShown] = useState(false); + + useEffect(() => { + return () => { + toast.dismiss(); + }; + }, []); const approveModal = useModal(); const rejectModal = useModal(); @@ -719,18 +730,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const recordedProjectFlockKandangIds = useMemo(() => { if (!isResponseSuccess(existingRecordings)) return new Set(); - const todayRecordings = existingRecordings.data; + const allRecordings = existingRecordings.data; const recordedIds = new Set(); - todayRecordings.forEach((recording) => { + allRecordings.forEach((recording) => { const recordingDate = recording.record_datetime?.split('T')[0]; - if (recordingDate === today) { + if (recordingDate === currentRecordDate) { recordedIds.add(recording.project_flock?.project_flock_kandang_id); } }); return recordedIds; - }, [existingRecordings, today]); + }, [existingRecordings, currentRecordDate]); const currentTotalChickQty = useMemo(() => { if (type === 'add' && projectFlockKandangLookup) { @@ -910,14 +921,38 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { initialValues: formikInitialValues, enableReinitialize: true, validationSchema: (() => { + let schema; if (isLayingCategory) { - return type === 'edit' - ? UpdateRecordingLayingFormSchema - : RecordingLayingFormSchema; + schema = + type === 'edit' + ? UpdateRecordingLayingFormSchema + : RecordingLayingFormSchema; + } else { + schema = + type === 'edit' + ? UpdateRecordingGrowingFormSchema + : RecordingGrowingFormSchema; } - return type === 'edit' - ? UpdateRecordingGrowingFormSchema - : RecordingGrowingFormSchema; + return schema.clone().concat( + Yup.object().shape({ + project_flock_kandang_id: Yup.number().test( + 'not-already-recorded', + 'Project Flock ini sudah direcord pada tanggal tersebut!', + function (value) { + if (type !== 'add') return true; + if (value && recordedProjectFlockKandangIds.has(value)) { + if (this.createError) { + return this.createError({ + message: `Project Flock ini sudah direcord pada tanggal ${formatDate(currentRecordDate, 'DD MMMM YYYY')}!`, + }); + } + return false; + } + return true; + } + ), + }) + ); })(), validateOnChange: true, validateOnBlur: true, @@ -1145,27 +1180,57 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { // ===== EVENT HANDLERS ===== const locationChangeHandler = (val: OptionType | OptionType[] | null) => { - const location = val as OptionType; + const location = val as OptionType | null; + const locationId = location ? Number(location.value) : 0; + + formik.setFieldTouched('location', true); + formik.setFieldValue('location', location); + formik.setFieldTouched('location_id', true); + formik.setFieldValue('location_id', locationId); + setSelectedLocation(location); setSelectedProjectFlock(null); setSelectedKandang(null); + if (duplicateErrorShown) { + toast.dismiss(); + setDuplicateErrorShown(false); + } setSelectedProjectFlockLocationId( location ? location.value.toString() : '' ); - formik.setFieldValue('project_flock_kandang', null); - formik.setFieldValue('project_flock_kandang_id', 0); }; const projectFlockChangeHandler = (val: OptionType | OptionType[] | null) => { - setSelectedProjectFlock(val as OptionType); + const projectFlock = val as OptionType | null; + const projectFlockId = Number(projectFlock?.value); + + formik.setFieldTouched('project_flock', true); + formik.setFieldValue('project_flock', projectFlock); + formik.setFieldTouched('project_flock_id', true); + formik.setFieldValue('project_flock_id', projectFlockId); + + setSelectedProjectFlock(projectFlock); setSelectedKandang(null); - formik.setFieldValue('project_flock_kandang', null); - formik.setFieldValue('project_flock_kandang_id', 0); + if (duplicateErrorShown) { + toast.dismiss(); + setDuplicateErrorShown(false); + } }; const kandangChangeHandler = (val: OptionType | OptionType[] | null) => { - const kandang = val as OptionType; + const kandang = val as OptionType | null; + const kandangId = Number(kandang?.value); + + formik.setFieldTouched('kandang', true); + formik.setFieldValue('kandang', kandang); + formik.setFieldTouched('kandang_id', true); + formik.setFieldValue('kandang_id', kandangId); + setSelectedKandang(kandang); + if (duplicateErrorShown) { + toast.dismiss(); + setDuplicateErrorShown(false); + } if (selectedLocation && kandang) { setStockProductsLocationId(selectedLocation.value.toString()); setStockProductsKandangId(kandang.value.toString()); @@ -1187,9 +1252,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const handleRecordDateChange = useCallback( (e: React.ChangeEvent) => { - formik.setFieldValue('record_date', e.target.value); + const newDate = e.target.value; + formik.setFieldValue('record_date', newDate); + setCurrentRecordDate(newDate); + if (duplicateErrorShown) { + toast.dismiss(); + setDuplicateErrorShown(false); + } + setTimeout(() => { + formik.validateField('project_flock_kandang_id'); + }, 0); }, - [formik] + [formik, duplicateErrorShown] ); useEffect(() => { @@ -1199,8 +1273,19 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { if (type === 'add') { if (recordedProjectFlockKandangIds.has(projectFlockKandangId)) { - toast.error('Project Flock Kandang ini sudah direcord hari ini!'); + if (!duplicateErrorShown) { + toast.error( + `Project Flock Kandang ini sudah direcord pada tanggal ${formatDate(currentRecordDate, 'DD MMMM YYYY')}!`, + { duration: Infinity } + ); + setDuplicateErrorShown(true); + } return; + } else { + if (duplicateErrorShown) { + toast.dismiss(); + setDuplicateErrorShown(false); + } } if ( @@ -1599,6 +1684,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { Boolean(formik.errors.record_date) } errorMessage={formik.errors.record_date as string} + disabled={type === 'edit'} /> { placeholder='Pilih Lokasi' isClearable isSearchable + isDisabled={type === 'edit'} + isError={ + formik.touched.location_id && + Boolean(formik.errors.location_id) + } + errorMessage={formik.errors.location_id as string} /> { onInputChange={setProjectFlockSearchValue} isLoading={isLoadingProjectFlocks} onMenuScrollToBottom={loadMoreProjectFlocks} - isDisabled={!selectedLocation} + isDisabled={!selectedLocation || type === 'edit'} placeholder={ selectedLocation ? 'Pilih Project Flock' @@ -1633,6 +1725,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } isClearable isSearchable + isError={ + formik.touched.project_flock_id && + Boolean(formik.errors.project_flock_id) + } + errorMessage={formik.errors.project_flock_id as string} /> { onChange={kandangChangeHandler} options={kandangOptions} isLoading={false} - isDisabled={!selectedProjectFlock} + isDisabled={!selectedProjectFlock || type === 'edit'} placeholder={ selectedProjectFlock ? 'Pilih Kandang' @@ -1651,6 +1748,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } isClearable isSearchable + isError={ + formik.touched.kandang_id && + Boolean(formik.errors.kandang_id) + } + errorMessage={formik.errors.kandang_id as string} startAdornment={ projectFlockKandangLookup || projectFlockKandangDetail ? getProjectFlockBadgeAdornment() @@ -2894,7 +2996,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { color='primary' className='px-4' isLoading={formik.isSubmitting} - disabled={hasExceededStock || formik.isSubmitting} + disabled={ + hasExceededStock || + formik.isSubmitting || + duplicateErrorShown + } > Submit @@ -2919,7 +3025,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { color='primary' className='px-4' isLoading={formik.isSubmitting} - disabled={hasExceededStock || formik.isSubmitting} + disabled={ + hasExceededStock || + formik.isSubmitting || + duplicateErrorShown + } > Submit diff --git a/src/components/pages/purchase/form/order/PurchaseOrderStaffApprovalForm.tsx b/src/components/pages/purchase/form/order/PurchaseOrderStaffApprovalForm.tsx index 729b6782..a232347d 100644 --- a/src/components/pages/purchase/form/order/PurchaseOrderStaffApprovalForm.tsx +++ b/src/components/pages/purchase/form/order/PurchaseOrderStaffApprovalForm.tsx @@ -633,8 +633,18 @@ const PurchaseOrderStaffApprovalForm = ({ formik.setFieldValue(`items.${idx}.qty`, numValue); - formik.setFieldValue(`items.${idx}.price`, ''); - formik.setFieldValue(`items.${idx}.total_price`, ''); + if ( + formItem.price !== '' && + formItem.price !== undefined && + formItem.price !== null && + numValue !== '' && + numValue > 0 + ) { + const calculatedTotal = Number(formItem.price) * Number(numValue); + formik.setFieldValue(`items.${idx}.total_price`, calculatedTotal); + } else if (numValue === '') { + formik.setFieldValue(`items.${idx}.total_price`, ''); + } } if (field === 'price' || field === 'total_price') { @@ -1184,8 +1194,10 @@ const PurchaseOrderStaffApprovalForm = ({ color='warning' className='px-4' onClick={() => { - formik.setValues(formikInitialValues); - formik.resetForm(); + if (type === 'add') { + formik.setValues(formikInitialValues); + formik.resetForm(); + } setPurchaseOrderFormErrorMessage(''); onCancel?.(); onModalClose?.();