summaryrefslogtreecommitdiff
path: root/lib/legal-review/status/request-review-dialog.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-08-06 04:23:40 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-08-06 04:23:40 +0000
commitde2ac5a2860bc25180971e7a11f852d9d44675b7 (patch)
treeb931c363f2cb19e177a0a7b17190d5de2a82d709 /lib/legal-review/status/request-review-dialog.tsx
parent6c549b0f264e9be4d60af38f9efc05b189d6849f (diff)
(대표님) 정기평가, 법적검토, 정책, 가입관련 처리 및 관련 컴포넌트 추가, 메뉴 변경
Diffstat (limited to 'lib/legal-review/status/request-review-dialog.tsx')
-rw-r--r--lib/legal-review/status/request-review-dialog.tsx976
1 files changed, 976 insertions, 0 deletions
diff --git a/lib/legal-review/status/request-review-dialog.tsx b/lib/legal-review/status/request-review-dialog.tsx
new file mode 100644
index 00000000..838752c4
--- /dev/null
+++ b/lib/legal-review/status/request-review-dialog.tsx
@@ -0,0 +1,976 @@
+"use client"
+
+import * as React from "react"
+import { useForm } from "react-hook-form"
+import { zodResolver } from "@hookform/resolvers/zod"
+import * as z from "zod"
+import { Loader2, Send, FileText, Clock, Upload, X, Building, User, Calendar } from "lucide-react"
+import { toast } from "sonner"
+
+import { Button } from "@/components/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+import { Input } from "@/components/ui/input"
+import { Textarea } from "@/components/ui/textarea"
+import { Badge } from "@/components/ui/badge"
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+import { Switch } from "@/components/ui/switch"
+import TiptapEditor from "@/components/qna/tiptap-editor"
+import { canRequestReview, requestReview } from "../service"
+import { LegalWorksDetailView } from "@/db/schema"
+
+type LegalWorkData = LegalWorksDetailView
+
+interface RequestReviewDialogProps {
+ open: boolean
+ onOpenChange: (open: boolean) => void
+ work: LegalWorkData | null
+ onSuccess?: () => void
+}
+
+// 검토요청 폼 스키마
+const requestReviewSchema = z.object({
+ // 기본 검토 설정
+ dueDate: z.string().min(1, "검토 완료 희망일을 선택해주세요"),
+ assignee: z.string().optional(),
+ notificationMethod: z.enum(["email", "internal", "both"]).default("both"),
+
+ // 법무업무 상세 정보
+ reviewDepartment: z.enum(["준법문의", "법무검토"]),
+ inquiryType: z.enum(["국내계약", "국내자문", "해외계약", "해외자문"]).optional(),
+
+ // 공통 필드
+ title: z.string().min(1, "제목을 선택해주세요"),
+ requestContent: z.string().min(1, "요청내용을 입력해주세요"),
+
+ // 준법문의 전용 필드
+ isPublic: z.boolean().default(false),
+
+ // 법무검토 전용 필드들
+ contractProjectName: z.string().optional(),
+ contractType: z.string().optional(),
+ contractCounterparty: z.string().optional(),
+ counterpartyType: z.enum(["법인", "개인"]).optional(),
+ contractPeriod: z.string().optional(),
+ contractAmount: z.string().optional(),
+ factualRelation: z.string().optional(),
+ projectNumber: z.string().optional(),
+ shipownerOrderer: z.string().optional(),
+ projectType: z.string().optional(),
+ governingLaw: z.string().optional(),
+}).refine((data) => {
+ // 법무검토 선택시 문의종류 필수
+ if (data.reviewDepartment === "법무검토" && !data.inquiryType) {
+ return false;
+ }
+ return true;
+}, {
+ message: "법무검토 선택시 문의종류를 선택해주세요",
+ path: ["inquiryType"]
+});
+
+type RequestReviewFormValues = z.infer<typeof requestReviewSchema>
+
+export function RequestReviewDialog({
+ open,
+ onOpenChange,
+ work,
+ onSuccess
+}: RequestReviewDialogProps) {
+ const [isSubmitting, setIsSubmitting] = React.useState(false)
+ const [attachments, setAttachments] = React.useState<File[]>([])
+ const [editorContent, setEditorContent] = React.useState("")
+ const [canRequest, setCanRequest] = React.useState(true)
+ const [requestCheckMessage, setRequestCheckMessage] = React.useState("")
+
+ // work의 category에 따라 기본 reviewDepartment 결정
+ const getDefaultReviewDepartment = () => {
+ return work?.category === "CP" ? "준법문의" : "법무검토"
+ }
+
+ const form = useForm<RequestReviewFormValues>({
+ resolver: zodResolver(requestReviewSchema),
+ defaultValues: {
+ dueDate: "",
+ assignee: "",
+ notificationMethod: "both",
+ reviewDepartment: getDefaultReviewDepartment(),
+ title: getDefaultReviewDepartment() === "준법문의" ? "CP검토" : "GTC검토",
+ requestContent: "",
+ isPublic: false,
+ },
+ })
+
+ // work 변경시 검토요청 가능 여부 확인
+ React.useEffect(() => {
+ if (work && open) {
+ canRequestReview(work.id).then((result) => {
+ setCanRequest(result.canRequest)
+ setRequestCheckMessage(result.reason || "")
+ })
+
+ const defaultDepartment = work.category === "CP" ? "준법문의" : "법무검토"
+ form.setValue("reviewDepartment", defaultDepartment)
+ }
+ }, [work, open, form])
+
+ // 검토부문 감시
+ const reviewDepartment = form.watch("reviewDepartment")
+ const inquiryType = form.watch("inquiryType")
+ const titleValue = form.watch("title")
+
+ // 조건부 필드 활성화 로직
+ const isContractTypeActive = inquiryType && ["국내계약", "해외계약", "해외자문"].includes(inquiryType)
+ const isDomesticContractFieldsActive = inquiryType === "국내계약"
+ const isFactualRelationActive = inquiryType && ["국내자문", "해외자문"].includes(inquiryType)
+ const isOverseasFieldsActive = inquiryType && ["해외계약", "해외자문"].includes(inquiryType)
+
+ // 제목 "기타" 선택 여부 확인
+ const isTitleOther = titleValue === "기타"
+
+ // 검토부문 변경시 관련 필드 초기화
+ React.useEffect(() => {
+ if (reviewDepartment === "준법문의") {
+ form.setValue("inquiryType", undefined)
+ // 제목 초기화 (기타 상태였거나 값이 없으면 기본값으로)
+ const currentTitle = form.getValues("title")
+ if (currentTitle === "기타" || !currentTitle || currentTitle === "GTC검토") {
+ form.setValue("title", "CP검토")
+ }
+ // 법무검토 전용 필드들 초기화
+ form.setValue("contractProjectName", "")
+ form.setValue("contractType", "")
+ form.setValue("contractCounterparty", "")
+ form.setValue("counterpartyType", undefined)
+ form.setValue("contractPeriod", "")
+ form.setValue("contractAmount", "")
+ form.setValue("factualRelation", "")
+ form.setValue("projectNumber", "")
+ form.setValue("shipownerOrderer", "")
+ form.setValue("projectType", "")
+ form.setValue("governingLaw", "")
+ } else {
+ // 제목 초기화 (기타 상태였거나 값이 없으면 기본값으로)
+ const currentTitle = form.getValues("title")
+ if (currentTitle === "기타" || !currentTitle || currentTitle === "CP검토") {
+ form.setValue("title", "GTC검토")
+ }
+ form.setValue("isPublic", false)
+ }
+ }, [reviewDepartment, form])
+
+ // 문의종류 변경시 관련 필드 초기화
+ React.useEffect(() => {
+ if (inquiryType) {
+ // 계약서 종류 초기화 (옵션이 달라지므로)
+ form.setValue("contractType", "")
+
+ // 조건에 맞지 않는 필드들 초기화
+ if (!isDomesticContractFieldsActive) {
+ form.setValue("contractCounterparty", "")
+ form.setValue("counterpartyType", undefined)
+ form.setValue("contractPeriod", "")
+ form.setValue("contractAmount", "")
+ }
+
+ if (!isFactualRelationActive) {
+ form.setValue("factualRelation", "")
+ }
+
+ if (!isOverseasFieldsActive) {
+ form.setValue("projectNumber", "")
+ form.setValue("shipownerOrderer", "")
+ form.setValue("projectType", "")
+ form.setValue("governingLaw", "")
+ }
+ }
+ }, [inquiryType, isDomesticContractFieldsActive, isFactualRelationActive, isOverseasFieldsActive, form])
+
+ // 에디터 내용이 변경될 때 폼에 반영
+ React.useEffect(() => {
+ form.setValue("requestContent", editorContent)
+ }, [editorContent, form])
+
+ // 첨부파일 처리
+ const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+ const files = Array.from(event.target.files || [])
+ setAttachments(prev => [...prev, ...files])
+ }
+
+ const removeAttachment = (index: number) => {
+ setAttachments(prev => prev.filter((_, i) => i !== index))
+ }
+
+ // 폼 제출
+ async function onSubmit(data: RequestReviewFormValues) {
+ if (!work) return
+
+ console.log("Request review data:", data)
+ console.log("Work to review:", work)
+ console.log("Attachments:", attachments)
+ setIsSubmitting(true)
+
+ try {
+ const result = await requestReview(work.id, data, attachments)
+
+ if (result.success) {
+ toast.success(result.data?.message || `법무업무 #${work.id}에 대한 검토요청이 완료되었습니다.`)
+ onOpenChange(false)
+ handleReset()
+ onSuccess?.()
+ } else {
+ toast.error(result.error || "검토요청 중 오류가 발생했습니다.")
+ }
+ } catch (error) {
+ console.error("Error requesting review:", error)
+ toast.error("검토요청 중 오류가 발생했습니다.")
+ } finally {
+ setIsSubmitting(false)
+ }
+ }
+
+ // 폼 리셋 함수
+ const handleReset = () => {
+ const defaultDepartment = getDefaultReviewDepartment()
+ form.reset({
+ dueDate: "",
+ assignee: "",
+ notificationMethod: "both",
+ reviewDepartment: defaultDepartment,
+ title: defaultDepartment === "준법문의" ? "CP검토" : "GTC검토",
+ requestContent: "",
+ isPublic: false,
+ })
+ setAttachments([])
+ setEditorContent("")
+ }
+
+ // 다이얼로그 닫기 핸들러
+ const handleOpenChange = (open: boolean) => {
+ onOpenChange(open)
+ if (!open) {
+ handleReset()
+ }
+ }
+
+ // 제목 옵션 (검토부문에 따라 다름)
+ const getTitleOptions = () => {
+ if (reviewDepartment === "준법문의") {
+ return [
+ { value: "CP검토", label: "CP검토" },
+ { value: "기타", label: "기타 (직접입력)" }
+ ]
+ } else {
+ return [
+ { value: "GTC검토", label: "GTC검토" },
+ { value: "기타", label: "기타 (직접입력)" }
+ ]
+ }
+ }
+
+ // 계약서 종류 옵션 (문의종류에 따라 다름)
+ const getContractTypeOptions = () => {
+ if (inquiryType === "국내계약") {
+ return [
+ { value: "공사도급계약", label: "공사도급계약" },
+ { value: "제작납품계약", label: "제작납품계약" },
+ { value: "자재매매계약", label: "자재매매계약" },
+ { value: "용역위탁계약", label: "용역위탁계약" },
+ { value: "기술사용 및 개발계약", label: "기술사용 및 개발계약" },
+ { value: "운송 및 자재관리 계약", label: "운송 및 자재관리 계약" },
+ { value: "자문 등 위임계약", label: "자문 등 위임계약" },
+ { value: "양해각서", label: "양해각서" },
+ { value: "양수도 계약", label: "양수도 계약" },
+ { value: "합의서", label: "합의서" },
+ { value: "공동도급(운영)협약서", label: "공동도급(운영)협약서" },
+ { value: "협정서", label: "협정서" },
+ { value: "약정서", label: "약정서" },
+ { value: "협의서", label: "협의서" },
+ { value: "기타", label: "기타" },
+ { value: "비밀유지계약서", label: "비밀유지계약서" },
+ { value: "분양계약서", label: "분양계약서" },
+ ]
+ } else {
+ // 해외계약/해외자문
+ return [
+ { value: "Shipbuilding Contract", label: "Shipbuilding Contract" },
+ { value: "Offshore Contract (EPCI, FEED)", label: "Offshore Contract (EPCI, FEED)" },
+ { value: "Supplementary / Addendum", label: "Supplementary / Addendum" },
+ { value: "Subcontract / GTC / PTC / PO", label: "Subcontract / GTC / PTC / PO" },
+ { value: "Novation / Assignment", label: "Novation / Assignment" },
+ { value: "NDA (Confidential, Secrecy)", label: "NDA (Confidential, Secrecy)" },
+ { value: "Warranty", label: "Warranty" },
+ { value: "Waiver and Release", label: "Waiver and Release" },
+ { value: "Bond (PG, RG, Advanced Payment)", label: "Bond (PG, RG, Advanced Payment)" },
+ { value: "MOU / LOI / LOA", label: "MOU / LOI / LOA" },
+ { value: "Power of Attorney (POA)", label: "Power of Attorney (POA)" },
+ { value: "Commission Agreement", label: "Commission Agreement" },
+ { value: "Consortium Agreement", label: "Consortium Agreement" },
+ { value: "JV / JDP Agreement", label: "JV / JDP Agreement" },
+ { value: "Engineering Service Contract", label: "Engineering Service Contract" },
+ { value: "Consultancy Service Agreement", label: "Consultancy Service Agreement" },
+ { value: "Purchase / Lease Agreement", label: "Purchase / Lease Agreement" },
+ { value: "Financial / Loan / Covenant", label: "Financial / Loan / Covenant" },
+ { value: "Other Contract / Agreement", label: "Other Contract / Agreement" },
+ ]
+ }
+ }
+
+ // 프로젝트 종류 옵션
+ const getProjectTypeOptions = () => {
+ return [
+ { value: "BARGE VESSEL", label: "BARGE VESSEL" },
+ { value: "BULK CARRIER", label: "BULK CARRIER" },
+ { value: "CHEMICAL CARRIER", label: "CHEMICAL CARRIER" },
+ { value: "FULL CONTAINER", label: "FULL CONTAINER" },
+ { value: "CRUDE OIL TANKER", label: "CRUDE OIL TANKER" },
+ { value: "CRUISE SHIP", label: "CRUISE SHIP" },
+ { value: "DRILL SHIP", label: "DRILL SHIP" },
+ { value: "FIELD DEVELOPMENT SHIP", label: "FIELD DEVELOPMENT SHIP" },
+ { value: "FLOATING PRODUCTION STORAGE OFFLOADING", label: "FLOATING PRODUCTION STORAGE OFFLOADING" },
+ { value: "CAR-FERRY & PASSENGER VESSEL", label: "CAR-FERRY & PASSENGER VESSEL" },
+ { value: "FLOATING STORAGE OFFLOADING", label: "FLOATING STORAGE OFFLOADING" },
+ { value: "HEAVY DECK CARGO", label: "HEAVY DECK CARGO" },
+ { value: "PRODUCT OIL TANKER", label: "PRODUCT OIL TANKER" },
+ { value: "HIGH SPEED LINER", label: "HIGH SPEED LINER" },
+ { value: "JACK-UP", label: "JACK-UP" },
+ { value: "LIQUEFIED NATURAL GAS CARRIER", label: "LIQUEFIED NATURAL GAS CARRIER" },
+ { value: "LIQUEFIED PETROLEUM GAS CARRIER", label: "LIQUEFIED PETROLEUM GAS CARRIER" },
+ { value: "MULTIPURPOSE CARGO CARRIER", label: "MULTIPURPOSE CARGO CARRIER" },
+ { value: "ORE-BULK-OIL CARRIER", label: "ORE-BULK-OIL CARRIER" },
+ { value: "OIL TANKER", label: "OIL TANKER" },
+ { value: "OTHER VESSEL", label: "OTHER VESSEL" },
+ { value: "PURE CAR CARRIER", label: "PURE CAR CARRIER" },
+ { value: "PRODUCT CARRIER", label: "PRODUCT CARRIER" },
+ { value: "PLATFORM", label: "PLATFORM" },
+ { value: "PUSHER", label: "PUSHER" },
+ { value: "REEFER TRANSPORT VESSEL", label: "REEFER TRANSPORT VESSEL" },
+ { value: "ROLL-ON ROLL-OFF VESSEL", label: "ROLL-ON ROLL-OFF VESSEL" },
+ { value: "SEMI RIG", label: "SEMI RIG" },
+ { value: "SUPPLY ANCHOR HANDLING VESSEL", label: "SUPPLY ANCHOR HANDLING VESSEL" },
+ { value: "SHUTTLE TANKER", label: "SHUTTLE TANKER" },
+ { value: "SUPPLY VESSEL", label: "SUPPLY VESSEL" },
+ { value: "TOPSIDE", label: "TOPSIDE" },
+ { value: "TUG SUPPLY VESSEL", label: "TUG SUPPLY VESSEL" },
+ { value: "VERY LARGE CRUDE OIL CARRIER", label: "VERY LARGE CRUDE OIL CARRIER" },
+ { value: "WELL INTERVENTION SHIP", label: "WELL INTERVENTION SHIP" },
+ { value: "WIND TURBINE INSTALLATION VESSEL", label: "WIND TURBINE INSTALLATION VESSEL" },
+ { value: "기타", label: "기타" },
+ ]
+ }
+
+ if (!work) {
+ return null
+ }
+
+ // 검토요청 불가능한 경우 안내 메시지
+ if (!canRequest) {
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="max-w-md">
+ <DialogHeader>
+ <DialogTitle className="flex items-center gap-2 text-amber-600">
+ <FileText className="h-5 w-5" />
+ 검토요청 불가
+ </DialogTitle>
+ <DialogDescription className="pt-4">
+ {requestCheckMessage}
+ </DialogDescription>
+ </DialogHeader>
+ <div className="flex justify-end pt-4">
+ <Button onClick={() => onOpenChange(false)}>확인</Button>
+ </div>
+ </DialogContent>
+ </Dialog>
+ )
+ }
+
+ return (
+ <Dialog open={open} onOpenChange={handleOpenChange}>
+ <DialogContent className="max-w-4xl h-[90vh] p-0 flex flex-col">
+ {/* 고정 헤더 */}
+ <div className="flex-shrink-0 p-6 border-b">
+ <DialogHeader>
+ <DialogTitle className="flex items-center gap-2">
+ <Send className="h-5 w-5" />
+ 검토요청 발송
+ </DialogTitle>
+ <DialogDescription>
+ 법무업무 #{work.id}에 대한 상세한 검토를 요청합니다.
+ </DialogDescription>
+ </DialogHeader>
+ </div>
+
+ <Form {...form}>
+ <form
+ onSubmit={form.handleSubmit(onSubmit)}
+ className="flex flex-col flex-1 min-h-0"
+ >
+ {/* 스크롤 가능한 콘텐츠 영역 */}
+ <div className="flex-1 overflow-y-auto p-6">
+ <div className="space-y-6">
+ {/* 선택된 업무 정보 */}
+ <Card className="bg-blue-50 border-blue-200">
+ <CardHeader>
+ <CardTitle className="text-lg flex items-center gap-2">
+ <FileText className="h-5 w-5" />
+ 검토 대상 업무
+ </CardTitle>
+ </CardHeader>
+ <CardContent>
+ <div className="grid grid-cols-2 gap-4 text-sm">
+ <div className="space-y-2">
+ <div className="flex items-center gap-2">
+ <span className="font-medium">업무 ID:</span>
+ <Badge variant="outline">#{work.id}</Badge>
+ </div>
+ <div className="flex items-center gap-2">
+ <span className="font-medium">구분:</span>
+ <Badge variant={work.category === "CP" ? "default" : "secondary"}>
+ {work.category}
+ </Badge>
+ {work.isUrgent && (
+ <Badge variant="destructive" className="text-xs">
+ 긴급
+ </Badge>
+ )}
+ </div>
+ <div className="flex items-center gap-2">
+ <Building className="h-4 w-4" />
+ <span className="font-medium">벤더:</span>
+ <span>{work.vendorCode} - {work.vendorName}</span>
+ </div>
+ </div>
+ <div className="space-y-2">
+ <div className="flex items-center gap-2">
+ <User className="h-4 w-4" />
+ <span className="font-medium">요청자:</span>
+ <span>{work.reviewer || "미지정"}</span>
+ </div>
+ <div className="flex items-center gap-2">
+ <Calendar className="h-4 w-4" />
+ <span className="font-medium">답변요청일:</span>
+ <span>{work.requestDate || "미설정"}</span>
+ </div>
+ <div className="flex items-center gap-2">
+ <span className="font-medium">상태:</span>
+ <Badge variant="outline">{work.status}</Badge>
+ </div>
+ </div>
+ </div>
+ </CardContent>
+ </Card>
+
+ {/* 기본 설정 */}
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-lg">기본 설정</CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-4">
+ {/* 검토 완료 희망일 */}
+ <FormField
+ control={form.control}
+ name="dueDate"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel className="flex items-center gap-2">
+ <Clock className="h-4 w-4" />
+ 검토 완료 희망일
+ </FormLabel>
+ <FormControl>
+ <Input type="date" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </CardContent>
+ </Card>
+
+ {/* 법무업무 상세 정보 */}
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-lg">법무업무 상세 정보</CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-4">
+ {/* 검토부문 */}
+ <FormField
+ control={form.control}
+ name="reviewDepartment"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>검토부문</FormLabel>
+ <Select onValueChange={field.onChange} value={field.value}>
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="검토부문 선택" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ <SelectItem value="준법문의">준법문의</SelectItem>
+ <SelectItem value="법무검토">법무검토</SelectItem>
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 문의종류 (법무검토 선택시만) */}
+ {reviewDepartment === "법무검토" && (
+ <FormField
+ control={form.control}
+ name="inquiryType"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>문의종류</FormLabel>
+ <Select onValueChange={field.onChange} value={field.value}>
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="문의종류 선택" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ <SelectItem value="국내계약">국내계약</SelectItem>
+ <SelectItem value="국내자문">국내자문</SelectItem>
+ <SelectItem value="해외계약">해외계약</SelectItem>
+ <SelectItem value="해외자문">해외자문</SelectItem>
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ )}
+
+ {/* 제목 - 조건부 렌더링 */}
+ <FormField
+ control={form.control}
+ name="title"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>제목</FormLabel>
+ {!isTitleOther ? (
+ // Select 모드
+ <Select
+ onValueChange={(value) => {
+ field.onChange(value)
+ if (value !== "기타") {
+ // 기타가 아닌 값으로 변경시 해당 값으로 설정
+ form.setValue("title", value)
+ }
+ }}
+ value={field.value}
+ >
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="제목 선택" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ {getTitleOptions().map((option) => (
+ <SelectItem key={option.value} value={option.value}>
+ {option.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ ) : (
+ // Input 모드 (기타 선택시)
+ <div className="space-y-2">
+ <div className="flex items-center gap-2">
+ <Badge variant="outline" className="text-xs">기타</Badge>
+ <Button
+ type="button"
+ variant="ghost"
+ size="sm"
+ onClick={() => {
+ const defaultTitle = reviewDepartment === "준법문의" ? "CP검토" : "GTC검토"
+ form.setValue("title", defaultTitle)
+ }}
+ className="h-6 text-xs"
+ >
+ 선택 모드로 돌아가기
+ </Button>
+ </div>
+ <FormControl>
+ <Input
+ placeholder="제목을 직접 입력하세요"
+ value={field.value === "기타" ? "" : field.value}
+ onChange={(e) => field.onChange(e.target.value)}
+ autoFocus
+ />
+ </FormControl>
+ </div>
+ )}
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 준법문의 전용 필드들 */}
+ {reviewDepartment === "준법문의" && (
+ <FormField
+ control={form.control}
+ name="isPublic"
+ render={({ field }) => (
+ <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
+ <div className="space-y-0.5">
+ <FormLabel className="text-base">공개여부</FormLabel>
+ <div className="text-sm text-muted-foreground">
+ 준법문의 공개 설정
+ </div>
+ </div>
+ <FormControl>
+ <Switch
+ checked={field.value}
+ onCheckedChange={field.onChange}
+ />
+ </FormControl>
+ </FormItem>
+ )}
+ />
+ )}
+
+ {/* 법무검토 전용 필드들 */}
+ {reviewDepartment === "법무검토" && (
+ <div className="space-y-4">
+ {/* 계약명/프로젝트명 */}
+ <FormField
+ control={form.control}
+ name="contractProjectName"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>계약명/프로젝트명</FormLabel>
+ <FormControl>
+ <Input placeholder="계약명 또는 프로젝트명 입력" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 계약서 종류 - 조건부 활성화 */}
+ {isContractTypeActive && (
+ <FormField
+ control={form.control}
+ name="contractType"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>계약서 종류</FormLabel>
+ <Select onValueChange={field.onChange} value={field.value}>
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="계약서 종류 선택" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ {getContractTypeOptions().map((option) => (
+ <SelectItem key={option.value} value={option.value}>
+ {option.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ )}
+
+ {/* 국내계약 전용 필드들 */}
+ {isDomesticContractFieldsActive && (
+ <div className="grid grid-cols-2 gap-4">
+ {/* 계약상대방 */}
+ <FormField
+ control={form.control}
+ name="contractCounterparty"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>계약상대방</FormLabel>
+ <FormControl>
+ <Input placeholder="계약상대방 입력" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 계약상대방 구분 */}
+ <FormField
+ control={form.control}
+ name="counterpartyType"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>계약상대방 구분</FormLabel>
+ <Select onValueChange={field.onChange} value={field.value}>
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="구분 선택" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ <SelectItem value="법인">법인</SelectItem>
+ <SelectItem value="개인">개인</SelectItem>
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 계약기간 */}
+ <FormField
+ control={form.control}
+ name="contractPeriod"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>계약기간</FormLabel>
+ <FormControl>
+ <Input placeholder="계약기간 입력" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 계약금액 */}
+ <FormField
+ control={form.control}
+ name="contractAmount"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>계약금액</FormLabel>
+ <FormControl>
+ <Input placeholder="계약금액 입력" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+ )}
+
+ {/* 사실관계 - 조건부 활성화 */}
+ {isFactualRelationActive && (
+ <FormField
+ control={form.control}
+ name="factualRelation"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>사실관계</FormLabel>
+ <FormControl>
+ <Textarea
+ placeholder="사실관계를 상세히 입력해주세요"
+ className="min-h-[80px]"
+ {...field}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ )}
+
+ {/* 해외 관련 필드들 - 조건부 활성화 */}
+ {isOverseasFieldsActive && (
+ <div className="grid grid-cols-2 gap-4">
+ {/* 프로젝트번호 */}
+ <FormField
+ control={form.control}
+ name="projectNumber"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>프로젝트번호</FormLabel>
+ <FormControl>
+ <Input placeholder="프로젝트번호 입력" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 선주/발주처 */}
+ <FormField
+ control={form.control}
+ name="shipownerOrderer"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>선주/발주처</FormLabel>
+ <FormControl>
+ <Input placeholder="선주/발주처 입력" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 프로젝트종류 */}
+ <FormField
+ control={form.control}
+ name="projectType"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>프로젝트종류</FormLabel>
+ <Select onValueChange={field.onChange} value={field.value}>
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="프로젝트종류 선택" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ {getProjectTypeOptions().map((option) => (
+ <SelectItem key={option.value} value={option.value}>
+ {option.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 준거법 */}
+ <FormField
+ control={form.control}
+ name="governingLaw"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>준거법</FormLabel>
+ <FormControl>
+ <Input placeholder="준거법 입력" {...field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+ )}
+ </div>
+ )}
+
+ {/* 요청내용 - TiptapEditor로 교체 */}
+ <FormField
+ control={form.control}
+ name="requestContent"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>요청내용</FormLabel>
+ <FormControl>
+ <div className="min-h-[250px]">
+ <TiptapEditor
+ content={editorContent}
+ setContent={setEditorContent}
+ disabled={isSubmitting}
+ height="250px"
+ />
+ </div>
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 첨부파일 */}
+ <div className="space-y-2">
+ <FormLabel>첨부파일</FormLabel>
+ <div className="border-2 border-dashed border-muted-foreground/25 rounded-lg p-4">
+ <input
+ type="file"
+ multiple
+ onChange={handleFileChange}
+ className="hidden"
+ id="file-upload"
+ />
+ <label
+ htmlFor="file-upload"
+ className="flex flex-col items-center justify-center cursor-pointer"
+ >
+ <Upload className="h-8 w-8 text-muted-foreground mb-2" />
+ <span className="text-sm text-muted-foreground">
+ 파일을 선택하거나 여기로 드래그하세요
+ </span>
+ </label>
+ </div>
+
+ {/* 선택된 파일 목록 */}
+ {attachments.length > 0 && (
+ <div className="space-y-2">
+ {attachments.map((file, index) => (
+ <div key={index} className="flex items-center justify-between bg-muted/50 p-2 rounded">
+ <span className="text-sm truncate">{file.name}</span>
+ <Button
+ type="button"
+ variant="ghost"
+ size="sm"
+ onClick={() => removeAttachment(index)}
+ >
+ <X className="h-4 w-4" />
+ </Button>
+ </div>
+ ))}
+ </div>
+ )}
+ </div>
+ </CardContent>
+ </Card>
+ </div>
+ </div>
+
+ {/* 고정 버튼 영역 */}
+ <div className="flex-shrink-0 border-t p-6">
+ <div className="flex justify-end gap-3">
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => handleOpenChange(false)}
+ disabled={isSubmitting}
+ >
+ 취소
+ </Button>
+ <Button
+ type="submit"
+ disabled={isSubmitting}
+ className="bg-blue-600 hover:bg-blue-700"
+ >
+ {isSubmitting ? (
+ <>
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
+ 발송 중...
+ </>
+ ) : (
+ <>
+ <Send className="mr-2 h-4 w-4" />
+ 검토요청 발송
+ </>
+ )}
+ </Button>
+ </div>
+ </div>
+ </form>
+ </Form>
+ </DialogContent>
+ </Dialog>
+ )
+} \ No newline at end of file