diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-15 10:07:09 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-15 10:07:09 +0000 |
| commit | 4eb7532f822c821fb6b69bf103bd075fefba769b (patch) | |
| tree | b4bcf6c0bf791d71569f3f35498ed256bf7cfaf3 /lib/basic-contract/template/add-basic-contract-template-dialog.tsx | |
| parent | 660c7888d885badab7af3e96f9c16bd0172ad0f1 (diff) | |
(대표님) 20250715 협력사 정기평가, spreadJS, roles 서비스에 함수 추가
Diffstat (limited to 'lib/basic-contract/template/add-basic-contract-template-dialog.tsx')
| -rw-r--r-- | lib/basic-contract/template/add-basic-contract-template-dialog.tsx | 406 |
1 files changed, 209 insertions, 197 deletions
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 3a83d50f..c88819e4 100644 --- a/lib/basic-contract/template/add-basic-contract-template-dialog.tsx +++ b/lib/basic-contract/template/add-basic-contract-template-dialog.tsx @@ -73,9 +73,12 @@ const templateFormSchema = z.object({ status: z.enum(["ACTIVE", "DISPOSED"]).default("ACTIVE"),
}).refine((data) => {
// 적어도 하나의 적용 범위는 선택되어야 함
- const hasAnyScope = BUSINESS_UNITS.some(unit =>
- data[unit.key as keyof typeof data] as boolean
- );
+ const scopeFields = [
+ 'shipBuildingApplicable', 'windApplicable', 'pcApplicable', 'nbApplicable',
+ 'rcApplicable', 'gyApplicable', 'sysApplicable', 'infraApplicable'
+ ];
+
+ const hasAnyScope = scopeFields.some(field => data[field as keyof typeof data] === true);
return hasAnyScope;
}, {
message: "적어도 하나의 적용 범위를 선택해야 합니다.",
@@ -274,42 +277,85 @@ export function AddTemplateDialog() { 템플릿 추가
</Button>
</DialogTrigger>
- <DialogContent className="sm:max-w-[600px] max-h-[90vh] overflow-y-auto">
- <DialogHeader>
+ <DialogContent className="sm:max-w-[600px] h-[90vh] flex flex-col p-0">
+ {/* 고정된 헤더 */}
+ <DialogHeader className="p-6 pb-4 border-b">
<DialogTitle>새 기본계약서 템플릿 추가</DialogTitle>
<DialogDescription>
템플릿 정보를 입력하고 계약서 파일을 업로드하세요.
<span className="text-red-500 mt-1 block text-sm">* 표시된 항목은 필수 입력사항입니다.</span>
</DialogDescription>
</DialogHeader>
- <Form {...form}>
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
- {/* 기본 정보 */}
- <Card>
- <CardHeader>
- <CardTitle className="text-lg">기본 정보</CardTitle>
- </CardHeader>
- <CardContent className="space-y-4">
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+
+ {/* 스크롤 가능한 컨텐츠 영역 */}
+ <div className="flex-1 overflow-y-auto px-6">
+ <Form {...form}>
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 py-4">
+ {/* 기본 정보 */}
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-lg">기본 정보</CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-4">
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="templateCode"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>
+ 템플릿 코드 <span className="text-red-500">*</span>
+ </FormLabel>
+ <FormControl>
+ <Input
+ placeholder="TEMPLATE_001"
+ {...field}
+ style={{ textTransform: 'uppercase' }}
+ onChange={(e) => field.onChange(e.target.value.toUpperCase())}
+ />
+ </FormControl>
+ <FormDescription>
+ 영문 대문자, 숫자, '_', '-'만 사용 가능
+ </FormDescription>
+ <FormMessage />
+ </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>
+ 템플릿 버전 (기본값: 1)
+ </FormDescription>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
<FormField
control={form.control}
- name="templateCode"
+ name="templateName"
render={({ field }) => (
<FormItem>
<FormLabel>
- 템플릿 코드 <span className="text-red-500">*</span>
+ 템플릿 이름 <span className="text-red-500">*</span>
</FormLabel>
<FormControl>
- <Input
- placeholder="TEMPLATE_001"
- {...field}
- style={{ textTransform: 'uppercase' }}
- onChange={(e) => field.onChange(e.target.value.toUpperCase())}
- />
+ <Input placeholder="기본 계약서 템플릿" {...field} />
</FormControl>
- <FormDescription>
- 영문 대문자, 숫자, '_', '-'만 사용 가능
- </FormDescription>
<FormMessage />
</FormItem>
)}
@@ -317,191 +363,157 @@ export function AddTemplateDialog() { <FormField
control={form.control}
- name="revision"
+ name="legalReviewRequired"
render={({ field }) => (
- <FormItem>
- <FormLabel>리비전</FormLabel>
+ <FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
+ <div className="space-y-0.5">
+ <FormLabel>법무검토 필요</FormLabel>
+ <FormDescription>
+ 법무팀 검토가 필요한 템플릿인지 설정
+ </FormDescription>
+ </div>
<FormControl>
- <Input
- type="number"
- min="1"
- {...field}
- onChange={(e) => field.onChange(parseInt(e.target.value) || 1)}
+ <Switch
+ checked={field.value}
+ onCheckedChange={field.onChange}
/>
</FormControl>
- <FormDescription>
- 템플릿 버전 (기본값: 1)
- </FormDescription>
- <FormMessage />
</FormItem>
)}
/>
- </div>
-
- <FormField
- control={form.control}
- name="templateName"
- render={({ field }) => (
- <FormItem>
- <FormLabel>
- 템플릿 이름 <span className="text-red-500">*</span>
- </FormLabel>
- <FormControl>
- <Input placeholder="기본 계약서 템플릿" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
+ </CardContent>
+ </Card>
- <FormField
- control={form.control}
- name="legalReviewRequired"
- render={({ field }) => (
- <FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
- <div className="space-y-0.5">
- <FormLabel>법무검토 필요</FormLabel>
- <FormDescription>
- 법무팀 검토가 필요한 템플릿인지 설정
- </FormDescription>
- </div>
- <FormControl>
- <Switch
- checked={field.value}
- onCheckedChange={field.onChange}
- />
- </FormControl>
- </FormItem>
+ {/* 적용 범위 */}
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-lg">
+ 적용 범위 <span className="text-red-500">*</span>
+ </CardTitle>
+ <CardDescription>
+ 이 템플릿이 적용될 사업부를 선택하세요. ({selectedScopesCount}개 선택됨)
+ </CardDescription>
+ </CardHeader>
+ <CardContent className="space-y-4">
+ <div className="flex items-center space-x-2">
+ <Checkbox
+ id="select-all"
+ checked={selectedScopesCount === BUSINESS_UNITS.length}
+ onCheckedChange={handleSelectAllScopes}
+ />
+ <label htmlFor="select-all" className="text-sm font-medium">
+ 전체 선택
+ </label>
+ </div>
+
+ <Separator />
+
+ <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
+ {BUSINESS_UNITS.map((unit) => (
+ <FormField
+ key={unit.key}
+ control={form.control}
+ name={unit.key as keyof TemplateFormValues}
+ render={({ field }) => (
+ <FormItem className="flex flex-row items-start space-x-3 space-y-0">
+ <FormControl>
+ <Checkbox
+ checked={field.value as boolean}
+ onCheckedChange={field.onChange}
+ />
+ </FormControl>
+ <div className="space-y-1 leading-none">
+ <FormLabel className="text-sm font-normal">
+ {unit.label}
+ </FormLabel>
+ </div>
+ </FormItem>
+ )}
+ />
+ ))}
+ </div>
+
+ {form.formState.errors.shipBuildingApplicable && (
+ <p className="text-sm text-destructive">
+ {form.formState.errors.shipBuildingApplicable.message}
+ </p>
)}
- />
- </CardContent>
- </Card>
+ </CardContent>
+ </Card>
- {/* 적용 범위 */}
- <Card>
- <CardHeader>
- <CardTitle className="text-lg">적용 범위</CardTitle>
- <CardDescription>
- 이 템플릿이 적용될 사업부를 선택하세요. ({selectedScopesCount}개 선택됨)
- </CardDescription>
- </CardHeader>
- <CardContent className="space-y-4">
- <div className="flex items-center space-x-2">
- <Checkbox
- id="select-all"
- checked={selectedScopesCount === BUSINESS_UNITS.length}
- onCheckedChange={handleSelectAllScopes}
+ {/* 파일 업로드 */}
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-lg">파일 업로드</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <FormField
+ control={form.control}
+ name="file"
+ render={() => (
+ <FormItem>
+ <FormLabel>
+ 계약서 파일 <span className="text-red-500">*</span>
+ </FormLabel>
+ <FormControl>
+ <Dropzone
+ onDrop={handleFileChange}
+ accept={{
+ 'application/pdf': ['.pdf']
+ }}
+ >
+ <DropzoneZone>
+ <DropzoneUploadIcon className="h-10 w-10 text-muted-foreground" />
+ <DropzoneTitle>
+ {selectedFile ? selectedFile.name : "PDF 파일을 여기에 드래그하세요"}
+ </DropzoneTitle>
+ <DropzoneDescription>
+ {selectedFile
+ ? `파일 크기: ${(selectedFile.size / (1024 * 1024)).toFixed(2)} MB`
+ : "또는 클릭하여 PDF 파일을 선택하세요 (최대 100MB)"}
+ </DropzoneDescription>
+ <DropzoneInput />
+ </DropzoneZone>
+ </Dropzone>
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
/>
- <label htmlFor="select-all" className="text-sm font-medium">
- 전체 선택
- </label>
- </div>
-
- <Separator />
-
- <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
- {BUSINESS_UNITS.map((unit) => (
- <FormField
- key={unit.key}
- control={form.control}
- name={unit.key as keyof TemplateFormValues}
- render={({ field }) => (
- <FormItem className="flex flex-row items-start space-x-3 space-y-0">
- <FormControl>
- <Checkbox
- checked={field.value as boolean}
- onCheckedChange={field.onChange}
- />
- </FormControl>
- <div className="space-y-1 leading-none">
- <FormLabel className="text-sm font-normal">
- {unit.label}
- </FormLabel>
- </div>
- </FormItem>
- )}
- />
- ))}
- </div>
-
- {form.formState.errors.shipBuildingApplicable && (
- <p className="text-sm text-destructive">
- {form.formState.errors.shipBuildingApplicable.message}
- </p>
- )}
- </CardContent>
- </Card>
-
- {/* 파일 업로드 */}
- <Card>
- <CardHeader>
- <CardTitle className="text-lg">파일 업로드</CardTitle>
- </CardHeader>
- <CardContent>
- <FormField
- control={form.control}
- name="file"
- render={() => (
- <FormItem>
- <FormLabel>
- 계약서 파일 <span className="text-red-500">*</span>
- </FormLabel>
- <FormControl>
- <Dropzone
- onDrop={handleFileChange}
- accept={{
- 'application/pdf': ['.pdf']
- }}
- >
- <DropzoneZone>
- <DropzoneUploadIcon className="h-10 w-10 text-muted-foreground" />
- <DropzoneTitle>
- {selectedFile ? selectedFile.name : "PDF 파일을 여기에 드래그하세요"}
- </DropzoneTitle>
- <DropzoneDescription>
- {selectedFile
- ? `파일 크기: ${(selectedFile.size / (1024 * 1024)).toFixed(2)} MB`
- : "또는 클릭하여 PDF 파일을 선택하세요 (최대 100MB)"}
- </DropzoneDescription>
- <DropzoneInput />
- </DropzoneZone>
- </Dropzone>
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
-
- {showProgress && (
- <div className="space-y-2 mt-4">
- <div className="flex justify-between text-sm">
- <span>업로드 진행률</span>
- <span>{uploadProgress}%</span>
+
+ {showProgress && (
+ <div className="space-y-2 mt-4">
+ <div className="flex justify-between text-sm">
+ <span>업로드 진행률</span>
+ <span>{uploadProgress}%</span>
+ </div>
+ <Progress value={uploadProgress} />
</div>
- <Progress value={uploadProgress} />
- </div>
- )}
- </CardContent>
- </Card>
+ )}
+ </CardContent>
+ </Card>
+ </form>
+ </Form>
+ </div>
- <DialogFooter>
- <Button
- type="button"
- variant="outline"
- onClick={() => setOpen(false)}
- disabled={isLoading}
- >
- 취소
- </Button>
- <Button
- type="submit"
- disabled={isLoading || !form.formState.isValid}
- >
- {isLoading ? "처리 중..." : "추가"}
- </Button>
- </DialogFooter>
- </form>
- </Form>
+ {/* 고정된 푸터 */}
+ <DialogFooter className="p-6 pt-4 border-t">
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => setOpen(false)}
+ disabled={isLoading}
+ >
+ 취소
+ </Button>
+ <Button
+ type="button"
+ onClick={form.handleSubmit(onSubmit)}
+ disabled={isLoading || !form.formState.isValid}
+ >
+ {isLoading ? "처리 중..." : "추가"}
+ </Button>
+ </DialogFooter>
</DialogContent>
</Dialog>
);
|
