From 82c1645d923c15e45272912ff45c96e99ea8668a Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Thu, 30 Oct 2025 10:45:41 +0700 Subject: [PATCH] chore(FE-91): rework ApprovalSteps and create helper function for formatting approval workflow --- src/components/pages/ApprovalSteps.tsx | 169 +++++++++++++++++++++---- 1 file changed, 143 insertions(+), 26 deletions(-) diff --git a/src/components/pages/ApprovalSteps.tsx b/src/components/pages/ApprovalSteps.tsx index 4022e254..184b3f20 100644 --- a/src/components/pages/ApprovalSteps.tsx +++ b/src/components/pages/ApprovalSteps.tsx @@ -3,11 +3,24 @@ import Steps from '@/components/steps/Steps'; import StepItem from '@/components/steps/StepItem'; import Tooltip from '@/components/Tooltip'; -import { formatDate } from '@/lib/helper'; -import { ApprovalsLine } from '@/types/api/api-general'; +import { cn, formatDate } from '@/lib/helper'; +import { BaseApproval, BaseGroupedApproval } from '@/types/api/api-general'; +import { ApprovalLine } from '@/types/config/constant'; + +export type ApprovalStepStatus = 'APPROVED' | 'REJECTED' | 'WAITING' | 'IDLE'; + +export type ApprovalStepLog = { + action_by?: string; + date?: string; + notes?: string | null; +}; interface ApprovalStepsProps { - approvals: ApprovalsLine; + approvals: { + name?: string; + status: ApprovalStepStatus; + logs?: ApprovalStepLog[]; + }[]; } const ApprovalSteps = ({ approvals }: ApprovalStepsProps) => { @@ -15,17 +28,23 @@ const ApprovalSteps = ({ approvals }: ApprovalStepsProps) => { {approvals.map((approval, idx) => { const stepItemColor = - approval.status === 'approved' + approval.status === 'APPROVED' ? 'success' - : approval.status === 'rejected' + : approval.status === 'REJECTED' ? 'error' + : approval.status === 'WAITING' + ? 'warning' : undefined; const stepItemIcon = - approval.status === 'approved' + approval.status === 'APPROVED' ? 'material-symbols:check-rounded' - : approval.status === 'rejected' + : approval.status === 'REJECTED' ? 'material-symbols:close-rounded' + : approval.status === 'WAITING' + ? 'pajamas:dash-circle' + : approval.logs && approval.logs.length > 0 + ? 'material-symbols:info-outline-rounded' : 'bxs:hourglass'; return ( @@ -33,27 +52,53 @@ const ApprovalSteps = ({ approvals }: ApprovalStepsProps) => { key={idx} color={stepItemColor} icon={ - approval.status !== 'waiting' && ( - - {formatDate(approval.date, 'YYYY-MM-DD')} - Oleh: {approval.action_by} - Catatan: {approval.notes} - - } - > - - - ) + + {approval.logs && approval.logs.length > 0 && ( +
+ {approval.logs?.map((approvalLog, logIdx) => ( +
+ {approvalLog.date && ( + + {formatDate( + approvalLog.date, + 'YYYY-MM-DD, HH:mm:ss' + )} + + )} + Oleh: {approvalLog.action_by ?? '-'} + Catatan: {approvalLog.notes ?? '-'} +
+ ))} +
+ )} + + } + > + +
} > - {approval.role} + {approval.name} ); })} @@ -61,4 +106,76 @@ const ApprovalSteps = ({ approvals }: ApprovalStepsProps) => { ); }; +export const formatGroupedApprovalsToApprovalSteps = ( + approvalLine: ApprovalLine, + groupedApprovals: BaseGroupedApproval[], + latestApproval: BaseApproval +): ApprovalStepsProps['approvals'] => { + const formattedApprovalSteps: ApprovalStepsProps['approvals'] = + approvalLine.map((approvalLineItem) => { + const approvalGroup = groupedApprovals.find( + (approvalGroupItem) => + approvalGroupItem.step_number === approvalLineItem.step_number + ); + + const currentStepNumber = approvalLineItem.step_number; + const lastStepNumber = + groupedApprovals[groupedApprovals.length - 1].step_number; + + if (!approvalGroup && currentStepNumber <= lastStepNumber) { + throw new Error( + `Approval dengan ${approvalLineItem.step_name} tidak ditemukan!` + ); + } + + if (!approvalGroup) { + const isWaiting = currentStepNumber === latestApproval.step_number + 1; + + return { + name: approvalLineItem.step_name, + status: isWaiting ? 'WAITING' : 'IDLE', + }; + } + + let approvalStatus: ApprovalStepStatus; + + if (approvalGroup.step_number <= latestApproval.step_number) { + switch (approvalGroup.approvals[0].action) { + case 'CREATED': + case 'APPROVED': + approvalStatus = 'APPROVED'; + break; + + case 'REJECTED': + approvalStatus = 'REJECTED'; + break; + + default: + approvalStatus = 'IDLE'; + break; + } + } else if (approvalGroup.step_number === latestApproval.step_number + 1) { + approvalStatus = 'WAITING'; + } else { + approvalStatus = 'IDLE'; + } + + const approvalLogs: ApprovalStepLog[] = 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, + }; + }); + + return formattedApprovalSteps; +}; + export default ApprovalSteps;