'use client' import { useState, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Badge } from '@/components/ui/badge'; import { Separator } from '@/components/ui/separator'; import { toast } from 'sonner'; import { Loader2, Search, FileText, Clock, User, AlertCircle } from 'lucide-react'; // API 함수 및 타입 import { getApprovalDetail, getApprovalContent } from '@/lib/knox-api/approval/approval'; import type { ApprovalDetailResponse, ApprovalContentResponse, ApprovalLine } from '@/lib/knox-api/approval/approval'; // 상태/역할 텍스트 매핑 (mock util 대체) const getStatusText = (status: string) => { const map: Record = { '-3': '암호화실패', '-2': '암호화중', '-1': '예약상신', '0': '보류', '1': '진행중', '2': '완결', '3': '반려', '4': '상신취소', '5': '전결', '6': '후완결', }; return map[status] || status; }; const getRoleText = (role: string) => { const map: Record = { '0': '기안', '1': '결재', '2': '합의', '3': '후결', '4': '병렬합의', '7': '병렬결재', '9': '통보', }; return map[role] || role; }; interface ApprovalDetailProps { initialApInfId?: string; } interface ApprovalDetailData { detail: ApprovalDetailResponse['data']; content: ApprovalContentResponse['data']; } // 첨부파일 타입 (가이드에 명확히 정의되어 있지 않아 필드 일부 추정) interface ApprovalAttachment { fileName?: string; fileSize?: string; downloadUrl?: string; fileId?: string; [key: string]: unknown; // 기타 필드 허용 } export default function ApprovalDetail({ initialApInfId = '' }: ApprovalDetailProps) { const [apInfId, setApInfId] = useState(initialApInfId); const [approvalData, setApprovalData] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const fetchApprovalDetail = async (id: string) => { if (!id.trim()) { toast.error('결재 ID를 입력해주세요.'); return; } setIsLoading(true); setError(null); setApprovalData(null); try { const [detailResponse, contentResponse] = await Promise.all([ getApprovalDetail(id), getApprovalContent(id) ]); if (detailResponse.result === 'SUCCESS' && contentResponse.result === 'SUCCESS') { setApprovalData({ detail: detailResponse.data, content: contentResponse.data }); } else { setError('결재 정보를 가져오는데 실패했습니다.'); toast.error('결재 정보를 가져오는데 실패했습니다.'); } } catch (err) { console.error('결재 상세 조회 오류:', err); setError('결재 정보를 가져오는 중 오류가 발생했습니다.'); toast.error('결재 정보를 가져오는 중 오류가 발생했습니다.'); } finally { setIsLoading(false); } }; const formatDate = (dateString: string) => { if (!dateString || dateString.length < 14) return dateString; // YYYYMMDDHHMMSS 형식을 YYYY-MM-DD HH:MM:SS로 변환 const year = dateString.substring(0, 4); const month = dateString.substring(4, 6); const day = dateString.substring(6, 8); const hour = dateString.substring(8, 10); const minute = dateString.substring(10, 12); const second = dateString.substring(12, 14); return `${year}-${month}-${day} ${hour}:${minute}:${second}`; }; const getSecurityTypeText = (type: string) => { const typeMap: Record = { 'PERSONAL': '개인', 'CONFIDENTIAL': '기밀', 'CONFIDENTIAL_STRICT': '극기밀' }; return typeMap[type] || type; }; const getSecurityTypeBadgeVariant = (type: string) => { switch (type) { case 'PERSONAL': return 'default'; case 'CONFIDENTIAL': return 'secondary'; case 'CONFIDENTIAL_STRICT': return 'destructive'; default: return 'outline'; } }; const getStatusBadgeVariant = (status: string) => { switch (status) { case '2': // 완결 return 'default'; case '1': // 진행중 return 'secondary'; case '3': // 반려 return 'destructive'; case '4': // 상신취소 return 'outline'; default: return 'outline'; } }; // 첨부파일 다운로드 헬퍼 const handleDownload = async (attachment: ApprovalAttachment) => { try { // 1) downloadUrl 이 이미 포함된 경우 if (attachment.downloadUrl) { window.open(attachment.downloadUrl, '_blank'); return; } // 2) fileId + 별도 엔드포인트 조합 (가이드에 명시되지 않았으므로 best-effort 처리) if (attachment.fileId) { const url = `${process.env.NEXT_PUBLIC_KNOX_API_BASE_URL || ''}/approval/api/v2.0/attachments/${attachment.fileId}`; const resp = await fetch(url, { method: 'GET', headers: { 'System-ID': process.env.NEXT_PUBLIC_KNOX_SYSTEM_ID || '', }, }); if (!resp.ok) throw new Error('다운로드 실패'); // blob 생성 후 브라우저 다운로드 const blob = await resp.blob(); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = attachment.fileName || 'attachment'; link.click(); URL.revokeObjectURL(link.href); return; } toast.error('다운로드 URL 정보를 찾을 수 없습니다.'); } catch (err) { console.error('첨부파일 다운로드 오류:', err); toast.error('첨부파일 다운로드 중 오류가 발생했습니다.'); } }; // 초기 로딩 (initialApInfId가 있는 경우) useEffect(() => { if (initialApInfId) { fetchApprovalDetail(initialApInfId); } }, [initialApInfId]); return ( 결재 상세 조회 결재 ID를 입력하여 상세 정보를 조회합니다. {/* 검색 영역 */}
setApInfId(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && fetchApprovalDetail(apInfId)} />
{/* 에러 메시지 */} {error && (
오류

{error}

)} {/* 결재 상세 정보 */} {approvalData && (
{/* 기본 정보 */}

기본 정보

{approvalData.detail.apInfId}

{approvalData.detail.systemId}

{approvalData.detail.subject}

{formatDate(approvalData.detail.sbmDt)}

{getStatusText(approvalData.detail.status)}
{getSecurityTypeText(approvalData.detail.docSecuType)}
{approvalData.detail.urgYn === 'Y' ? '긴급' : '일반'}

{approvalData.detail.sbmLang}

{/* 결재 내용 */}

결재 내용

{approvalData.content.contentsType}
                    {approvalData.content.contents}
                  
{/* 결재 경로 */}

결재 경로

{approvalData.detail.aplns.map((apln: ApprovalLine, index: number) => (
{apln.seq}

{apln.userId || apln.epId || '-'}

{apln.emailAddress || '-'}

{getRoleText(apln.role)}
{getStatusText(apln.aplnStatsCode)}
{apln.arbPmtYn === 'Y' && ( 전결권한 )} {apln.contentsMdfyPmtYn === 'Y' && ( 본문수정 )} {apln.aplnMdfyPmtYn === 'Y' && ( 경로변경 )}
))}
{/* 첨부파일 */} {approvalData.detail.attachments && approvalData.detail.attachments.length > 0 && ( <>

첨부파일

{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} {approvalData.detail.attachments.map((attachment: any, index: number) => (

{attachment.fileName || `첨부파일 ${index + 1}`}

{attachment.fileSize || '크기 정보 없음'}

))}
)}
)}
); }