From 15c3ae6536c264db0508e4fc4aaa59c3e6d1af30 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Tue, 15 Jul 2025 00:50:39 +0000 Subject: (대표님) 기본계약 및 정기평가 작업사항, OCR 변경사항 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../add-basic-contract-template-dialog.tsx | 387 ++++++++++++++------- 1 file changed, 268 insertions(+), 119 deletions(-) (limited to 'lib/basic-contract/template/add-basic-contract-template-dialog.tsx') diff --git a/lib/basic-contract/template/add-basic-contract-template-dialog.tsx b/lib/basic-contract/template/add-basic-contract-template-dialog.tsx index cf0986f0..3a83d50f 100644 --- a/lib/basic-contract/template/add-basic-contract-template-dialog.tsx +++ b/lib/basic-contract/template/add-basic-contract-template-dialog.tsx @@ -18,6 +18,8 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Switch } from "@/components/ui/switch"; import { Select, SelectContent, @@ -34,17 +36,31 @@ import { DropzoneInput } from "@/components/ui/dropzone"; import { Progress } from "@/components/ui/progress"; -import { useRouter } from "next/navigation" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Separator } from "@/components/ui/separator"; +import { useRouter } from "next/navigation"; +import { BUSINESS_UNITS } from "@/config/basicContractColumnsConfig"; -// 유효기간 필드가 추가된 계약서 템플릿 스키마 정의 +// 업데이트된 계약서 템플릿 스키마 정의 const templateFormSchema = z.object({ + templateCode: z.string() + .min(1, "템플릿 코드는 필수입니다.") + .max(50, "템플릿 코드는 50자 이하여야 합니다.") + .regex(/^[A-Z0-9_-]+$/, "템플릿 코드는 영문 대문자, 숫자, '_', '-'만 사용 가능합니다."), templateName: z.string().min(1, "템플릿 이름은 필수입니다."), - validityPeriod: z.coerce - .number({ invalid_type_error: "유효기간은 숫자여야 합니다." }) - .int("유효기간은 정수여야 합니다.") - .min(1, "유효기간은 최소 1개월 이상이어야 합니다.") - .max(120, "유효기간은 최대 120개월(10년)을 초과할 수 없습니다.") - .default(12), + revision: z.coerce.number().int().min(1).default(1), + legalReviewRequired: z.boolean().default(false), + + // 적용 범위 + shipBuildingApplicable: z.boolean().default(false), + windApplicable: z.boolean().default(false), + pcApplicable: z.boolean().default(false), + nbApplicable: z.boolean().default(false), + rcApplicable: z.boolean().default(false), + gyApplicable: z.boolean().default(false), + sysApplicable: z.boolean().default(false), + infraApplicable: z.boolean().default(false), + file: z .instanceof(File, { message: "파일을 업로드해주세요." }) .refine((file) => file.size <= 100 * 1024 * 1024, { @@ -55,6 +71,15 @@ const templateFormSchema = z.object({ { message: "PDF 파일만 업로드 가능합니다." } ), status: z.enum(["ACTIVE", "DISPOSED"]).default("ACTIVE"), +}).refine((data) => { + // 적어도 하나의 적용 범위는 선택되어야 함 + const hasAnyScope = BUSINESS_UNITS.some(unit => + data[unit.key as keyof typeof data] as boolean + ); + return hasAnyScope; +}, { + message: "적어도 하나의 적용 범위를 선택해야 합니다.", + path: ["shipBuildingApplicable"], // 에러를 첫 번째 체크박스에 표시 }); type TemplateFormValues = z.infer; @@ -65,12 +90,22 @@ export function AddTemplateDialog() { const [selectedFile, setSelectedFile] = React.useState(null); const [uploadProgress, setUploadProgress] = React.useState(0); const [showProgress, setShowProgress] = React.useState(false); - const router = useRouter() + const router = useRouter(); // 기본값 설정 const defaultValues: Partial = { + templateCode: "", templateName: "", - validityPeriod: 12, // 기본값 1년 + revision: 1, + legalReviewRequired: false, + shipBuildingApplicable: false, + windApplicable: false, + pcApplicable: false, + nbApplicable: false, + rcApplicable: false, + gyApplicable: false, + sysApplicable: false, + infraApplicable: false, status: "ACTIVE", }; @@ -81,11 +116,6 @@ export function AddTemplateDialog() { mode: "onChange", }); - // 폼 값 감시 - const templateName = form.watch("templateName"); - const validityPeriod = form.watch("validityPeriod"); - const file = form.watch("file"); - // 파일 선택 핸들러 const handleFileChange = (files: File[]) => { if (files.length > 0) { @@ -95,6 +125,13 @@ export function AddTemplateDialog() { } }; + // 모든 적용 범위 선택/해제 + const handleSelectAllScopes = (checked: boolean) => { + BUSINESS_UNITS.forEach(unit => { + form.setValue(unit.key as keyof TemplateFormValues, checked); + }); + }; + // 청크 크기 설정 (1MB) const CHUNK_SIZE = 1 * 1024 * 1024; @@ -161,15 +198,25 @@ export function AddTemplateDialog() { throw new Error("파일 업로드에 실패했습니다."); } - // 메타데이터 저장 + // 메타데이터 저장 (업데이트된 필드들 포함) const saveResponse = await fetch('/api/upload/basicContract/complete', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ + templateCode: formData.templateCode, templateName: formData.templateName, - validityPeriod: formData.validityPeriod, // 유효기간 추가 + revision: formData.revision, + legalReviewRequired: formData.legalReviewRequired, + shipBuildingApplicable: formData.shipBuildingApplicable, + windApplicable: formData.windApplicable, + pcApplicable: formData.pcApplicable, + nbApplicable: formData.nbApplicable, + rcApplicable: formData.rcApplicable, + gyApplicable: formData.gyApplicable, + sysApplicable: formData.sysApplicable, + infraApplicable: formData.infraApplicable, status: formData.status, fileName: uploadResult.fileName, filePath: uploadResult.filePath, @@ -215,15 +262,10 @@ export function AddTemplateDialog() { setOpen(nextOpen); } - // 유효기간 선택 옵션 - const validityOptions = [ - { value: "3", label: "3개월" }, - { value: "6", label: "6개월" }, - { value: "12", label: "1년" }, - { value: "24", label: "2년" }, - { value: "36", label: "3년" }, - { value: "60", label: "5년" }, - ]; + // 현재 선택된 적용 범위 수 + const selectedScopesCount = BUSINESS_UNITS.filter(unit => + form.watch(unit.key as keyof TemplateFormValues) + ).length; return ( @@ -232,108 +274,215 @@ export function AddTemplateDialog() { 템플릿 추가 - + 새 기본계약서 템플릿 추가 - 템플릿 이름을 입력하고 계약서 파일을 업로드하세요. + 템플릿 정보를 입력하고 계약서 파일을 업로드하세요. * 표시된 항목은 필수 입력사항입니다.
- - ( - - - 템플릿 이름 * - - - - - - - )} - /> + + {/* 기본 정보 */} + + + 기본 정보 + + +
+ ( + + + 템플릿 코드 * + + + field.onChange(e.target.value.toUpperCase())} + /> + + + 영문 대문자, 숫자, '_', '-'만 사용 가능 + + + + )} + /> - ( - - - 계약 유효기간 * - - - - 계약서의 유효 기간을 설정합니다. 이 기간이 지나면 재계약이 필요합니다. - - - - )} - /> + ( + + 리비전 + + field.onChange(parseInt(e.target.value) || 1)} + /> + + + 템플릿 버전 (기본값: 1) + + + + )} + /> +
- ( - - - 계약서 파일 * - - - - - - - {selectedFile ? selectedFile.name : "PDF 파일을 여기에 드래그하세요"} - - - {selectedFile - ? `파일 크기: ${(selectedFile.size / (1024 * 1024)).toFixed(2)} MB` - : "또는 클릭하여 PDF 파일을 선택하세요 (최대 100MB)"} - - - - - - - - )} - /> - - {showProgress && ( -
-
- 업로드 진행률 - {uploadProgress}% + ( + + + 템플릿 이름 * + + + + + + + )} + /> + + ( + +
+ 법무검토 필요 + + 법무팀 검토가 필요한 템플릿인지 설정 + +
+ + + +
+ )} + /> + + + + {/* 적용 범위 */} + + + 적용 범위 + + 이 템플릿이 적용될 사업부를 선택하세요. ({selectedScopesCount}개 선택됨) + + + +
+ +
- -
- )} + + + +
+ {BUSINESS_UNITS.map((unit) => ( + ( + + + + +
+ + {unit.label} + +
+
+ )} + /> + ))} +
+ + {form.formState.errors.shipBuildingApplicable && ( +

+ {form.formState.errors.shipBuildingApplicable.message} +

+ )} + + + + {/* 파일 업로드 */} + + + 파일 업로드 + + + ( + + + 계약서 파일 * + + + + + + + {selectedFile ? selectedFile.name : "PDF 파일을 여기에 드래그하세요"} + + + {selectedFile + ? `파일 크기: ${(selectedFile.size / (1024 * 1024)).toFixed(2)} MB` + : "또는 클릭하여 PDF 파일을 선택하세요 (최대 100MB)"} + + + + + + + + )} + /> + + {showProgress && ( +
+
+ 업로드 진행률 + {uploadProgress}% +
+ +
+ )} +
+
-- cgit v1.2.3