diff options
Diffstat (limited to 'lib/legal-review/status/legal-work-detail-dialog.tsx')
| -rw-r--r-- | lib/legal-review/status/legal-work-detail-dialog.tsx | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/lib/legal-review/status/legal-work-detail-dialog.tsx b/lib/legal-review/status/legal-work-detail-dialog.tsx new file mode 100644 index 00000000..23ceccb2 --- /dev/null +++ b/lib/legal-review/status/legal-work-detail-dialog.tsx @@ -0,0 +1,409 @@ +"use client"; + +import * as React from "react"; +import { + Eye, + FileText, + Building, + User, + Calendar, + Clock, + MessageSquare, + CheckCircle, + ShieldCheck, +} from "lucide-react"; + +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Badge } from "@/components/ui/badge"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Separator } from "@/components/ui/separator"; +import { formatDate } from "@/lib/utils"; +import { LegalWorksDetailView } from "@/db/schema"; + +// ----------------------------------------------------------------------------- +// TYPES +// ----------------------------------------------------------------------------- + +type LegalWorkData = LegalWorksDetailView; + +interface LegalWorkDetailDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + work: LegalWorkData | null; +} + +// ----------------------------------------------------------------------------- +// HELPERS +// ----------------------------------------------------------------------------- + +// 상태별 배지 스타일 +const getStatusBadgeVariant = (status: string) => { + switch (status) { + case "검토요청": + return "bg-blue-100 text-blue-800 border-blue-200"; + case "담당자배정": + return "bg-yellow-100 text-yellow-800 border-yellow-200"; + case "검토중": + return "bg-orange-100 text-orange-800 border-orange-200"; + case "답변완료": + return "bg-green-100 text-green-800 border-green-200"; + case "재검토요청": + return "bg-purple-100 text-purple-800 border-purple-200"; + case "보류": + return "bg-gray-100 text-gray-800 border-gray-200"; + case "취소": + return "bg-red-100 text-red-800 border-red-200"; + default: + return "bg-gray-100 text-gray-800 border-gray-200"; + } +}; + +export function LegalWorkDetailDialog({ + open, + onOpenChange, + work, +}: LegalWorkDetailDialogProps) { + if (!work) return null; + + // --------------------------------------------------------------------------- + // CONDITIONAL FLAGS + // --------------------------------------------------------------------------- + + const isLegalReview = work.reviewDepartment === "법무검토"; + const isCompliance = work.reviewDepartment === "준법문의"; + + const isDomesticContract = work.inquiryType === "국내계약"; + const isDomesticAdvisory = work.inquiryType === "국내자문"; + const isOverseasContract = work.inquiryType === "해외계약"; + const isOverseasAdvisory = work.inquiryType === "해외자문"; + + const isContractTypeActive = + isDomesticContract || isOverseasContract || isOverseasAdvisory; + const isDomesticContractFieldsActive = isDomesticContract; + const isFactualRelationActive = isDomesticAdvisory || isOverseasAdvisory; + const isOverseasFieldsActive = isOverseasContract || isOverseasAdvisory; + + // --------------------------------------------------------------------------- + // RENDER + // --------------------------------------------------------------------------- + + return ( + <Dialog open={open} onOpenChange={onOpenChange}> + <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"> + <Eye className="h-5 w-5" /> 법무업무 상세보기 + </DialogTitle> + <DialogDescription> + 법무업무 #{work.id}의 상세 정보를 확인합니다. + </DialogDescription> + </DialogHeader> + </div> + + {/* 본문 */} + <ScrollArea className="flex-1 p-6"> + <div className="space-y-6"> + {/* 1. 기본 정보 */} + <Card> + <CardHeader> + <CardTitle className="flex items-center gap-2 text-lg"> + <FileText className="h-5 w-5" /> 기본 정보 + </CardTitle> + </CardHeader> + <CardContent> + <div className="grid grid-cols-2 gap-6 text-sm"> + <div className="space-y-4"> + <div className="flex items-center gap-2"> + <span className="font-medium text-muted-foreground">업무 ID:</span> + <Badge variant="outline">#{work.id}</Badge> + </div> + <div className="flex items-center gap-2"> + <span className="font-medium text-muted-foreground">구분:</span> + <Badge + variant={ + work.category === "CP" + ? "default" + : work.category === "GTC" + ? "secondary" + : "outline" + } + > + {work.category} + </Badge> + {work.isUrgent && ( + <Badge variant="destructive" className="text-xs"> + 긴급 + </Badge> + )} + </div> + <div className="flex items-center gap-2"> + <span className="font-medium text-muted-foreground">상태:</span> + <Badge + className={getStatusBadgeVariant(work.status)} + variant="outline" + > + {work.status} + </Badge> + </div> + </div> + <div className="space-y-4"> + <div className="flex items-center gap-2"> + <Building className="h-4 w-4 text-muted-foreground" /> + <span className="font-medium text-muted-foreground">벤더:</span> + <span> + {work.vendorCode} - {work.vendorName} + </span> + </div> + <div className="flex items-center gap-2"> + <Calendar className="h-4 w-4 text-muted-foreground" /> + <span className="font-medium text-muted-foreground">의뢰일:</span> + <span>{formatDate(work.consultationDate, "KR")}</span> + </div> + <div className="flex items-center gap-2"> + <Clock className="h-4 w-4 text-muted-foreground" /> + <span className="font-medium text-muted-foreground">답변요청일:</span> + <span>{formatDate(work.requestDate, "KR")}</span> + </div> + </div> + </div> + </CardContent> + </Card> + + {/* 2. 담당자 정보 */} + <Card> + <CardHeader> + <CardTitle className="flex items-center gap-2 text-lg"> + <User className="h-5 w-5" /> 담당자 정보 + </CardTitle> + </CardHeader> + <CardContent> + <div className="grid grid-cols-2 gap-6 text-sm"> + <div className="space-y-2"> + <span className="font-medium text-muted-foreground">검토요청자</span> + <p>{work.reviewer || "미지정"}</p> + </div> + <div className="space-y-2"> + <span className="font-medium text-muted-foreground">법무답변자</span> + <p>{work.legalResponder || "미배정"}</p> + </div> + {work.expectedAnswerDate && ( + <div className="space-y-2"> + <span className="font-medium text-muted-foreground">답변예정일</span> + <p>{formatDate(work.expectedAnswerDate, "KR")}</p> + </div> + )} + {work.legalCompletionDate && ( + <div className="space-y-2"> + <span className="font-medium text-muted-foreground">법무완료일</span> + <p>{formatDate(work.legalCompletionDate, "KR")}</p> + </div> + )} + </div> + </CardContent> + </Card> + + {/* 3. 법무업무 상세 정보 */} + <Card> + <CardHeader> + <CardTitle className="flex items-center gap-2 text-lg"> + <ShieldCheck className="h-5 w-5" /> 법무업무 상세 정보 + </CardTitle> + </CardHeader> + <CardContent className="space-y-4 text-sm"> + <div className="grid grid-cols-2 gap-6"> + <div className="space-y-2"> + <span className="font-medium text-muted-foreground">검토부문</span> + <Badge variant="outline">{work.reviewDepartment}</Badge> + </div> + {work.inquiryType && ( + <div className="space-y-2"> + <span className="font-medium text-muted-foreground">문의종류</span> + <Badge variant="secondary">{work.inquiryType}</Badge> + </div> + )} + {isCompliance && ( + <div className="space-y-2 col-span-2"> + <span className="font-medium text-muted-foreground">공개여부</span> + <Badge variant={work.isPublic ? "default" : "outline"}> + {work.isPublic ? "공개" : "비공개"} + </Badge> + </div> + )} + </div> + + {/* 법무검토 전용 필드 */} + {isLegalReview && ( + <> + {work.contractProjectName && ( + <> + <Separator className="my-2" /> + <div className="space-y-2"> + <span className="font-medium text-muted-foreground"> + 계약명 / 프로젝트명 + </span> + <p>{work.contractProjectName}</p> + </div> + </> + )} + + {/* 계약서 종류 */} + {isContractTypeActive && work.contractType && ( + <div className="space-y-2"> + <span className="font-medium text-muted-foreground">계약서 종류</span> + <Badge variant="outline" className="max-w-max"> + {work.contractType} + </Badge> + </div> + )} + + {/* 국내계약 전용 필드 */} + {isDomesticContractFieldsActive && ( + <div className="grid grid-cols-2 gap-6 mt-4"> + {work.contractCounterparty && ( + <div className="space-y-1"> + <span className="font-medium text-muted-foreground"> + 계약상대방 + </span> + <p>{work.contractCounterparty}</p> + </div> + )} + {work.counterpartyType && ( + <div className="space-y-1"> + <span className="font-medium text-muted-foreground"> + 계약상대방 구분 + </span> + <p>{work.counterpartyType}</p> + </div> + )} + {work.contractPeriod && ( + <div className="space-y-1"> + <span className="font-medium text-muted-foreground">계약기간</span> + <p>{work.contractPeriod}</p> + </div> + )} + {work.contractAmount && ( + <div className="space-y-1"> + <span className="font-medium text-muted-foreground">계약금액</span> + <p>{work.contractAmount}</p> + </div> + )} + </div> + )} + + {/* 사실관계 */} + {isFactualRelationActive && work.factualRelation && ( + <div className="space-y-2 mt-4"> + <span className="font-medium text-muted-foreground">사실관계</span> + <p className="whitespace-pre-wrap">{work.factualRelation}</p> + </div> + )} + + {/* 해외 전용 필드 */} + {isOverseasFieldsActive && ( + <div className="grid grid-cols-2 gap-6 mt-4"> + {work.projectNumber && ( + <div className="space-y-1"> + <span className="font-medium text-muted-foreground">프로젝트번호</span> + <p>{work.projectNumber}</p> + </div> + )} + {work.shipownerOrderer && ( + <div className="space-y-1"> + <span className="font-medium text-muted-foreground">선주 / 발주처</span> + <p>{work.shipownerOrderer}</p> + </div> + )} + {work.projectType && ( + <div className="space-y-1"> + <span className="font-medium text-muted-foreground">프로젝트종류</span> + <p>{work.projectType}</p> + </div> + )} + {work.governingLaw && ( + <div className="space-y-1"> + <span className="font-medium text-muted-foreground">준거법</span> + <p>{work.governingLaw}</p> + </div> + )} + </div> + )} + </> + )} + </CardContent> + </Card> + + {/* 4. 요청 내용 */} + <Card> + <CardHeader> + <CardTitle className="flex items-center gap-2 text-lg"> + <MessageSquare className="h-5 w-5" /> 요청 내용 + </CardTitle> + </CardHeader> + <CardContent className="space-y-4 text-sm"> + {work.title && ( + <div className="space-y-1"> + <span className="font-medium text-muted-foreground">제목</span> + <p className="font-medium">{work.title}</p> + </div> + )} + <Separator /> + <div className="space-y-1"> + <span className="font-medium text-muted-foreground">상세 내용</span> + <div className="bg-muted/30 rounded-lg p-4"> + {work.requestContent ? ( + <div className="prose prose-sm max-w-none"> + <div + dangerouslySetInnerHTML={{ __html: work.requestContent }} + /> + </div> + ) : ( + <p className="italic text-muted-foreground">요청 내용이 없습니다.</p> + )} + </div> + </div> + {work.attachmentCount > 0 && ( + <div className="flex items-center gap-2"> + <FileText className="h-4 w-4" /> 첨부파일 {work.attachmentCount}개 + </div> + )} + </CardContent> + </Card> + + {/* 5. 답변 내용 */} + <Card> + <CardHeader> + <CardTitle className="flex items-center gap-2 text-lg"> + <CheckCircle className="h-5 w-5" /> 답변 내용 + </CardTitle> + </CardHeader> + <CardContent className="space-y-4 text-sm"> + <div className="bg-green-50 border border-green-200 rounded-lg p-4"> + {work.responseContent ? ( + <div className="prose prose-sm max-w-none"> + <div + dangerouslySetInnerHTML={{ __html: work.responseContent }} + /> + </div> + ) : ( + <p className="italic text-muted-foreground"> + 아직 답변이 등록되지 않았습니다. + </p> + )} + </div> + </CardContent> + </Card> + </div> + </ScrollArea> + </DialogContent> + </Dialog> + ); +} |
