mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE-174): enhance GradingForm and RecordingForm with improved error handling and modal integration for delete actions
This commit is contained in:
@@ -31,11 +31,10 @@ import {
|
|||||||
UpdateRecordingGrowingFormSchema,
|
UpdateRecordingGrowingFormSchema,
|
||||||
UpdateRecordingLayingFormSchema,
|
UpdateRecordingLayingFormSchema,
|
||||||
} from './RecordingForm.schema';
|
} from './RecordingForm.schema';
|
||||||
import { useRecordingFormHandlers } from './useRecordingFormHandlers';
|
|
||||||
import { ProjectFlockApi } from '@/services/api/production';
|
import { ProjectFlockApi } from '@/services/api/production';
|
||||||
import { LocationApi } from '@/services/api/master-data';
|
import { LocationApi } from '@/services/api/master-data';
|
||||||
import { ProductWarehouseApi } from '@/services/api/inventory';
|
import { ProductWarehouseApi } from '@/services/api/inventory';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess, isResponseError } from '@/lib/api-helper';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock';
|
import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock';
|
||||||
import { useModal } from '@/components/Modal';
|
import { useModal } from '@/components/Modal';
|
||||||
@@ -77,9 +76,61 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
||||||
const [isRejectLoading, setIsRejectLoading] = useState(false);
|
const [isRejectLoading, setIsRejectLoading] = useState(false);
|
||||||
const [formSteps, setFormSteps] = useState<FormStepStatus[] | null>(null);
|
const [formSteps, setFormSteps] = useState<FormStepStatus[] | null>(null);
|
||||||
|
const [recordingFormErrorMessage, setRecordingFormErrorMessage] =
|
||||||
|
useState('');
|
||||||
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const approveModal = useModal();
|
const approveModal = useModal();
|
||||||
const rejectModal = useModal();
|
const rejectModal = useModal();
|
||||||
|
const deleteModal = useModal();
|
||||||
|
|
||||||
|
// ===== FORM HANDLERS =====
|
||||||
|
const createRecordingHandler = useCallback(
|
||||||
|
async (
|
||||||
|
payload: CreateGrowingRecordingPayload | CreateLayingRecordingPayload
|
||||||
|
) => {
|
||||||
|
const res = await RecordingApi.create(payload);
|
||||||
|
if (isResponseError(res)) {
|
||||||
|
setRecordingFormErrorMessage(res.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
toast.success(res?.message as string);
|
||||||
|
router.push('/production/recording');
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateRecordingHandler = useCallback(
|
||||||
|
async (
|
||||||
|
recordingId: number,
|
||||||
|
payload: UpdateGrowingRecordingPayload | UpdateLayingRecordingPayload
|
||||||
|
) => {
|
||||||
|
const res = await RecordingApi.update(recordingId, payload);
|
||||||
|
if (res?.status === 'error') {
|
||||||
|
setRecordingFormErrorMessage(res.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
toast.success(res?.message as string);
|
||||||
|
router.refresh();
|
||||||
|
router.push('/production/recording');
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
|
||||||
|
const deleteRecordingClickHandler = useCallback(() => {
|
||||||
|
deleteModal.openModal();
|
||||||
|
}, [deleteModal]);
|
||||||
|
|
||||||
|
const confirmationModalDeleteClickHandler = useCallback(async () => {
|
||||||
|
if (!initialValues?.id) return;
|
||||||
|
|
||||||
|
setIsDeleteLoading(true);
|
||||||
|
await RecordingApi.delete(initialValues.id);
|
||||||
|
deleteModal.closeModal();
|
||||||
|
toast.success('Successfully delete Recording!');
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
router.push('/production/recording');
|
||||||
|
}, [deleteModal, initialValues?.id, router]);
|
||||||
|
|
||||||
// ===== API DATA FETCHING =====
|
// ===== API DATA FETCHING =====
|
||||||
const locationsUrl = `${LocationApi.basePath}?${new URLSearchParams({
|
const locationsUrl = `${LocationApi.basePath}?${new URLSearchParams({
|
||||||
@@ -336,17 +387,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
return options;
|
return options;
|
||||||
}, [eggProductsData]);
|
}, [eggProductsData]);
|
||||||
|
|
||||||
// ===== FORM HANDLERS =====
|
|
||||||
const {
|
|
||||||
deleteModal,
|
|
||||||
recordingFormErrorMessage,
|
|
||||||
isDeleteLoading,
|
|
||||||
createRecordingHandler,
|
|
||||||
updateRecordingHandler,
|
|
||||||
deleteRecordingClickHandler,
|
|
||||||
confirmationModalDeleteClickHandler,
|
|
||||||
} = useRecordingFormHandlers(initialValues?.id);
|
|
||||||
|
|
||||||
const isLayingCategory =
|
const isLayingCategory =
|
||||||
projectFlockKandangLookup?.project_flock?.category === 'LAYING';
|
projectFlockKandangLookup?.project_flock?.category === 'LAYING';
|
||||||
|
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
import { useCallback, useState } from 'react';
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
import { toast } from 'react-hot-toast';
|
|
||||||
import { useModal } from '@/components/Modal';
|
|
||||||
import { RecordingApi } from '@/services/api/production';
|
|
||||||
import {
|
|
||||||
CreateRecordingPayload,
|
|
||||||
UpdateRecordingPayload,
|
|
||||||
} from '@/types/api/production/recording';
|
|
||||||
import { isResponseError } from '@/lib/api-helper';
|
|
||||||
|
|
||||||
export const useRecordingFormHandlers = (initialValuesId?: number) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const deleteModal = useModal();
|
|
||||||
const [recordingFormErrorMessage, setRecordingFormErrorMessage] =
|
|
||||||
useState('');
|
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
|
||||||
|
|
||||||
const createRecordingHandler = useCallback(
|
|
||||||
async (payload: CreateRecordingPayload) => {
|
|
||||||
const res = await RecordingApi.create(payload);
|
|
||||||
if (isResponseError(res)) {
|
|
||||||
setRecordingFormErrorMessage(res.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toast.success(res?.message as string);
|
|
||||||
router.push('/production/recording');
|
|
||||||
},
|
|
||||||
[router]
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateRecordingHandler = useCallback(
|
|
||||||
async (recordingId: number, payload: UpdateRecordingPayload) => {
|
|
||||||
const res = await RecordingApi.update(recordingId, payload);
|
|
||||||
if (res?.status === 'error') {
|
|
||||||
setRecordingFormErrorMessage(res.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toast.success(res?.message as string);
|
|
||||||
router.refresh();
|
|
||||||
router.push('/production/recording');
|
|
||||||
},
|
|
||||||
[router]
|
|
||||||
);
|
|
||||||
|
|
||||||
const deleteRecordingClickHandler = useCallback(() => {
|
|
||||||
deleteModal.openModal();
|
|
||||||
}, [deleteModal]);
|
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = useCallback(async () => {
|
|
||||||
if (!initialValuesId) return;
|
|
||||||
|
|
||||||
setIsDeleteLoading(true);
|
|
||||||
await RecordingApi.delete(initialValuesId);
|
|
||||||
deleteModal.closeModal();
|
|
||||||
toast.success('Successfully delete Recording!');
|
|
||||||
setIsDeleteLoading(false);
|
|
||||||
router.push('/production/recording');
|
|
||||||
}, [deleteModal, initialValuesId, router]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
deleteModal,
|
|
||||||
recordingFormErrorMessage,
|
|
||||||
isDeleteLoading,
|
|
||||||
createRecordingHandler,
|
|
||||||
updateRecordingHandler,
|
|
||||||
deleteRecordingClickHandler,
|
|
||||||
confirmationModalDeleteClickHandler,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -17,7 +17,10 @@ import {
|
|||||||
RecordingEgg,
|
RecordingEgg,
|
||||||
GradingEgg,
|
GradingEgg,
|
||||||
} from '@/types/api/production/recording';
|
} from '@/types/api/production/recording';
|
||||||
import { type FormStepStatus } from '@/types/api/api-general';
|
import {
|
||||||
|
type FormStepStatus,
|
||||||
|
type BaseApiResponse,
|
||||||
|
} from '@/types/api/api-general';
|
||||||
import {
|
import {
|
||||||
RecordingGradingFormSchema,
|
RecordingGradingFormSchema,
|
||||||
RecordingGradingFormValues,
|
RecordingGradingFormValues,
|
||||||
@@ -26,10 +29,12 @@ import {
|
|||||||
} from '../../form/RecordingForm.schema';
|
} from '../../form/RecordingForm.schema';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
import { RecordingApi } from '@/services/api/production';
|
||||||
|
import { isResponseError } from '@/lib/api-helper';
|
||||||
|
import { useModal } from '@/components/Modal';
|
||||||
|
|
||||||
import Card from '@/components/Card';
|
import Card from '@/components/Card';
|
||||||
import StepItem from '@/components/steps/StepItem';
|
import StepItem from '@/components/steps/StepItem';
|
||||||
import { useGradingFormHandlers } from './useGradingFormHandlers';
|
|
||||||
|
|
||||||
interface GradingFormProps {
|
interface GradingFormProps {
|
||||||
type?: 'add' | 'edit' | 'detail';
|
type?: 'add' | 'edit' | 'detail';
|
||||||
@@ -45,6 +50,9 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [formSteps, setFormSteps] = useState<FormStepStatus[] | null>(null);
|
const [formSteps, setFormSteps] = useState<FormStepStatus[] | null>(null);
|
||||||
|
const [gradingFormErrorMessage, setGradingFormErrorMessage] = useState('');
|
||||||
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
const deleteModal = useModal();
|
||||||
|
|
||||||
// ===== API DATA FETCHING =====
|
// ===== API DATA FETCHING =====
|
||||||
// const existingGradingsUrl = useMemo(() => {
|
// const existingGradingsUrl = useMemo(() => {
|
||||||
@@ -61,15 +69,67 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => {
|
|||||||
// ===== DATA PROCESSING =====
|
// ===== DATA PROCESSING =====
|
||||||
|
|
||||||
// ===== FORM HANDLERS =====
|
// ===== FORM HANDLERS =====
|
||||||
const {
|
const createGradingHandler = useCallback(
|
||||||
deleteModal,
|
async (payload: CreateGradingPayload) => {
|
||||||
recordingFormErrorMessage,
|
const res = (await RecordingApi.createGrading(payload)) as
|
||||||
isDeleteLoading,
|
| BaseApiResponse<unknown>
|
||||||
createGradingHandler,
|
| undefined;
|
||||||
updateGradingHandler,
|
|
||||||
deleteRecordingClickHandler,
|
if (!res || isResponseError(res)) {
|
||||||
confirmationModalDeleteClickHandler,
|
setGradingFormErrorMessage(res?.message || 'Failed to add Grading');
|
||||||
} = useGradingFormHandlers(initialValues?.id);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.success(res?.message || 'Successfully added Grading!');
|
||||||
|
router.push('/production/recording');
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateGradingHandler = useCallback(
|
||||||
|
async (gradingId: number, payload: UpdateGradingPayload) => {
|
||||||
|
const res = (await RecordingApi.updateGrading(gradingId, payload)) as
|
||||||
|
| BaseApiResponse<unknown>
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
if (!res || isResponseError(res)) {
|
||||||
|
setGradingFormErrorMessage(res?.message || 'Failed to update Grading');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
toast.success(res?.message || 'Successfully updated Grading!');
|
||||||
|
router.refresh();
|
||||||
|
router.push('/production/recording');
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
|
||||||
|
const deleteRecordingClickHandler = useCallback(() => {
|
||||||
|
deleteModal.openModal();
|
||||||
|
}, [deleteModal]);
|
||||||
|
|
||||||
|
const confirmationModalDeleteClickHandler = useCallback(async () => {
|
||||||
|
if (!initialValues?.id) return;
|
||||||
|
|
||||||
|
setIsDeleteLoading(true);
|
||||||
|
try {
|
||||||
|
const res = (await RecordingApi.deleteGrading(initialValues.id)) as
|
||||||
|
| BaseApiResponse<unknown>
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
if (!res || isResponseError(res)) {
|
||||||
|
setGradingFormErrorMessage(res?.message || 'Failed to delete Grading');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deleteModal.closeModal();
|
||||||
|
toast.success(res?.message || 'Successfully delete Grading!');
|
||||||
|
router.push('/production/recording');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
setGradingFormErrorMessage('Failed to delete Grading');
|
||||||
|
} finally {
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
}
|
||||||
|
}, [deleteModal, initialValues?.id, router]);
|
||||||
|
|
||||||
const formikInitialValues = useMemo(() => {
|
const formikInitialValues = useMemo(() => {
|
||||||
let recordingEggId: number | undefined = initialValues?.id;
|
let recordingEggId: number | undefined = initialValues?.id;
|
||||||
@@ -599,14 +659,14 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{recordingFormErrorMessage && (
|
{gradingFormErrorMessage && (
|
||||||
<div role='alert' className='alert alert-error'>
|
<div role='alert' className='alert alert-error'>
|
||||||
<Icon
|
<Icon
|
||||||
icon='material-symbols:error-outline'
|
icon='material-symbols:error-outline'
|
||||||
width={24}
|
width={24}
|
||||||
height={24}
|
height={24}
|
||||||
/>
|
/>
|
||||||
<span>{recordingFormErrorMessage}</span>
|
<span>{gradingFormErrorMessage}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
import { useCallback, useState } from 'react';
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
import { toast } from 'react-hot-toast';
|
|
||||||
import { useModal } from '@/components/Modal';
|
|
||||||
import { RecordingApi } from '@/services/api/production';
|
|
||||||
import {
|
|
||||||
CreateGradingPayload,
|
|
||||||
UpdateGradingPayload,
|
|
||||||
} from '@/types/api/production/recording';
|
|
||||||
import { isResponseError } from '@/lib/api-helper';
|
|
||||||
import { type BaseApiResponse } from '@/types/api/api-general';
|
|
||||||
|
|
||||||
export const useGradingFormHandlers = (gradingId?: number) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const deleteModal = useModal();
|
|
||||||
const [recordingFormErrorMessage, setRecordingFormErrorMessage] =
|
|
||||||
useState('');
|
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
|
||||||
|
|
||||||
const createGradingHandler = useCallback(
|
|
||||||
async (payload: CreateGradingPayload) => {
|
|
||||||
const res = (await RecordingApi.createGrading(payload)) as
|
|
||||||
| BaseApiResponse<unknown>
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
if (!res || isResponseError(res)) {
|
|
||||||
setRecordingFormErrorMessage(res?.message || 'Failed to add Grading');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success(res?.message || 'Successfully added Grading!');
|
|
||||||
router.push('/production/recording');
|
|
||||||
},
|
|
||||||
[router]
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateGradingHandler = useCallback(
|
|
||||||
async (gradingId: number, payload: UpdateGradingPayload) => {
|
|
||||||
const res = (await RecordingApi.updateGrading(gradingId, payload)) as
|
|
||||||
| BaseApiResponse<unknown>
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
if (!res || isResponseError(res)) {
|
|
||||||
setRecordingFormErrorMessage(
|
|
||||||
res?.message || 'Failed to update Grading'
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toast.success(res?.message || 'Successfully updated Grading!');
|
|
||||||
router.refresh();
|
|
||||||
router.push('/production/recording');
|
|
||||||
},
|
|
||||||
[router]
|
|
||||||
);
|
|
||||||
|
|
||||||
const deleteRecordingClickHandler = useCallback(() => {
|
|
||||||
deleteModal.openModal();
|
|
||||||
}, [deleteModal]);
|
|
||||||
|
|
||||||
const confirmationModalDeleteClickHandler = useCallback(async () => {
|
|
||||||
if (!gradingId) return;
|
|
||||||
|
|
||||||
setIsDeleteLoading(true);
|
|
||||||
try {
|
|
||||||
const res = (await RecordingApi.deleteGrading(gradingId)) as
|
|
||||||
| BaseApiResponse<unknown>
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
if (!res || isResponseError(res)) {
|
|
||||||
setRecordingFormErrorMessage(
|
|
||||||
res?.message || 'Failed to delete Grading'
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
deleteModal.closeModal();
|
|
||||||
toast.success(res?.message || 'Successfully delete Grading!');
|
|
||||||
router.push('/production/recording');
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
setRecordingFormErrorMessage('Failed to delete Grading');
|
|
||||||
} finally {
|
|
||||||
setIsDeleteLoading(false);
|
|
||||||
}
|
|
||||||
}, [deleteModal, gradingId, router]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
deleteModal,
|
|
||||||
recordingFormErrorMessage,
|
|
||||||
isDeleteLoading,
|
|
||||||
createGradingHandler,
|
|
||||||
updateGradingHandler,
|
|
||||||
deleteRecordingClickHandler,
|
|
||||||
confirmationModalDeleteClickHandler,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user