"use client" import * as React from "react" import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Badge } from "@/components/ui/badge" import { Switch } from "@/components/ui/switch" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription, } from "@/components/ui/form" import { Loader, Wand2, Info, Eye } from "lucide-react" import { toast } from "sonner" import { generateVariableNamesSchema, type GenerateVariableNamesSchema } from "@/lib/gtc-contract/gtc-clauses/validations" import { generateVariableNames, getGtcClausesTree } from "@/lib/gtc-contract/gtc-clauses/service" import { type GtcClauseTreeView } from "@/db/schema/gtc" import { useSession } from "next-auth/react" interface GenerateVariableNamesDialogProps extends React.ComponentPropsWithRef { documentId: number document: any } export function GenerateVariableNamesDialog({ documentId, document, ...props }: GenerateVariableNamesDialogProps) { const [isGenerating, startGenerating] = React.useTransition() const [clauses, setClauses] = React.useState([]) const [previewClauses, setPreviewClauses] = React.useState([]) const [showPreview, setShowPreview] = React.useState(false) const { data: session } = useSession() const currentUserId = React.useMemo(() => { return session?.user?.id ? Number(session.user.id) : null }, [session]) React.useEffect(() => { if (props.open && documentId) { loadClauses() } }, [props.open, documentId]) const loadClauses = async () => { try { const tree = await getGtcClausesTree(documentId) const flatClauses = flattenTree(tree) setClauses(flatClauses) } catch (error) { console.error("Error loading clauses:", error) } } const form = useForm({ resolver: zodResolver(generateVariableNamesSchema), defaultValues: { documentId, prefix: "CLAUSE", includeVendorCode: false, vendorCode: "", }, }) const watchedPrefix = form.watch("prefix") const watchedIncludeVendorCode = form.watch("includeVendorCode") const watchedVendorCode = form.watch("vendorCode") // 미리보기 생성 React.useEffect(() => { if (clauses.length > 0) { generatePreview() } }, [clauses, watchedPrefix, watchedIncludeVendorCode, watchedVendorCode]) const generatePreview = () => { const basePrefix = watchedIncludeVendorCode && watchedVendorCode ? `${watchedVendorCode}_${watchedPrefix}` : watchedPrefix console.log(basePrefix,"basePrefix") const updated = clauses.slice(0, 5).map(clause => { console.log(clause.fullPath,"clause.fullPath") const pathPrefix = clause.fullPath?.replace(/\./g, "_") || clause.itemNumber.replace(/\./g, "_") const varPrefix = `${basePrefix}_${pathPrefix}` return { ...clause, previewNumberVar: `${varPrefix}_NUMBER`, previewSubtitleVar: `${varPrefix}_SUBTITLE`, previewContentVar: `${varPrefix}_CONTENT`, } }) setPreviewClauses(updated as any) } async function onSubmit(data: GenerateVariableNamesSchema) { startGenerating(async () => { if (!currentUserId) { toast.error("로그인이 필요합니다") return } try { const result = await generateVariableNames({ ...data, updatedById: currentUserId }) if (result.error) { toast.error(`에러: ${result.error}`) return } form.reset() props.onOpenChange?.(false) toast.success("PDFTron 변수명이 생성되었습니다.") } catch (error) { toast.error("변수명 생성 중 오류가 발생했습니다.") } }) } function handleDialogOpenChange(nextOpen: boolean) { if (!nextOpen) { form.reset() setShowPreview(false) } props.onOpenChange?.(nextOpen) } const clausesWithoutVariables = clauses.filter(clause => !clause.hasAllVariableNames) const clausesWithVariables = clauses.filter(clause => clause.hasAllVariableNames) return ( PDFTron 변수명 자동 생성 문서의 모든 조항에 대해 PDFTron 변수명을 자동으로 생성합니다. {/* 문서 및 조항 현황 */}
문서 및 조항 현황
문서 타입
{document?.type === "standard" ? "표준" : "프로젝트"}
총 조항 수
{clauses.length}개
변수명 설정 완료
{clausesWithVariables.length}개
변수명 미설정
{clausesWithoutVariables.length}개
{document?.project && (
프로젝트: {document.project.name} ({document.project.code})
)}
{/* 기본 접두사 */} ( 기본 접두사 모든 변수명의 시작에 사용될 접두사입니다. )} /> {/* 벤더 코드 포함 여부 */} (
벤더 코드 포함 변수명에 벤더 코드를 포함시킵니다. (벤더별 GTC 용)
)} /> {/* 벤더 코드 입력 */} {watchedIncludeVendorCode && ( ( 벤더 코드 변수명에 포함될 벤더 식별 코드입니다. )} /> )} /> {/* 미리보기 토글 */}
{/* 미리보기 */} {showPreview && (
변수명 미리보기 (상위 5개 조항)
{previewClauses.map((clause: any) => (
{clause.itemNumber} {clause.subtitle}
채번: {clause.previewNumberVar}
소제목: {clause.previewSubtitleVar}
상세항목: {clause.previewContentVar}
))}
)}
) } // 트리를 평면 배열로 변환하는 유틸리티 함수 function flattenTree(tree: any[]): any[] { const result: any[] = [] function traverse(nodes: any[]) { for (const node of nodes) { result.push(node) if (node.children && node.children.length > 0) { traverse(node.children) } } } traverse(tree) return result }