60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
|
|
import { Steps, Typography } from 'antd';
|
||
|
|
import { IconCheck, IconX, IconClock } from '@tabler/icons-react';
|
||
|
|
import { ApprovalRequestStep } from '@/api/approval';
|
||
|
|
import { COLOR_SUCCESS, COLOR_ERROR, COLOR_PRIMARY, GRAY } from '@/theme/tokens';
|
||
|
|
|
||
|
|
const { Text } = Typography;
|
||
|
|
|
||
|
|
interface Props {
|
||
|
|
steps: ApprovalRequestStep[];
|
||
|
|
currentStep: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
function stepIcon(status: string) {
|
||
|
|
if (status === 'APPROVED') return <IconCheck size={14} style={{ color: COLOR_SUCCESS }} />;
|
||
|
|
if (status === 'REJECTED') return <IconX size={14} style={{ color: COLOR_ERROR }} />;
|
||
|
|
return <IconClock size={14} style={{ color: COLOR_PRIMARY }} />;
|
||
|
|
}
|
||
|
|
|
||
|
|
function stepStatus(status: string): 'finish' | 'error' | 'process' | 'wait' {
|
||
|
|
if (status === 'APPROVED') return 'finish';
|
||
|
|
if (status === 'REJECTED') return 'error';
|
||
|
|
if (status === 'IN_PROGRESS') return 'process';
|
||
|
|
return 'wait';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 결재선 진행 시각화 컴포넌트.
|
||
|
|
* Steps 수평 렌더링 + 단계별 상태 색상.
|
||
|
|
*/
|
||
|
|
export default function ApprovalProgress({ steps, currentStep }: Props) {
|
||
|
|
const items = steps.map((s) => ({
|
||
|
|
title: (
|
||
|
|
<Text style={{ fontSize: 12, fontWeight: 600, color: GRAY[700] }}>
|
||
|
|
{s.approverName}
|
||
|
|
</Text>
|
||
|
|
),
|
||
|
|
description: (
|
||
|
|
<div style={{ fontSize: 11, color: GRAY[500] }}>
|
||
|
|
{s.comment && <div style={{ marginTop: 2, color: GRAY[600] }}>{s.comment}</div>}
|
||
|
|
{s.processedAt && (
|
||
|
|
<div style={{ marginTop: 2 }}>
|
||
|
|
{s.processedAt.slice(0, 10)}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
),
|
||
|
|
icon: stepIcon(s.status),
|
||
|
|
status: stepStatus(s.status),
|
||
|
|
}));
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Steps
|
||
|
|
size="small"
|
||
|
|
current={currentStep - 1}
|
||
|
|
items={items}
|
||
|
|
style={{ marginTop: 8 }}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
}
|