mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
206 lines
6.4 KiB
TypeScript
206 lines
6.4 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useMemo, useState } from 'react';
|
|
|
|
import { Icon } from '@iconify/react';
|
|
import { BaseApproval } from '@/types/api/api-general';
|
|
import Button from '@/components/Button';
|
|
|
|
import { cn, formatDate } from '@/lib/helper';
|
|
|
|
interface ApprovalStepsV2Props {
|
|
title?: string;
|
|
approvals?: BaseApproval[];
|
|
steps: {
|
|
step_number: number;
|
|
step_name: string;
|
|
}[];
|
|
maxVisibleSteps?: number;
|
|
className?: {
|
|
wrapper?: string;
|
|
stepsWrapper?: string;
|
|
stepsContainer?: string;
|
|
};
|
|
}
|
|
|
|
const ApprovalStepsV2 = ({
|
|
title = 'Progress Details',
|
|
approvals,
|
|
steps,
|
|
maxVisibleSteps = 2,
|
|
className,
|
|
}: ApprovalStepsV2Props) => {
|
|
const [isSeeAll, setIsSeeAll] = useState(false);
|
|
const [formattedApprovals, setFormattedApprovals] = useState<
|
|
(BaseApproval & { isActive: boolean })[]
|
|
>([]);
|
|
|
|
const latestApprovalStepNumber =
|
|
approvals?.[approvals.length - 1].step_number ?? 0;
|
|
|
|
const lastStepNumber = steps[steps.length - 1].step_number;
|
|
|
|
const isLatestApprovalStepNumberLessThanLastStepNumber =
|
|
latestApprovalStepNumber < lastStepNumber;
|
|
|
|
const slicedFormattedApprovals = useMemo(() => {
|
|
return formattedApprovals.slice(0, isSeeAll ? undefined : maxVisibleSteps);
|
|
}, [formattedApprovals, isSeeAll]);
|
|
|
|
const seeMoreClickHandler = () => {
|
|
setIsSeeAll((prevVal) => !prevVal);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (approvals) {
|
|
const tempFormattedApprovals: (BaseApproval & { isActive: boolean })[] =
|
|
[];
|
|
|
|
approvals.forEach((approval) => {
|
|
tempFormattedApprovals.push({
|
|
...approval,
|
|
isActive: true,
|
|
});
|
|
});
|
|
|
|
if (isLatestApprovalStepNumberLessThanLastStepNumber) {
|
|
const latestApprovalStepNumberIndexInSteps = steps.findIndex(
|
|
(step) => step.step_number === latestApprovalStepNumber
|
|
);
|
|
|
|
const slicedSteps = steps.slice(
|
|
latestApprovalStepNumberIndexInSteps + 1
|
|
);
|
|
|
|
slicedSteps.forEach((step) => {
|
|
tempFormattedApprovals.push({
|
|
action: 'APPROVED',
|
|
action_at: new Date().toISOString(),
|
|
action_by: {
|
|
id: 0,
|
|
id_user: 0,
|
|
email: '',
|
|
name: '',
|
|
},
|
|
step_name: step.step_name,
|
|
step_number: step.step_number,
|
|
isActive: false,
|
|
});
|
|
});
|
|
}
|
|
|
|
setFormattedApprovals(tempFormattedApprovals);
|
|
}
|
|
}, [approvals]);
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'w-full p-4 flex flex-col border-b border-base-content/10',
|
|
className?.wrapper
|
|
)}
|
|
>
|
|
<h4 className='text-base font-medium text-base-content/50 font-roboto'>
|
|
{title}
|
|
</h4>
|
|
|
|
<div
|
|
className={cn(
|
|
'mt-6 mb-8 flex flex-col gap-10',
|
|
className?.stepsWrapper
|
|
)}
|
|
>
|
|
{slicedFormattedApprovals.map((approval, idx) => {
|
|
const isApprovalActionCreated = approval.action === 'CREATED';
|
|
const isApprovalActionUpdated = approval.action === 'UPDATED';
|
|
const isApprovalActionRejected = approval.action === 'REJECTED';
|
|
const isApprovalActionApproved = approval.action === 'APPROVED';
|
|
|
|
const approvalIcon =
|
|
isApprovalActionCreated || isApprovalActionUpdated
|
|
? 'heroicons:clock-solid'
|
|
: isApprovalActionRejected
|
|
? 'heroicons:x-circle-solid'
|
|
: isApprovalActionApproved
|
|
? 'heroicons:check-badge-solid'
|
|
: 'heroicons:check-badge-solid';
|
|
|
|
return (
|
|
<div key={idx} className='w-full flex flex-row items-stretch gap-3'>
|
|
<div className='w-fit self-stretch relative'>
|
|
<div className='w-fit h-fit flex flex-col items-start'>
|
|
<Icon
|
|
icon={approvalIcon}
|
|
width={24}
|
|
height={24}
|
|
className={cn({
|
|
'text-warning':
|
|
isApprovalActionCreated || isApprovalActionUpdated,
|
|
'text-error': isApprovalActionRejected,
|
|
'text-success': isApprovalActionApproved,
|
|
'text-base-content/20': !approval.isActive,
|
|
})}
|
|
/>
|
|
|
|
{idx < formattedApprovals.length - 1 && (
|
|
<div className='absolute top-6 left-1/2 -translate-x-1/2 w-0 min-h-full h-[calc(100%)] mx-auto my-2 border border-dashed border-base-content/10' />
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className={cn('w-full flex flex-col gap-1 text-base-content', {
|
|
'text-base-content/20': !approval.isActive,
|
|
})}
|
|
>
|
|
<div className='flex flex-col'>
|
|
<span className='text-xs'>{approval.step_name}</span>
|
|
<span className='text-sm font-semibold'>
|
|
{(isApprovalActionCreated || isApprovalActionUpdated) &&
|
|
'Diajukan oleh '}
|
|
{isApprovalActionRejected && 'Ditolak oleh '}
|
|
{isApprovalActionApproved && 'Disetujui oleh '}
|
|
{approval.isActive ? approval.action_by.name : '...'}
|
|
</span>
|
|
</div>
|
|
|
|
{approval.isActive && (
|
|
<p className='w-full max-w-60 p-3 bg-base-content/5 rounded-xl text-xs text-base-content/50'>
|
|
Created at :{' '}
|
|
{formatDate(approval.action_at, 'DD-MM-YYYY, HH:mm')}
|
|
<br />
|
|
Notes : {approval.notes ?? '-'}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{formattedApprovals.length > maxVisibleSteps && (
|
|
<Button
|
|
variant='outline'
|
|
color='none'
|
|
onClick={seeMoreClickHandler}
|
|
className={cn(
|
|
'px-3 py-2 gap-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-lg transition-all'
|
|
)}
|
|
>
|
|
<Icon
|
|
icon='heroicons-outline:chevron-double-down'
|
|
width={20}
|
|
height={20}
|
|
className={cn('transition-all duration-300', {
|
|
'-rotate-180': isSeeAll,
|
|
})}
|
|
/>
|
|
See {isSeeAll ? 'Less' : 'More'}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ApprovalStepsV2;
|