"use client" import * as React from "react" import { zodResolver } from "@hookform/resolvers/zod" import { useForm, useFieldArray } from "react-hook-form" import { z } from "zod" import { toast } from "sonner" import { Plus, X, Trash2 } from "lucide-react" import { useTransition } from "react" import { Button } from "@/components/ui/button" import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from "@/components/ui/sheet" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" import { ScrollArea } from "@/components/ui/scroll-area" // 기존 서비스 함수 import import { getEsgEvaluationDetails, createEsgEvaluationWithItemsEnhanced, updateEsgEvaluationWithItems } from "../service" // 기존 서비스 파일 경로에 맞게 수정 import { EsgEvaluationsView } from "@/db/schema" // 폼 스키마 정의 const evaluationFormSchema = z.object({ serialNumber: z.string().min(1, "시리얼번호는 필수입니다"), category: z.string().min(1, "분류는 필수입니다"), inspectionItem: z.string().min(1, "점검항목은 필수입니다"), evaluationItems: z.array( z.object({ evaluationItem: z.string().min(1, "평가항목은 필수입니다"), evaluationItemDescription: z.string().min(1, "평가항목 설명은 필수입니다"), answerOptions: z.array( z.object({ answerText: z.string().min(1, "답변 내용은 필수입니다"), score: z.coerce.number().min(0, "점수는 0 이상이어야 합니다"), }) ).min(1, "최소 1개의 답변 옵션이 필요합니다"), }) ).min(1, "최소 1개의 평가항목이 필요합니다"), }) type EvaluationFormData = z.infer interface EsgEvaluationFormSheetProps { open: boolean onOpenChange: (open: boolean) => void evaluation: EsgEvaluationsView | null onSuccess: () => void } export function EsgEvaluationFormSheet({ open, onOpenChange, evaluation, onSuccess, }: EsgEvaluationFormSheetProps) { const [isPending, startTransition] = useTransition() const isEdit = !!evaluation const form = useForm({ resolver: zodResolver(evaluationFormSchema), defaultValues: { serialNumber: "", category: "", inspectionItem: "", evaluationItems: [ { evaluationItem: "", evaluationItemDescription: "", answerOptions: [ { answerText: "", score: 0 }, { answerText: "", score: 0 }, ], }, ], }, }) const { fields, append, remove } = useFieldArray({ control: form.control, name: "evaluationItems", }) // 편집 모드일 때 기존 데이터 로드 React.useEffect(() => { if (open && isEdit && evaluation) { // 기존 서비스 함수를 사용하여 상세 데이터 로드 startTransition(async () => { try { const details = await getEsgEvaluationDetails(evaluation.id) console.log(details) if (details) { form.reset({ serialNumber: details.serialNumber, category: details.category, inspectionItem: details.inspectionItem, evaluationItems: details.evaluationItems?.map((item) => ({ evaluationItem: item.evaluationItem, evaluationItemDescription: item.evaluationItemDescription, answerOptions: item.answerOptions?.map((option) => ({ answerText: option.answerText, score: parseFloat(option.score), })) || [], })) || [], }) } } catch (error) { console.error('Error loading evaluation for edit:', error) toast.error(error instanceof Error ? error.message : '편집할 데이터를 불러오는데 실패했습니다.') } }) } else if (open && !isEdit) { // 새 생성 모드 form.reset({ serialNumber: "", category: "", inspectionItem: "", evaluationItems: [ { evaluationItem: "", evaluationItemDescription: "", answerOptions: [ { answerText: "", score: 0 }, { answerText: "", score: 0 }, ], }, ], }) } }, [open, isEdit, evaluation, form]) const onSubmit = async (data: EvaluationFormData) => { startTransition(async () => { try { // 폼 데이터를 서비스 함수에 맞는 형태로 변환 const evaluationData = { serialNumber: data.serialNumber, category: data.category, inspectionItem: data.inspectionItem, } const items = data.evaluationItems.map(item => ({ evaluationItem: item.evaluationItem, evaluationItemDescription: item.evaluationItemDescription, answerOptions: item.answerOptions.map(option => ({ answerText: option.answerText, score: option.score, })) })) if (isEdit && evaluation) { // 수정 - 전체 평가표 수정 await updateEsgEvaluationWithItems(evaluation.id, evaluationData, items) toast.success('평가표가 수정되었습니다.') } else { // 생성 - 평가표와 항목들 함께 생성 await createEsgEvaluationWithItemsEnhanced(evaluationData, items) toast.success('평가표가 생성되었습니다.') } onSuccess() onOpenChange(false) } catch (error) { console.error('Error saving evaluation:', error) toast.error( error instanceof Error ? error.message : '저장 중 오류가 발생했습니다.' ) } }) } if (!open) return null return ( {/* 고정 헤더 */} {isEdit ? 'ESG 평가표 수정' : '새 ESG 평가표 생성'} {isEdit ? '평가표의 정보를 수정합니다.' : '새로운 ESG 평가표를 생성합니다.'}
{/* 스크롤 가능한 콘텐츠 영역 */}
{/* 기본 정보 */} 기본 정보 ( 시리얼번호 )} /> ( 분류 )} /> ( 점검항목 )} /> {/* 평가항목들 */}
평가항목들 각 평가항목과 해당 답변 옵션들을 설정합니다.
{fields.map((field, index) => ( remove(index)} canRemove={fields.length > 1} disabled={isPending} /> ))}
{/* 고정 버튼 영역 */}
) } // 평가항목 개별 폼 컴포넌트 interface EvaluationItemFormProps { index: number form: any onRemove: () => void canRemove: boolean disabled?: boolean } function EvaluationItemForm({ index, form, onRemove, canRemove, disabled = false, }: EvaluationItemFormProps) { const { fields, append, remove } = useFieldArray({ control: form.control, name: `evaluationItems.${index}.answerOptions`, }) return (
평가항목 {index + 1} {canRemove && ( )}
( 평가항목 )} /> ( 평가항목 설명