diff --git a/src/components/pages/ApprovalSteps.tsx b/src/components/pages/ApprovalSteps.tsx index 7185e31b..b9590bc2 100644 --- a/src/components/pages/ApprovalSteps.tsx +++ b/src/components/pages/ApprovalSteps.tsx @@ -158,6 +158,7 @@ export const formatGroupedApprovalsToApprovalSteps = ( if (approvalGroup.approvals) { switch (approvalGroup?.approvals[0]?.action) { case 'CREATED': + case 'UPDATED': case 'APPROVED': approvalStatus = 'APPROVED'; break; @@ -256,7 +257,7 @@ const useApprovalSteps = ({ moduleName: string; moduleId: string; params?: { - page: number; + page?: number; limit: number; search?: string; group_step_number?: boolean; diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 77b322bb..4a5c9760 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -23,7 +23,6 @@ import { } from '@/services/api/production'; import { LocationApi } from '@/services/api/master-data'; import { ProductWarehouseApi } from '@/services/api/inventory'; -import { ApprovalApi } from '@/services/api/approval'; import { CreateGrowingRecordingPayload, @@ -32,11 +31,7 @@ import { UpdateLayingRecordingPayload, Recording, } from '@/types/api/production/recording'; -import { - type BaseApiResponse, - BaseApproval, - BaseGroupedApproval, -} from '@/types/api/api-general'; +import { type BaseApiResponse } from '@/types/api/api-general'; import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock'; import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; import { Kandang } from '@/types/api/master-data/kandang'; @@ -55,8 +50,13 @@ import { import { isResponseSuccess, isResponseError } from '@/lib/api-helper'; import { formatDate } from '@/lib/helper'; import toast from 'react-hot-toast'; -import ApprovalSteps from '@/components/pages/ApprovalSteps'; -import { RECORDING_APPROVAL_LINE } from '@/config/approval-line'; +import ApprovalSteps, { + useApprovalSteps, +} from '@/components/pages/ApprovalSteps'; +import { + GROWING_RECORDING_APPROVAL_LINE, + LAYING_RECORDING_APPROVAL_LINE, +} from '@/config/approval-line'; interface RecordingFormProps { type?: 'add' | 'edit' | 'detail'; @@ -434,213 +434,41 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { return approvedProjectFlockKandangsData.data; }, [approvedProjectFlockKandangsData]); - // ===== APPROVAL DATA FETCHING ===== - const approvalHistoryUrl = useMemo(() => { - if (!initialValues?.id || type !== 'detail') return null; - const params = new URLSearchParams({ - module_name: 'RECORDINGS', - module_id: initialValues.id.toString(), - group_step_number: 'true', - limit: '100', - }); - return `${ApprovalApi.basePath}?${params.toString()}`; - }, [initialValues?.id, type]); + const isLayingCategory = + initialValues?.project_flock_category === 'LAYING' || + projectFlockKandangLookup?.project_flock?.category === 'LAYING' || + projectFlockKandangDetail?.project_flock?.category === 'LAYING'; - const { data: approvalHistoryData } = useSWR( - approvalHistoryUrl, - approvalHistoryUrl ? ApprovalApi.getAllFetcher : null - ); + const isGrowingCategory = + initialValues?.project_flock_category === 'GROWING' || + projectFlockKandangLookup?.project_flock?.category === 'GROWING' || + projectFlockKandangDetail?.project_flock?.category === 'GROWING'; - // Helper functions for approval data processing - const groupApprovalsByStep = useCallback( - (approvals: BaseApproval[]): BaseGroupedApproval[] => { - const groups: Record = {}; - for (const approval of approvals) { - if (!groups[approval.step_number]) { - groups[approval.step_number] = { - step_number: approval.step_number, - step_name: approval.step_name, - approvals: [], - }; - } - groups[approval.step_number].approvals.push(approval); - } - return Object.values(groups); + const recordingApprovalLines = useMemo(() => { + if (isLayingCategory) { + return LAYING_RECORDING_APPROVAL_LINE; + } + if (isGrowingCategory) { + return GROWING_RECORDING_APPROVAL_LINE; + } + return GROWING_RECORDING_APPROVAL_LINE; + }, [isLayingCategory, isGrowingCategory]); + + // ===== APPROVAL DATA FETCHING USING HOOK ===== + const { + approvals, + isLoading: approvalsLoading, + refresh: refreshApprovals, + } = useApprovalSteps({ + latestApproval: initialValues?.approval, + approvalLines: recordingApprovalLines, + moduleName: 'RECORDINGS', + moduleId: initialValues?.id?.toString() ?? '', + params: { + limit: 100, + group_step_number: true, }, - [] - ); - - const isGroupedApprovalData = useCallback( - ( - data: BaseApproval[] | BaseGroupedApproval[] - ): data is BaseGroupedApproval[] => { - if (!data || data.length === 0) { - return true; - } - const firstElement = data[0]; - return ( - typeof firstElement === 'object' && - firstElement !== null && - 'approvals' in firstElement && - Array.isArray(firstElement.approvals) - ); - }, - [] - ); - - // Process approval data - const { groupedApprovals, latestApproval } = useMemo(() => { - const latest = initialValues?.approval; - - const rawData = isResponseSuccess(approvalHistoryData) - ? approvalHistoryData.data - : undefined; - - let processedGroupedApprovals: BaseGroupedApproval[] = []; - - if (rawData) { - if (isGroupedApprovalData(rawData)) { - processedGroupedApprovals = rawData as BaseGroupedApproval[]; - } else { - processedGroupedApprovals = groupApprovalsByStep( - rawData as BaseApproval[] - ); - } - } - - if ( - latest && - !processedGroupedApprovals.find( - (g) => g.step_number === latest.step_number - ) - ) { - processedGroupedApprovals.push({ - step_number: latest.step_number, - step_name: latest.step_name, - approvals: [latest], - }); - - processedGroupedApprovals.sort((a, b) => a.step_number - b.step_number); - } - - return { - groupedApprovals: processedGroupedApprovals, - latestApproval: latest, - }; - }, [ - approvalHistoryData, - initialValues?.approval, - isGroupedApprovalData, - groupApprovalsByStep, - ]); - - // Format approval steps for display - const approvalStepsData = useMemo(() => { - if (type !== 'detail') { - return []; - } - - try { - return RECORDING_APPROVAL_LINE.map( - ( - approvalLineItem - ): { - name: string; - status: 'APPROVED' | 'REJECTED' | 'WAITING' | 'IDLE'; - logs: Array<{ - action_by?: string; - date?: string; - notes?: string | null; - }>; - } => { - const approvalGroup = groupedApprovals.find( - (approvalGroupItem) => - approvalGroupItem.step_number === approvalLineItem.step_number - ); - - if (!latestApproval) { - return { - name: approvalLineItem.step_name, - status: 'IDLE', - logs: approvalGroup - ? approvalGroup.approvals.map((approval) => ({ - action_by: approval.action_by.name, - date: approval.action_at, - notes: approval.notes, - })) - : [], - }; - } - - const currentStepNumber = approvalLineItem.step_number; - const latestStepNumber = latestApproval.step_number; - - if (!approvalGroup) { - if (currentStepNumber === latestStepNumber + 1) { - return { - name: approvalLineItem.step_name, - status: 'WAITING', - logs: [], - }; - } - return { - name: approvalLineItem.step_name, - status: 'IDLE', - logs: [], - }; - } - - let approvalStatus: 'APPROVED' | 'REJECTED' | 'WAITING' | 'IDLE' = - 'IDLE'; - - if (currentStepNumber <= latestStepNumber) { - if (approvalGroup.approvals && approvalGroup.approvals.length > 0) { - const latestApprovalInGroup = approvalGroup.approvals.sort( - (a, b) => - new Date(b.action_at).getTime() - - new Date(a.action_at).getTime() - )[0]; - - switch (latestApprovalInGroup.action) { - case 'CREATED': - case 'APPROVED': - approvalStatus = 'APPROVED'; - break; - case 'REJECTED': - approvalStatus = 'REJECTED'; - break; - case 'UPDATED': - approvalStatus = 'APPROVED'; - break; - default: - approvalStatus = 'APPROVED'; - break; - } - } - } else if (currentStepNumber === latestStepNumber + 1) { - approvalStatus = 'WAITING'; - } else { - approvalStatus = 'IDLE'; - } - - const approvalLogs = approvalGroup.approvals.map((approval) => ({ - action_by: approval.action_by.name, - date: approval.action_at, - notes: approval.notes, - })); - - return { - name: approvalGroup.step_name, - status: approvalStatus, - logs: approvalLogs, - }; - } - ); - } catch (error) { - console.warn('Gagal memformat approval steps:', error); - return []; - } - }, [groupedApprovals, latestApproval, type]); + }); // ===== DATA PROCESSING ===== const locationOptions = useMemo(() => { @@ -889,11 +717,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { return options; }, [eggProductsData, initialValues, type, selectedKandang]); - const isLayingCategory = - initialValues?.project_flock_category === 'LAYING' || - projectFlockKandangLookup?.project_flock?.category === 'LAYING' || - projectFlockKandangDetail?.project_flock?.category === 'LAYING'; - // ===== FORMIK SETUP ===== const formikInitialValues = useMemo(() => { let baseValues; @@ -1276,6 +1099,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { if (isResponseSuccess(approveResponse)) { toast.success('Recording berhasil disetujui!'); approveModal.closeModal(); + await refreshApprovals(); router.push('/production/recording'); } else { toast.error( @@ -1298,6 +1122,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { if (isResponseSuccess(rejectResponse)) { toast.success('Recording berhasil ditolak!'); rejectModal.closeModal(); + await refreshApprovals(); router.push('/production/recording'); } else { toast.error( @@ -1586,7 +1411,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } }, [bodyWeightValues, editingAverageIndex, manuallyEditedRows]); - // ===== RENDER ===== return ( <>
@@ -1645,37 +1469,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - {/* Project Flock Info Card */} - {(projectFlockKandangLookup || projectFlockKandangDetail) && ( -
- {/* Approval Steps for all categories */} - {type === 'detail' && approvalStepsData.length > 0 && ( -
- -
- )} - {/* Default approval steps for add/edit modes */} - {type !== 'detail' && ( -
- -
- )} -
+ {type === 'detail' && approvals && !approvalsLoading && ( + )}
{ const [selectedGradingItems, setSelectedGradingItems] = useState( [] ); - const [formSteps, setFormSteps] = useState(null); const [gradingFormErrorMessage, setGradingFormErrorMessage] = useState(''); const [isDeleteLoading, setIsDeleteLoading] = useState(false); const deleteModal = useModal(); @@ -113,213 +98,6 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { ? (projectFlockKandangData.data as unknown as ProjectFlockKandang) : undefined; - // ===== APPROVAL DATA FETCHING ===== - const approvalHistoryUrl = useMemo(() => { - if (!recording?.id || type !== 'detail') return null; - const params = new URLSearchParams({ - module_name: 'RECORDINGS', - module_id: recording.id.toString(), - group_step_number: 'true', - }); - return `${ApprovalApi.basePath}?${params.toString()}`; - }, [recording?.id, type]); - - const { data: approvalHistoryData } = useSWR( - approvalHistoryUrl, - approvalHistoryUrl ? ApprovalApi.getAllFetcher : null - ); - - // Helper functions for approval data processing - const groupApprovalsByStep = useCallback( - (approvals: BaseApproval[]): BaseGroupedApproval[] => { - const groups: Record = {}; - for (const approval of approvals) { - if (!groups[approval.step_number]) { - groups[approval.step_number] = { - step_number: approval.step_number, - step_name: approval.step_name, - approvals: [], - }; - } - groups[approval.step_number].approvals.push(approval); - } - return Object.values(groups); - }, - [] - ); - - const isGroupedApprovalData = useCallback( - ( - data: BaseApproval[] | BaseGroupedApproval[] - ): data is BaseGroupedApproval[] => { - if (!data || data.length === 0) { - return true; - } - const firstElement = data[0]; - return ( - typeof firstElement === 'object' && - firstElement !== null && - 'approvals' in firstElement && - Array.isArray(firstElement.approvals) - ); - }, - [] - ); - - // Process approval data - const { groupedApprovals, latestApproval } = useMemo(() => { - const latest = recording?.approval; - - const rawData = isResponseSuccess(approvalHistoryData) - ? approvalHistoryData.data - : undefined; - - let processedGroupedApprovals: BaseGroupedApproval[] = []; - - if (rawData) { - if (isGroupedApprovalData(rawData)) { - processedGroupedApprovals = rawData as BaseGroupedApproval[]; - } else { - processedGroupedApprovals = groupApprovalsByStep( - rawData as BaseApproval[] - ); - } - } - - if ( - latest && - !processedGroupedApprovals.find( - (g) => g.step_number === latest.step_number - ) - ) { - processedGroupedApprovals.push({ - step_number: latest.step_number, - step_name: latest.step_name, - approvals: [latest], - }); - - processedGroupedApprovals.sort((a, b) => a.step_number - b.step_number); - } - - return { - groupedApprovals: processedGroupedApprovals, - latestApproval: latest, - }; - }, [ - approvalHistoryData, - recording?.approval, - isGroupedApprovalData, - groupApprovalsByStep, - ]); - - // Format approval steps for display - const approvalStepsData = useMemo(() => { - if (!latestApproval || type !== 'detail') { - return []; - } - - try { - return RECORDING_APPROVAL_LINE.map( - ( - approvalLineItem - ): { - name: string; - status: 'APPROVED' | 'REJECTED' | 'WAITING' | 'IDLE'; - logs: Array<{ - action_by?: string; - date?: string; - notes?: string | null; - }>; - } => { - const approvalGroup = groupedApprovals.find( - (approvalGroupItem) => - approvalGroupItem.step_number === approvalLineItem.step_number - ); - - if (!latestApproval) { - return { - name: approvalLineItem.step_name, - status: 'IDLE', - logs: approvalGroup - ? approvalGroup.approvals.map((approval) => ({ - action_by: approval.action_by.name, - date: approval.action_at, - notes: approval.notes, - })) - : [], - }; - } - - const currentStepNumber = approvalLineItem.step_number; - const latestStepNumber = latestApproval.step_number; - - if (!approvalGroup) { - if (currentStepNumber === latestStepNumber + 1) { - return { - name: approvalLineItem.step_name, - status: 'WAITING', - logs: [], - }; - } - return { - name: approvalLineItem.step_name, - status: 'IDLE', - logs: [], - }; - } - - let approvalStatus: 'APPROVED' | 'REJECTED' | 'WAITING' | 'IDLE' = - 'IDLE'; - - if (currentStepNumber <= latestStepNumber) { - if (approvalGroup.approvals && approvalGroup.approvals.length > 0) { - const latestApprovalInGroup = approvalGroup.approvals.sort( - (a, b) => - new Date(b.action_at).getTime() - - new Date(a.action_at).getTime() - )[0]; - - switch (latestApprovalInGroup.action) { - case 'CREATED': - case 'APPROVED': - approvalStatus = 'APPROVED'; - break; - case 'REJECTED': - approvalStatus = 'REJECTED'; - break; - case 'UPDATED': - approvalStatus = 'APPROVED'; - break; - default: - approvalStatus = 'APPROVED'; - break; - } - } - } else if (currentStepNumber === latestStepNumber + 1) { - approvalStatus = 'WAITING'; - } else { - approvalStatus = 'IDLE'; - } - - const approvalLogs = approvalGroup.approvals.map((approval) => ({ - action_by: approval.action_by.name, - date: approval.action_at, - notes: approval.notes, - })); - - return { - name: approvalGroup.step_name, - status: approvalStatus, - logs: approvalLogs, - }; - } - ); - } catch (error) { - console.warn('Gagal memformat approval steps:', error); - return []; - } - }, [groupedApprovals, latestApproval, type]); - const konsumsiBaikEggData = useMemo(() => { if (!recording?.eggs) return null; @@ -568,22 +346,6 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { }; // EFFECTS - useEffect(() => { - const steps: FormStepStatus[] = [ - { - name: 'Recording', - isCompleted: true, - isCurrent: false, - }, - { - name: 'Grading', - isCompleted: type !== 'add', - isCurrent: type === 'add', - }, - ]; - setFormSteps(steps); - }, [type]); - useEffect(() => { if (isGradingExceedsAvailable && currentGradingTotal > 0) { toast.error( @@ -629,37 +391,6 @@ const GradingForm = ({ type = 'add', initialValues }: GradingFormProps) => { - {/* Project Flock Info Card */} -
- {/* Approval Steps */} - {type === 'detail' && approvalStepsData.length > 0 && ( -
- -
- )} - {/* Default approval steps for add/edit modes */} - {type !== 'detail' && ( -
- -
- )} -
-