"use client" import * as React from "react" import { zodResolver } from "@hookform/resolvers/zod" import { useForm, useFieldArray } from "react-hook-form" import * as z from "zod" import { toast } from "sonner" import { CheckCircle2, AlertCircle, Building2 } from "lucide-react" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Badge } from "@/components/ui/badge" import { Alert, AlertDescription } from "@/components/ui/alert" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { PeriodicEvaluationView } from "@/db/schema" import { finalizeEvaluations } from "../service" // 등급 옵션 const GRADE_OPTIONS = [ { value: "S", label: "S등급 (90점 이상)" }, { value: "A", label: "A등급 (80-89점)" }, { value: "B", label: "B등급 (70-79점)" }, { value: "C", label: "C등급 (60-69점)" }, { value: "D", label: "D등급 (60점 미만)" }, ] as const // 점수에 따른 등급 계산 const calculateGrade = (score: number): string => { if (score >= 90) return "S" if (score >= 80) return "A" if (score >= 70) return "B" if (score >= 60) return "C" return "D" } // 개별 평가 스키마 const evaluationItemSchema = z.object({ id: z.number(), vendorName: z.string(), vendorCode: z.string(), evaluationScore: z.number().nullable(), finalScore: z.number() .min(0, "점수는 0 이상이어야 합니다"), // .max(100, "점수는 100 이하여야 합니다"), finalGrade: z.enum(["S", "A", "B", "C", "D"]), }) // 전체 폼 스키마 const finalizeEvaluationSchema = z.object({ evaluations: z.array(evaluationItemSchema).min(1, "확정할 평가가 없습니다"), }) type FinalizeEvaluationFormData = z.infer interface FinalizeEvaluationDialogProps { open: boolean onOpenChange: (open: boolean) => void evaluations: PeriodicEvaluationView[] onSuccess?: () => void } export function FinalizeEvaluationDialog({ open, onOpenChange, evaluations, onSuccess, }: FinalizeEvaluationDialogProps) { const [isLoading, setIsLoading] = React.useState(false) const form = useForm({ resolver: zodResolver(finalizeEvaluationSchema), defaultValues: { evaluations: [], }, }) const { fields, update } = useFieldArray({ control: form.control, name: "evaluations", }) // evaluations가 변경될 때 폼 초기화 React.useEffect(() => { if (evaluations.length > 0) { const formData = evaluations.map(evaluation => ({ id: evaluation.id, vendorName: evaluation.vendorName || "", vendorCode: evaluation.vendorCode || "", evaluationScore: evaluation.evaluationScore || null, finalScore: Number(evaluation.evaluationScore || 0), finalGrade: calculateGrade(Number(evaluation.evaluationScore || 0)), })) form.reset({ evaluations: formData }) } }, [evaluations, form]) // 점수 변경 시 등급 자동 계산 const handleScoreChange = (index: number, score: number) => { const currentEvaluation = form.getValues(`evaluations.${index}`) const newGrade = calculateGrade(score) update(index, { ...currentEvaluation, finalScore: score, finalGrade: newGrade, }) } // 폼 제출 const onSubmit = async (data: FinalizeEvaluationFormData) => { try { setIsLoading(true) const finalizeData = data.evaluations.map(evaluation => ({ id: evaluation.id, finalScore: evaluation.finalScore, finalGrade: evaluation.finalGrade, })) await finalizeEvaluations(finalizeData) toast.success("평가가 확정되었습니다", { description: `${data.evaluations.length}건의 평가가 최종 확정되었습니다.`, }) onSuccess?.() onOpenChange(false) } catch (error) { console.error("Failed to finalize evaluations:", error) toast.error("평가 확정 실패", { description: error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다.", }) } finally { setIsLoading(false) } } return ( 평가 확정 검토가 완료된 평가의 최종 점수와 등급을 확정합니다. 확정 후에는 수정이 제한됩니다. 확정할 평가: {evaluations.length}건
평가 점수는 리뷰어들의 평가를 바탕으로 계산된 값을 기본으로 하며, 필요시 조정 가능합니다.
협력업체 평가점수 최종점수 최종등급 {fields.map((field, index) => (
{form.watch(`evaluations.${index}.vendorName`)}
{form.watch(`evaluations.${index}.vendorCode`)}
{form.watch(`evaluations.${index}.evaluationScore`) !== null ? ( {Number(form.watch(`evaluations.${index}.evaluationScore`)).toFixed(1)}점 ) : ( - )}
( { const value = parseFloat(e.target.value) field.onChange(value) if (!isNaN(value)) { handleScoreChange(index, value) } }} className="text-center font-mono" /> )} /> ( )} />
))}
) }