From a50bc9baea332f996e6bc3a5d70c69f6d2d0f194 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Wed, 23 Jul 2025 09:08:03 +0000 Subject: (대표님, 최겸) 기본계약 템플릿 및 에디터, 기술영업 벤더정보, 파일 보안다운로드, 벤더 document sync 상태 서비스, 메뉴 Config, 기술영업 미사용 제거 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../template/update-basicContract-sheet.tsx | 487 ++++++++++----------- 1 file changed, 243 insertions(+), 244 deletions(-) (limited to 'lib/basic-contract/template/update-basicContract-sheet.tsx') diff --git a/lib/basic-contract/template/update-basicContract-sheet.tsx b/lib/basic-contract/template/update-basicContract-sheet.tsx index 810e1b77..88783461 100644 --- a/lib/basic-contract/template/update-basicContract-sheet.tsx +++ b/lib/basic-contract/template/update-basicContract-sheet.tsx @@ -47,15 +47,30 @@ import { } from "@/components/ui/dropzone" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Separator } from "@/components/ui/separator" -import { Badge } from "@/components/ui/badge" import { updateTemplate } from "../service" import { BasicContractTemplate } from "@/db/schema" import { BUSINESS_UNITS, scopeHelpers } from "@/config/basicContractColumnsConfig" -// 업데이트 템플릿 스키마 정의 +// 템플릿 이름 옵션 정의 +const TEMPLATE_NAME_OPTIONS = [ + "준법서약", + "기술자료 요구서", + "비밀유지 계약서", + "표준하도급기본 계약서", + "General GTC", + "안전보건관리 약정서", + "동반성장", + "윤리규범 준수 서약서", + "기술자료 동의서", + "내국신용장 미개설 합의서", + "직납자재 하도급대급등 연동제 의향서" +] as const; + +// 업데이트 템플릿 스키마 정의 (templateCode, status 제거, 워드파일만 허용) export const updateTemplateSchema = z.object({ - templateCode: z.string().min(1, "템플릿 코드는 필수입니다."), // readonly로 처리 - templateName: z.string().min(1, "템플릿 이름은 필수입니다."), + templateName: z.enum(TEMPLATE_NAME_OPTIONS, { + required_error: "템플릿 이름을 선택해주세요.", + }), revision: z.coerce.number().int().min(1, "리비전은 1 이상이어야 합니다."), legalReviewRequired: z.boolean(), @@ -69,10 +84,18 @@ export const updateTemplateSchema = z.object({ sysApplicable: z.boolean(), infraApplicable: z.boolean(), - status: z.enum(["ACTIVE", "DISPOSED"], { - required_error: "상태는 필수 선택사항입니다.", - }), - file: z.instanceof(File, { message: "파일을 업로드해주세요." }).optional(), + file: z + .instanceof(File, { message: "파일을 업로드해주세요." }) + .refine((file) => file.size <= 100 * 1024 * 1024, { + message: "파일 크기는 100MB 이하여야 합니다.", + }) + .refine( + (file) => + file.type === 'application/msword' || + file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + { message: "워드 파일(.doc, .docx)만 업로드 가능합니다." } + ) + .optional(), }).refine((data) => { // 적어도 하나의 적용 범위는 선택되어야 함 const hasAnyScope = BUSINESS_UNITS.some(unit => @@ -99,8 +122,7 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem const form = useForm({ resolver: zodResolver(updateTemplateSchema), defaultValues: { - templateCode: template?.templateCode ?? "", - templateName: template?.templateName ?? "", + templateName: template?.templateName as typeof TEMPLATE_NAME_OPTIONS[number] ?? "준법서약", revision: template?.revision ?? 1, legalReviewRequired: template?.legalReviewRequired ?? false, shipBuildingApplicable: template?.shipBuildingApplicable ?? false, @@ -111,7 +133,6 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem gyApplicable: template?.gyApplicable ?? false, sysApplicable: template?.sysApplicable ?? false, infraApplicable: template?.infraApplicable ?? false, - status: (template?.status as "ACTIVE" | "DISPOSED") || "ACTIVE" }, mode: "onChange" }) @@ -136,8 +157,7 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem React.useEffect(() => { if (template) { form.reset({ - templateCode: template.templateCode, - templateName: template.templateName, + templateName: template.templateName as typeof TEMPLATE_NAME_OPTIONS[number], revision: template.revision ?? 1, legalReviewRequired: template.legalReviewRequired ?? false, shipBuildingApplicable: template.shipBuildingApplicable ?? false, @@ -148,7 +168,6 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem gyApplicable: template.gyApplicable ?? false, sysApplicable: template.sysApplicable ?? false, infraApplicable: template.infraApplicable ?? false, - status: template.status as "ACTIVE" | "DISPOSED", }); } }, [template, form]); @@ -162,9 +181,8 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem startUpdateTransition(async () => { if (!template) return - // FormData 객체 생성하여 파일과 데이터를 함께 전송 + // FormData 객체 생성하여 파일과 데이터를 함께 전송 (templateCode, status 제거) const formData = new FormData(); - formData.append("templateCode", input.templateCode); formData.append("templateName", input.templateName); formData.append("revision", input.revision.toString()); formData.append("legalReviewRequired", input.legalReviewRequired.toString()); @@ -175,8 +193,6 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem formData.append(unit.key, value.toString()); }); - formData.append("status", input.status); - if (input.file) { formData.append("file", input.file); } @@ -209,259 +225,242 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem return ( - - + + {/* 고정된 헤더 */} + 템플릿 업데이트 템플릿 정보를 수정하고 변경사항을 저장하세요 + * 표시된 항목은 필수 입력사항입니다. -
- - {/* 기본 정보 */} - - - 기본 정보 - - 현재 적용 범위: {scopeHelpers.getScopeDisplayText(template)} - - - -
- ( - - 템플릿 코드 - - - - - 템플릿 코드는 수정할 수 없습니다. - - - )} - /> + {/* 스크롤 가능한 컨텐츠 영역 */} +
+ + + {/* 기본 정보 */} + + + 기본 정보 + + 현재 적용 범위: {scopeHelpers.getScopeDisplayText(template)} + + + +
+ ( + + + 템플릿 이름 * + + + + 미리 정의된 템플릿 중에서 선택 + + + + )} + /> + + ( + + 리비전 + + field.onChange(parseInt(e.target.value) || 1)} + /> + + + 템플릿 버전을 업데이트하세요. +
+ + 동일한 템플릿 이름의 리비전은 중복될 수 없습니다. + +
+ +
+ )} + /> +
( - - 리비전 + +
+ 법무검토 필요 + + 법무팀 검토가 필요한 템플릿인지 설정 + +
- field.onChange(parseInt(e.target.value) || 1)} + - - 템플릿 버전을 업데이트하세요. - -
)} /> -
+ + - ( - - 템플릿 이름 - - - - - + {/* 적용 범위 */} + + + + 적용 범위 * + + + 이 템플릿이 적용될 사업부를 선택하세요. ({selectedScopesCount}개 선택됨) + + + +
+ + +
+ + + +
+ {BUSINESS_UNITS.map((unit) => ( + ( + + + + +
+ + {unit.label} + +
+
+ )} + /> + ))} +
+ + {form.formState.errors.shipBuildingApplicable && ( +

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

)} - /> +
+
-
+ {/* 파일 업데이트 */} + + + 파일 업데이트 + + 현재 파일: {template.fileName} + + + ( + name="file" + render={() => ( - 상태 - + 템플릿 파일 (선택사항) + + + + + + {selectedFile + ? selectedFile.name + : "새 워드 파일을 드래그하세요 (선택사항)"} + + + {selectedFile + ? `파일 크기: ${(selectedFile.size / (1024 * 1024)).toFixed(2)} MB` + : "또는 클릭하여 워드 파일(.doc, .docx)을 선택하세요 (최대 100MB)"} + + + + + )} /> -
- - ( - -
- 법무검토 필요 - - 법무팀 검토가 필요한 템플릿인지 설정 - -
- - - -
- )} - /> - - - - {/* 적용 범위 */} - - - 적용 범위 - - 이 템플릿이 적용될 사업부를 선택하세요. ({selectedScopesCount}개 선택됨) - - - -
- - -
- - - -
- {BUSINESS_UNITS.map((unit) => ( - ( - - - - -
- - {unit.label} - -
-
- )} - /> - ))} -
- - {form.formState.errors.shipBuildingApplicable && ( -

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

- )} -
-
+ + + + +
- {/* 파일 업데이트 */} - - - 파일 업데이트 - - 현재 파일: {template.fileName} - - - - ( - - 템플릿 파일 (선택사항) - - - - - - {selectedFile - ? selectedFile.name - : "새 파일을 드래그하세요 (선택사항)"} - - - {selectedFile - ? `파일 크기: ${(selectedFile.size / (1024 * 1024)).toFixed(2)} MB` - : "또는 클릭하여 파일을 선택하세요"} - - - - - - - - )} - /> - - - - - - - - - - - + {/* 고정된 푸터 */} + + + + + +
) -- cgit v1.2.3