"use client" import * as React from "react" import { zodResolver } from "@hookform/resolvers/zod" import { Loader, Info } from "lucide-react" import { useForm } from "react-hook-form" import { toast } from "sonner" import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, } from "@/components/ui/sheet" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" import { Badge } from "@/components/ui/badge" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription, } from "@/components/ui/form" import { type GtcClauseTreeView } from "@/db/schema/gtc" import { updateGtcClauseSchema, type UpdateGtcClauseSchema } from "@/lib/gtc-contract/gtc-clauses/validations" import { updateGtcClause } from "@/lib/gtc-contract/gtc-clauses/service" import { useSession } from "next-auth/react" import { MarkdownImageEditor } from "./markdown-image-editor" interface ClauseImage { id: string url: string fileName: string size: number savedName?: string mimeType?: string width?: number height?: number hash?: string } export interface UpdateGtcClauseSheetProps extends React.ComponentPropsWithRef { gtcClause: GtcClauseTreeView | null documentId: number } export function UpdateGtcClauseSheet({ gtcClause, documentId, ...props }: UpdateGtcClauseSheetProps) { const [isUpdatePending, startUpdateTransition] = React.useTransition() const { data: session } = useSession() const [images, setImages] = React.useState([]) const [rawFiles, setRawFiles] = React.useState([]) const [removedImageIds, setRemovedImageIds] = React.useState([]) const currentUserId = React.useMemo(() => { return session?.user?.id ? Number(session.user.id) : null }, [session]) const form = useForm({ resolver: zodResolver(updateGtcClauseSchema), defaultValues: { itemNumber: "", category: "", subtitle: "", content: "", // numberVariableName: "", // subtitleVariableName: "", // contentVariableName: "", editReason: "", isActive: true, }, }) React.useEffect(() => { if (gtcClause) { form.reset({ itemNumber: gtcClause.itemNumber, category: gtcClause.category || "", subtitle: gtcClause.subtitle, content: gtcClause.content || "", editReason: "", isActive: gtcClause.isActive, }) // ✅ 초기 이미지 세팅 setImages((gtcClause.images as any[]) || []) setRawFiles([]) setRemovedImageIds([]) } }, [gtcClause, form]) async function onSubmit(input: UpdateGtcClauseSchema) { startUpdateTransition(async () => { if (!gtcClause || !currentUserId) { toast.error("조항 정보를 찾을 수 없습니다.") return } try { const result = await updateGtcClause(gtcClause.id, { ...input, images: images, // 이미지 배열 추가 updatedById: currentUserId, }) if (result.error) { toast.error(result.error) return } form.reset() props.onOpenChange?.(false) toast.success("GTC 조항이 업데이트되었습니다!") } catch (error) { toast.error("조항 업데이트 중 오류가 발생했습니다.") } }) } const getDepthBadge = (depth: number) => { const levels = ["1단계", "2단계", "3단계", "4단계", "5단계+"] return levels[depth] || levels[4] } const handleContentImageChange = (content: string, newImages: ClauseImage[]) => { form.setValue("content", content) setImages(newImages) } return ( GTC 조항 수정 조항 정보를 수정하고 변경사항을 저장하세요 {/* 조항 정보 표시 */}
현재 조항 정보
위치: {getDepthBadge(gtcClause?.depth || 0)} {gtcClause?.fullPath && ( {gtcClause.fullPath} )}
{gtcClause?.parentItemNumber && (
부모 조항: {gtcClause.parentItemNumber} - {gtcClause.parentSubtitle}
)} {gtcClause?.childrenCount > 0 && (
하위 조항: {gtcClause.childrenCount}개
)}
{/* 스크롤 가능한 폼 내용 영역 */}
{/* 채번 */} ( 채번 * 조항의 번호입니다. 영문, 숫자, 점(.), 하이픈(-), 언더스코어(_)를 사용할 수 있습니다. )} /> {/* 분류 */} ( 분류 )} /> {/* 소제목 */} ( 소제목 * )} /> {/* 상세항목 */} ( 상세항목 (선택사항) 조항의 실제 내용입니다. 텍스트와 이미지를 조합할 수 있으며, 하위 조항들을 그룹핑하는 제목용 조항인 경우 비워둘 수 있습니다. )} /> {/* PDFTron 변수명 섹션 */} {/*
PDFTron 변수명 설정 {gtcClause?.hasAllVariableNames && ( 설정됨 )}
( 채번 변수명 )} /> ( 소제목 변수명 )} /> ( 상세항목 변수명 )} />
*/} {/* 편집 사유 */} ( 편집 사유 (권장)