"use client"; import React, { useState, useEffect } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Loader2, CheckCircle2, XCircle, Clock, FileCheck2 } from "lucide-react"; import { getApprovalRequest, processApprovalLine, cancelApprovalRequest, type ApprovalRequest, } from "@/lib/api/approval"; import { useAuth } from "@/hooks/useAuth"; const statusConfig: Record = { requested: { label: "요청됨", variant: "secondary" }, in_progress: { label: "진행 중", variant: "default" }, approved: { label: "승인됨", variant: "outline" }, rejected: { label: "반려됨", variant: "destructive" }, cancelled: { label: "취소됨", variant: "secondary" }, post_pending: { label: "후결대기", variant: "secondary" }, }; const lineStatusConfig: Record = { waiting: { label: "대기", icon: }, pending: { label: "진행 중", icon: }, approved: { label: "승인", icon: }, rejected: { label: "반려", icon: }, skipped: { label: "건너뜀", icon: }, }; export interface ApprovalDetailEventDetail { requestId: number; } export const ApprovalDetailModal: React.FC = () => { const { user } = useAuth(); const [open, setOpen] = useState(false); const [request, setRequest] = useState(null); const [loading, setLoading] = useState(false); const [comment, setComment] = useState(""); const [isProcessing, setIsProcessing] = useState(false); const [isCancelling, setIsCancelling] = useState(false); // 글로벌 이벤트 수신 useEffect(() => { const handler = async (e: Event) => { const detail = (e as CustomEvent).detail as ApprovalDetailEventDetail; if (!detail?.requestId) return; setOpen(true); setLoading(true); const res = await getApprovalRequest(detail.requestId); setLoading(false); if (res.success && res.data) setRequest(res.data); else setRequest(null); }; window.addEventListener("open-approval-detail-modal", handler); return () => window.removeEventListener("open-approval-detail-modal", handler); }, []); useEffect(() => { if (!open) { setComment(""); setRequest(null); } }, [open]); // 내가 처리할 결재 라인 ID 찾기 const pendingLineId = request?.lines?.find( (l) => l.approver_id === user?.userId && l.status === "pending" )?.line_id; const handleProcess = async (action: "approved" | "rejected") => { if (!pendingLineId) return; setIsProcessing(true); const res = await processApprovalLine(pendingLineId, { action, comment: comment.trim() || undefined }); setIsProcessing(false); if (res.success) { setOpen(false); // 호출한 페이지에 새로고침 알림 window.dispatchEvent(new CustomEvent("approval-processed", { detail: { requestId: request?.request_id, action } })); } }; const handleCancel = async () => { if (!request) return; setIsCancelling(true); const res = await cancelApprovalRequest(request.request_id); setIsCancelling(false); if (res.success) { setOpen(false); window.dispatchEvent(new CustomEvent("approval-processed", { detail: { requestId: request.request_id, action: "cancelled" } })); } }; if (!open) return null; const statusInfo = request ? (statusConfig[request.status] || { label: request.status, variant: "secondary" as const }) : null; const isRequester = request?.requester_id === user?.userId; return ( {loading || !request ? (
) : ( <> {request.title} {statusInfo && ( {statusInfo.label} )} 요청자: {request.requester_name || request.requester_id} {request.requester_dept ? ` (${request.requester_dept})` : ""}
{request.description && (

결재 사유

{request.description}

)}

결재선

{(request.lines || []).map((line) => { const lineStatus = lineStatusConfig[line.status] || { label: line.status, icon: null }; return (
{lineStatus.icon}

{line.approver_label || `${line.step_order}차 결재`} — {line.approver_name || line.approver_id}

{line.approver_position && (

{line.approver_position}

)} {line.comment && (

의견: {line.comment}

)}
{lineStatus.label}
); })}
{pendingLineId && (

결재 의견 (선택사항)