From 1dc24d48e52f2e490f5603ceb02842586ecae533 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Thu, 24 Jul 2025 11:06:32 +0000
Subject: (대표님) 정기평가 피드백 반영, 설계 피드백 반영, (최겸) 기술영업
 피드백 반영
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../template/update-basicContract-sheet.tsx        | 106 +++++++++------------
 1 file changed, 43 insertions(+), 63 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 88783461..66037601 100644
--- a/lib/basic-contract/template/update-basicContract-sheet.tsx
+++ b/lib/basic-contract/template/update-basicContract-sheet.tsx
@@ -36,7 +36,6 @@ import {
   SheetHeader,
   SheetTitle,
 } from "@/components/ui/sheet"
-import { Input } from "@/components/ui/input"
 import {
   Dropzone,
   DropzoneZone,
@@ -47,14 +46,16 @@ 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",
@@ -66,14 +67,13 @@ const TEMPLATE_NAME_OPTIONS = [
   "직납자재 하도급대급등 연동제 의향서"
 ] as const;
 
-// 업데이트 템플릿 스키마 정의 (templateCode, status 제거, 워드파일만 허용)
+// 업데이트 템플릿 스키마 정의 (리비전 필드 제거, 워드파일만 허용)
 export const updateTemplateSchema = z.object({
   templateName: z.enum(TEMPLATE_NAME_OPTIONS, {
     required_error: "템플릿 이름을 선택해주세요.",
   }),
-  revision: z.coerce.number().int().min(1, "리비전은 1 이상이어야 합니다."),
   legalReviewRequired: z.boolean(),
-  
+
   // 적용 범위
   shipBuildingApplicable: z.boolean(),
   windApplicable: z.boolean(),
@@ -83,22 +83,22 @@ export const updateTemplateSchema = z.object({
   gyApplicable: z.boolean(),
   sysApplicable: z.boolean(),
   infraApplicable: z.boolean(),
-  
+
   file: z
     .instanceof(File, { message: "파일을 업로드해주세요." })
     .refine((file) => file.size <= 100 * 1024 * 1024, {
       message: "파일 크기는 100MB 이하여야 합니다.",
     })
     .refine(
-      (file) => 
-        file.type === 'application/msword' || 
+      (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 => 
+  const hasAnyScope = BUSINESS_UNITS.some(unit =>
     data[unit.key as keyof typeof data] as boolean
   );
   return hasAnyScope;
@@ -122,8 +122,7 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
   const form = useForm<UpdateTemplateSchema>({
     resolver: zodResolver(updateTemplateSchema),
     defaultValues: {
-      templateName: template?.templateName as typeof TEMPLATE_NAME_OPTIONS[number] ?? "준법서약",
-      revision: template?.revision ?? 1,
+      templateName: template?.templateName as typeof TEMPLATE_NAME_OPTIONS[number] ?? "준법서약 (한글)",
       legalReviewRequired: template?.legalReviewRequired ?? false,
       shipBuildingApplicable: template?.shipBuildingApplicable ?? false,
       windApplicable: template?.windApplicable ?? false,
@@ -147,9 +146,10 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
   };
 
   // 모든 적용 범위 선택/해제
-  const handleSelectAllScopes = (checked: boolean) => {
+  const handleSelectAllScopes = (checked: boolean | "indeterminate") => {
+    const value = checked === true;
     BUSINESS_UNITS.forEach(unit => {
-      form.setValue(unit.key as keyof UpdateTemplateSchema, checked);
+      form.setValue(unit.key as keyof UpdateTemplateSchema, value);
     });
   };
 
@@ -158,7 +158,6 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
     if (template) {
       form.reset({
         templateName: template.templateName as typeof TEMPLATE_NAME_OPTIONS[number],
-        revision: template.revision ?? 1,
         legalReviewRequired: template.legalReviewRequired ?? false,
         shipBuildingApplicable: template.shipBuildingApplicable ?? false,
         windApplicable: template.windApplicable ?? false,
@@ -173,7 +172,7 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
   }, [template, form]);
 
   // 현재 선택된 적용 범위 수
-  const selectedScopesCount = BUSINESS_UNITS.filter(unit => 
+  const selectedScopesCount = BUSINESS_UNITS.filter(unit =>
     form.watch(unit.key as keyof UpdateTemplateSchema)
   ).length;
 
@@ -181,22 +180,21 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
     startUpdateTransition(async () => {
       if (!template) return
 
-      // FormData 객체 생성하여 파일과 데이터를 함께 전송 (templateCode, status 제거)
+      // FormData 객체 생성하여 파일과 데이터를 함께 전송
       const formData = new FormData();
       formData.append("templateName", input.templateName);
-      formData.append("revision", input.revision.toString());
       formData.append("legalReviewRequired", input.legalReviewRequired.toString());
-      
+
       // 적용 범위 추가
       BUSINESS_UNITS.forEach(unit => {
         const value = input[unit.key as keyof UpdateTemplateSchema] as boolean;
         formData.append(unit.key, value.toString());
       });
-      
+
       if (input.file) {
         formData.append("file", input.file);
       }
-      
+
       try {
         // 서비스 함수 호출
         const { error } = await updateTemplate({
@@ -223,6 +221,15 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
 
   if (!template) return null;
 
+  const scopeSelected = BUSINESS_UNITS.some(
+    (unit) => form.watch(unit.key as keyof UpdateTemplateSchema)
+  );
+
+  const isDisabled =
+    isUpdatePending ||
+    !form.watch("templateName") ||
+    !scopeSelected;
+
   return (
     <Sheet {...props}>
       <SheetContent className="sm:max-w-[600px] h-[100vh] flex flex-col p-0">
@@ -234,7 +241,7 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
             <span className="text-red-500 mt-1 block text-sm">* 표시된 항목은 필수 입력사항입니다.</span>
           </SheetDescription>
         </SheetHeader>
-        
+
         {/* 스크롤 가능한 컨텐츠 영역 */}
         <div className="flex-1 overflow-y-auto px-6">
           <Form {...form}>
@@ -247,11 +254,13 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
                 <CardHeader>
                   <CardTitle className="text-lg">기본 정보</CardTitle>
                   <CardDescription>
+                    현재 리비전: <Badge variant="outline">v{template.revision}</Badge>
+                    <br />
                     현재 적용 범위: {scopeHelpers.getScopeDisplayText(template)}
                   </CardDescription>
                 </CardHeader>
                 <CardContent className="space-y-4">
-                  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+                  <div className="grid grid-cols-1 gap-4">
                     <FormField
                       control={form.control}
                       name="templateName"
@@ -283,32 +292,6 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
                         </FormItem>
                       )}
                     />
-
-                    <FormField
-                      control={form.control}
-                      name="revision"
-                      render={({ field }) => (
-                        <FormItem>
-                          <FormLabel>리비전</FormLabel>
-                          <FormControl>
-                            <Input 
-                              type="number" 
-                              min="1" 
-                              {...field}
-                              onChange={(e) => field.onChange(parseInt(e.target.value) || 1)}
-                            />
-                          </FormControl>
-                          <FormDescription>
-                            템플릿 버전을 업데이트하세요.
-                            <br />
-                            <span className="text-xs text-muted-foreground">
-                              동일한 템플릿 이름의 리비전은 중복될 수 없습니다.
-                            </span>
-                          </FormDescription>
-                          <FormMessage />
-                        </FormItem>
-                      )}
-                    />
                   </div>
 
                   <FormField
@@ -346,7 +329,7 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
                 </CardHeader>
                 <CardContent className="space-y-4">
                   <div className="flex items-center space-x-2">
-                    <Checkbox 
+                    <Checkbox
                       id="select-all"
                       checked={selectedScopesCount === BUSINESS_UNITS.length}
                       onCheckedChange={handleSelectAllScopes}
@@ -355,9 +338,9 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
                       전체 선택
                     </label>
                   </div>
-                  
+
                   <Separator />
-                  
+
                   <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
                     {BUSINESS_UNITS.map((unit) => (
                       <FormField
@@ -382,7 +365,7 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
                       />
                     ))}
                   </div>
-                  
+
                   {form.formState.errors.shipBuildingApplicable && (
                     <p className="text-sm text-destructive">
                       {form.formState.errors.shipBuildingApplicable.message}
@@ -417,13 +400,13 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
                             <DropzoneZone>
                               <DropzoneUploadIcon className="h-10 w-10 text-muted-foreground" />
                               <DropzoneTitle>
-                                {selectedFile 
-                                  ? selectedFile.name 
+                                {selectedFile
+                                  ? selectedFile.name
                                   : "새 워드 파일을 드래그하세요 (선택사항)"}
                               </DropzoneTitle>
                               <DropzoneDescription>
-                                {selectedFile 
-                                  ? `파일 크기: ${(selectedFile.size / (1024 * 1024)).toFixed(2)} MB` 
+                                {selectedFile
+                                  ? `파일 크기: ${(selectedFile.size / (1024 * 1024)).toFixed(2)} MB`
                                   : "또는 클릭하여 워드 파일(.doc, .docx)을 선택하세요 (최대 100MB)"}
                               </DropzoneDescription>
                               <DropzoneInput />
@@ -447,16 +430,13 @@ export function UpdateTemplateSheet({ template, onSuccess, ...props }: UpdateTem
               취소
             </Button>
           </SheetClose>
-          <Button 
+          <Button
             type="button"
             onClick={form.handleSubmit(onSubmit)}
-            disabled={isUpdatePending || !form.formState.isValid}
+            disabled={isDisabled}
           >
             {isUpdatePending && (
-              <Loader
-                className="mr-2 size-4 animate-spin"
-                aria-hidden="true"
-              />
+              <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" />
             )}
             저장
           </Button>
-- 
cgit v1.2.3

