// evaluation-target-action-dialogs.tsx "use client" import * as React from "react" import { Loader2, AlertTriangle, Check, X, MessageSquare } from "lucide-react" import { toast } from "sonner" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Button } from "@/components/ui/button" import { Textarea } from "@/components/ui/textarea" import { Label } from "@/components/ui/label" import { Badge } from "@/components/ui/badge" import { confirmEvaluationTargets, excludeEvaluationTargets, requestEvaluationReview } from "../service" import { EvaluationTargetWithDepartments } from "@/db/schema" // ---------------------------------------------------------------- // 확정 컨펌 다이얼로그 // ---------------------------------------------------------------- interface ConfirmTargetsDialogProps { open: boolean onOpenChange: (open: boolean) => void targets: EvaluationTargetWithDepartments[] onSuccess?: () => void } export function ConfirmTargetsDialog({ open, onOpenChange, targets, onSuccess }: ConfirmTargetsDialogProps) { const [isLoading, setIsLoading] = React.useState(false) // 확정 가능한 대상들 (consensusStatus가 true인 것들) const confirmableTargets = targets.filter( t => t.status === "PENDING" && t.consensusStatus === true ) const handleConfirm = async () => { if (confirmableTargets.length === 0) return setIsLoading(true) try { const targetIds = confirmableTargets.map(t => t.id) const result = await confirmEvaluationTargets(targetIds) if (result.success) { toast.success(result.message) onSuccess?.() onOpenChange(false) } else { toast.error(result.error) } } catch (error) { toast.error("확정 처리 중 오류가 발생했습니다.") } finally { setIsLoading(false) } } return ( 평가 대상 확정

선택된 {targets.length}개 항목 중{" "} {confirmableTargets.length}개 항목 을 확정하시겠습니까?

{confirmableTargets.length !== targets.length && (

의견 일치 상태인 대기중 항목만 확정 가능합니다. ({targets.length - confirmableTargets.length}개 항목 제외됨)

)} {confirmableTargets.length > 0 && (
{confirmableTargets.slice(0, 5).map(target => (
{target.vendorCode} {target.vendorName}
))} {confirmableTargets.length > 5 && (

...외 {confirmableTargets.length - 5}개

)}
)}
취소 {isLoading && } 확정 ({confirmableTargets.length})
) } // ---------------------------------------------------------------- // 제외 컨펌 다이얼로그 // ---------------------------------------------------------------- interface ExcludeTargetsDialogProps { open: boolean onOpenChange: (open: boolean) => void targets: EvaluationTargetWithDepartments[] onSuccess?: () => void } export function ExcludeTargetsDialog({ open, onOpenChange, targets, onSuccess }: ExcludeTargetsDialogProps) { const [isLoading, setIsLoading] = React.useState(false) // 제외 가능한 대상들 (PENDING 상태인 것들) const excludableTargets = targets.filter(t => t.status === "PENDING") const handleExclude = async () => { if (excludableTargets.length === 0) return setIsLoading(true) try { const targetIds = excludableTargets.map(t => t.id) const result = await excludeEvaluationTargets(targetIds) if (result.success) { toast.success(result.message) onSuccess?.() onOpenChange(false) } else { toast.error(result.error) } } catch (error) { toast.error("제외 처리 중 오류가 발생했습니다.") } finally { setIsLoading(false) } } return ( 평가 대상 제외

선택된 {targets.length}개 항목 중{" "} {excludableTargets.length}개 항목 을 제외하시겠습니까?

{excludableTargets.length !== targets.length && (

대기중 상태인 항목만 제외 가능합니다. ({targets.length - excludableTargets.length}개 항목 제외됨)

)} {excludableTargets.length > 0 && (
{excludableTargets.slice(0, 5).map(target => (
{target.vendorCode} {target.vendorName}
))} {excludableTargets.length > 5 && (

...외 {excludableTargets.length - 5}개

)}
)}
취소 {isLoading && } 제외 ({excludableTargets.length})
) } // ---------------------------------------------------------------- // 의견 요청 다이얼로그 // ---------------------------------------------------------------- interface RequestReviewDialogProps { open: boolean onOpenChange: (open: boolean) => void targets: EvaluationTargetWithDepartments[] onSuccess?: () => void } export function RequestReviewDialog({ open, onOpenChange, targets, onSuccess }: RequestReviewDialogProps) { const [isLoading, setIsLoading] = React.useState(false) const [message, setMessage] = React.useState("") // 의견 요청 가능한 대상들 (PENDING 상태인 것들) const reviewableTargets = targets.filter(t => t.status === "PENDING") // 담당자 이메일들 수집 const reviewerEmails = React.useMemo(() => { const emails = new Set() reviewableTargets.forEach(target => { if (target.orderReviewerEmail) emails.add(target.orderReviewerEmail) if (target.procurementReviewerEmail) emails.add(target.procurementReviewerEmail) if (target.qualityReviewerEmail) emails.add(target.qualityReviewerEmail) if (target.designReviewerEmail) emails.add(target.designReviewerEmail) if (target.csReviewerEmail) emails.add(target.csReviewerEmail) }) return Array.from(emails) }, [reviewableTargets]) const handleRequestReview = async () => { if (reviewableTargets.length === 0) return setIsLoading(true) try { const targetIds = reviewableTargets.map(t => t.id) const result = await requestEvaluationReview(targetIds, message) if (result.success) { toast.success(result.message) onSuccess?.() onOpenChange(false) setMessage("") } else { toast.error(result.error) } } catch (error) { toast.error("의견 요청 중 오류가 발생했습니다.") } finally { setIsLoading(false) } } return ( 평가 의견 요청 선택된 평가 대상에 대한 의견을 담당자들에게 요청합니다.
{/* 요약 정보 */}

요청 대상: {reviewableTargets.length}개 평가 항목

받는 사람: {reviewerEmails.length}명의 담당자

{/* 메시지 입력 */}