Merge branch 'development' of https://gitlab.com/mbugroup/lti-web-client into hot-fix/marketing-disabled-edit-customer

This commit is contained in:
randy-ar
2026-01-17 15:59:49 +07:00
3 changed files with 197 additions and 29 deletions
@@ -8,6 +8,21 @@ import {
type RecordingGrowingFormSchemaType = { type RecordingGrowingFormSchemaType = {
record_date: string; 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: { project_flock_kandang: {
value: number; value: number;
label: string; label: string;
@@ -88,7 +103,32 @@ export const RecordingGrowingFormSchema: Yup.ObjectSchema<RecordingGrowingFormSc
Yup.object({ Yup.object({
record_date: Yup.string() record_date: Yup.string()
.required('Tanggal recording wajib diisi!') .required('Tanggal recording wajib diisi!')
.min(1, 'Tanggal recording wajib diisi!')
.typeError('Tanggal recording wajib diisi!'), .typeError('Tanggal recording wajib diisi!'),
location: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
location_id: Yup.number()
.min(1, 'Lokasi wajib diisi!')
.required('Lokasi wajib diisi!')
.typeError('Lokasi wajib diisi!'),
project_flock: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
project_flock_id: Yup.number()
.min(1, 'Project flock wajib diisi!')
.required('Project flock wajib diisi!')
.typeError('Project flock wajib diisi!'),
kandang: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
kandang_id: Yup.number()
.min(1, 'Kandang wajib diisi!')
.required('Kandang wajib diisi!')
.typeError('Kandang wajib diisi!'),
project_flock_kandang: Yup.object({ project_flock_kandang: Yup.object({
value: Yup.number().min(1).required(), value: Yup.number().min(1).required(),
label: Yup.string().required(), label: Yup.string().required(),
@@ -104,7 +144,7 @@ export const RecordingGrowingFormSchema: Yup.ObjectSchema<RecordingGrowingFormSc
.required('Project Flock Kandang wajib diisi!') .required('Project Flock Kandang wajib diisi!')
.test( .test(
'not-already-recorded', 'not-already-recorded',
'Project Flock ini sudah direcord hari ini!', 'Project Flock ini sudah direcord pada tanggal tersebut!',
function (value) { function (value) {
const recordedProjectFlockIds = this.options.context const recordedProjectFlockIds = this.options.context
?.recordedProjectFlockIds as Set<number>; ?.recordedProjectFlockIds as Set<number>;
@@ -186,6 +226,12 @@ export const getRecordingGrowingFormInitialValues = (
record_date: initialValues?.record_datetime record_date: initialValues?.record_datetime
? new Date(initialValues.record_datetime).toISOString().split('T')[0] ? new Date(initialValues.record_datetime).toISOString().split('T')[0]
: new Date().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 project_flock_kandang: initialValues?.project_flock_kandang_id
? { ? {
value: initialValues.project_flock_kandang_id, value: initialValues.project_flock_kandang_id,
@@ -56,6 +56,7 @@ import {
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
import { Kandang } from '@/types/api/master-data/kandang'; import { Kandang } from '@/types/api/master-data/kandang';
import * as Yup from 'yup';
import { import {
RecordingGrowingFormSchema, RecordingGrowingFormSchema,
RecordingLayingFormSchema, RecordingLayingFormSchema,
@@ -231,6 +232,16 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const [, setNewRecordingData] = useState<Recording | null>(null); const [, setNewRecordingData] = useState<Recording | null>(null);
const [nextDayRecording, setNextDayRecording] = const [nextDayRecording, setNextDayRecording] =
useState<NextDayRecording | null>(null); useState<NextDayRecording | null>(null);
const [currentRecordDate, setCurrentRecordDate] = useState<string>(
new Date().toISOString().split('T')[0]
);
const [duplicateErrorShown, setDuplicateErrorShown] = useState(false);
useEffect(() => {
return () => {
toast.dismiss();
};
}, []);
const approveModal = useModal(); const approveModal = useModal();
const rejectModal = useModal(); const rejectModal = useModal();
@@ -719,18 +730,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const recordedProjectFlockKandangIds = useMemo(() => { const recordedProjectFlockKandangIds = useMemo(() => {
if (!isResponseSuccess(existingRecordings)) return new Set<number>(); if (!isResponseSuccess(existingRecordings)) return new Set<number>();
const todayRecordings = existingRecordings.data; const allRecordings = existingRecordings.data;
const recordedIds = new Set<number>(); const recordedIds = new Set<number>();
todayRecordings.forEach((recording) => { allRecordings.forEach((recording) => {
const recordingDate = recording.record_datetime?.split('T')[0]; const recordingDate = recording.record_datetime?.split('T')[0];
if (recordingDate === today) { if (recordingDate === currentRecordDate) {
recordedIds.add(recording.project_flock?.project_flock_kandang_id); recordedIds.add(recording.project_flock?.project_flock_kandang_id);
} }
}); });
return recordedIds; return recordedIds;
}, [existingRecordings, today]); }, [existingRecordings, currentRecordDate]);
const currentTotalChickQty = useMemo(() => { const currentTotalChickQty = useMemo(() => {
if (type === 'add' && projectFlockKandangLookup) { if (type === 'add' && projectFlockKandangLookup) {
@@ -910,14 +921,38 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
initialValues: formikInitialValues, initialValues: formikInitialValues,
enableReinitialize: true, enableReinitialize: true,
validationSchema: (() => { validationSchema: (() => {
let schema;
if (isLayingCategory) { if (isLayingCategory) {
return type === 'edit' schema =
? UpdateRecordingLayingFormSchema type === 'edit'
: RecordingLayingFormSchema; ? UpdateRecordingLayingFormSchema
: RecordingLayingFormSchema;
} else {
schema =
type === 'edit'
? UpdateRecordingGrowingFormSchema
: RecordingGrowingFormSchema;
} }
return type === 'edit' return schema.clone().concat(
? UpdateRecordingGrowingFormSchema Yup.object().shape({
: RecordingGrowingFormSchema; 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, validateOnChange: true,
validateOnBlur: true, validateOnBlur: true,
@@ -1145,27 +1180,57 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
// ===== EVENT HANDLERS ===== // ===== EVENT HANDLERS =====
const locationChangeHandler = (val: OptionType | OptionType[] | null) => { 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); setSelectedLocation(location);
setSelectedProjectFlock(null); setSelectedProjectFlock(null);
setSelectedKandang(null); setSelectedKandang(null);
if (duplicateErrorShown) {
toast.dismiss();
setDuplicateErrorShown(false);
}
setSelectedProjectFlockLocationId( setSelectedProjectFlockLocationId(
location ? location.value.toString() : '' location ? location.value.toString() : ''
); );
formik.setFieldValue('project_flock_kandang', null);
formik.setFieldValue('project_flock_kandang_id', 0);
}; };
const projectFlockChangeHandler = (val: OptionType | OptionType[] | null) => { 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); setSelectedKandang(null);
formik.setFieldValue('project_flock_kandang', null); if (duplicateErrorShown) {
formik.setFieldValue('project_flock_kandang_id', 0); toast.dismiss();
setDuplicateErrorShown(false);
}
}; };
const kandangChangeHandler = (val: OptionType | OptionType[] | null) => { 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); setSelectedKandang(kandang);
if (duplicateErrorShown) {
toast.dismiss();
setDuplicateErrorShown(false);
}
if (selectedLocation && kandang) { if (selectedLocation && kandang) {
setStockProductsLocationId(selectedLocation.value.toString()); setStockProductsLocationId(selectedLocation.value.toString());
setStockProductsKandangId(kandang.value.toString()); setStockProductsKandangId(kandang.value.toString());
@@ -1187,9 +1252,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const handleRecordDateChange = useCallback( const handleRecordDateChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => { (e: React.ChangeEvent<HTMLInputElement>) => {
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(() => { useEffect(() => {
@@ -1199,8 +1273,19 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
if (type === 'add') { if (type === 'add') {
if (recordedProjectFlockKandangIds.has(projectFlockKandangId)) { 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; return;
} else {
if (duplicateErrorShown) {
toast.dismiss();
setDuplicateErrorShown(false);
}
} }
if ( if (
@@ -1599,6 +1684,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
Boolean(formik.errors.record_date) Boolean(formik.errors.record_date)
} }
errorMessage={formik.errors.record_date as string} errorMessage={formik.errors.record_date as string}
disabled={type === 'edit'}
/> />
<SelectInput <SelectInput
key={`location-select-${selectedLocation?.value || 'default'}`} key={`location-select-${selectedLocation?.value || 'default'}`}
@@ -1613,6 +1699,12 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
placeholder='Pilih Lokasi' placeholder='Pilih Lokasi'
isClearable isClearable
isSearchable isSearchable
isDisabled={type === 'edit'}
isError={
formik.touched.location_id &&
Boolean(formik.errors.location_id)
}
errorMessage={formik.errors.location_id as string}
/> />
<SelectInput <SelectInput
@@ -1625,7 +1717,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
onInputChange={setProjectFlockSearchValue} onInputChange={setProjectFlockSearchValue}
isLoading={isLoadingProjectFlocks} isLoading={isLoadingProjectFlocks}
onMenuScrollToBottom={loadMoreProjectFlocks} onMenuScrollToBottom={loadMoreProjectFlocks}
isDisabled={!selectedLocation} isDisabled={!selectedLocation || type === 'edit'}
placeholder={ placeholder={
selectedLocation selectedLocation
? 'Pilih Project Flock' ? 'Pilih Project Flock'
@@ -1633,6 +1725,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
} }
isClearable isClearable
isSearchable isSearchable
isError={
formik.touched.project_flock_id &&
Boolean(formik.errors.project_flock_id)
}
errorMessage={formik.errors.project_flock_id as string}
/> />
<SelectInput <SelectInput
@@ -1643,7 +1740,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
onChange={kandangChangeHandler} onChange={kandangChangeHandler}
options={kandangOptions} options={kandangOptions}
isLoading={false} isLoading={false}
isDisabled={!selectedProjectFlock} isDisabled={!selectedProjectFlock || type === 'edit'}
placeholder={ placeholder={
selectedProjectFlock selectedProjectFlock
? 'Pilih Kandang' ? 'Pilih Kandang'
@@ -1651,6 +1748,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
} }
isClearable isClearable
isSearchable isSearchable
isError={
formik.touched.kandang_id &&
Boolean(formik.errors.kandang_id)
}
errorMessage={formik.errors.kandang_id as string}
startAdornment={ startAdornment={
projectFlockKandangLookup || projectFlockKandangDetail projectFlockKandangLookup || projectFlockKandangDetail
? getProjectFlockBadgeAdornment() ? getProjectFlockBadgeAdornment()
@@ -2894,7 +2996,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
color='primary' color='primary'
className='px-4' className='px-4'
isLoading={formik.isSubmitting} isLoading={formik.isSubmitting}
disabled={hasExceededStock || formik.isSubmitting} disabled={
hasExceededStock ||
formik.isSubmitting ||
duplicateErrorShown
}
> >
Submit Submit
</Button> </Button>
@@ -2919,7 +3025,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
color='primary' color='primary'
className='px-4' className='px-4'
isLoading={formik.isSubmitting} isLoading={formik.isSubmitting}
disabled={hasExceededStock || formik.isSubmitting} disabled={
hasExceededStock ||
formik.isSubmitting ||
duplicateErrorShown
}
> >
Submit Submit
</Button> </Button>
@@ -633,8 +633,18 @@ const PurchaseOrderStaffApprovalForm = ({
formik.setFieldValue(`items.${idx}.qty`, numValue); formik.setFieldValue(`items.${idx}.qty`, numValue);
formik.setFieldValue(`items.${idx}.price`, ''); if (
formik.setFieldValue(`items.${idx}.total_price`, ''); 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') { if (field === 'price' || field === 'total_price') {
@@ -1184,8 +1194,10 @@ const PurchaseOrderStaffApprovalForm = ({
color='warning' color='warning'
className='px-4' className='px-4'
onClick={() => { onClick={() => {
formik.setValues(formikInitialValues); if (type === 'add') {
formik.resetForm(); formik.setValues(formikInitialValues);
formik.resetForm();
}
setPurchaseOrderFormErrorMessage(''); setPurchaseOrderFormErrorMessage('');
onCancel?.(); onCancel?.();
onModalClose?.(); onModalClose?.();