diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-19 09:23:47 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-19 09:23:47 +0000 |
| commit | 8077419e40368dc703f94d558fc746b73fbc6702 (patch) | |
| tree | 333bdfb3b0d84336f1bf7d4f0f1bbced6bec2d4c /lib/vendors/table/request-pq-dialog.tsx | |
| parent | aa71f75ace013b2fe982e5a104e61440458e0fd2 (diff) | |
(최겸) 구매 PQ 비밀유지계약서 별첨 첨부파일 추가, 정규업체등록관리 개발
Diffstat (limited to 'lib/vendors/table/request-pq-dialog.tsx')
| -rw-r--r-- | lib/vendors/table/request-pq-dialog.tsx | 148 |
1 files changed, 134 insertions, 14 deletions
diff --git a/lib/vendors/table/request-pq-dialog.tsx b/lib/vendors/table/request-pq-dialog.tsx index 5b5f722c..9fd7b1d8 100644 --- a/lib/vendors/table/request-pq-dialog.tsx +++ b/lib/vendors/table/request-pq-dialog.tsx @@ -46,6 +46,7 @@ import { DatePicker } from "@/components/ui/date-picker" import { getALLBasicContractTemplates } from "@/lib/basic-contract/service"
import type { BasicContractTemplate } from "@/db/schema"
import { searchItemsForPQ } from "@/lib/items/service"
+import { saveNdaAttachments } from "../service"
// import { PQContractViewer } from "../pq-contract-viewer" // 더 이상 사용하지 않음
interface RequestPQDialogProps extends React.ComponentPropsWithoutRef<typeof Dialog> {
@@ -54,16 +55,16 @@ interface RequestPQDialogProps extends React.ComponentPropsWithoutRef<typeof Dia onSuccess?: () => void
}
-const AGREEMENT_LIST = [
- "준법서약",
- "표준하도급계약",
- "안전보건관리계약",
- "윤리규범 준수 서약",
- "동반성장협약",
- "내국신용장 미개설 합의",
- "기술자료 제출 기본 동의",
- "GTC 합의",
-]
+// const AGREEMENT_LIST = [
+// "준법서약",
+// "표준하도급계약",
+// "안전보건관리계약",
+// "윤리규범 준수 서약",
+// "동반성장협약",
+// "내국신용장 미개설 합의",
+// "기술자료 제출 기본 동의",
+// "GTC 합의",
+// ]
// PQ 대상 품목 타입 정의
interface PQItem {
@@ -92,6 +93,10 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro const [basicContractTemplates, setBasicContractTemplates] = React.useState<BasicContractTemplate[]>([])
const [selectedTemplateIds, setSelectedTemplateIds] = React.useState<number[]>([])
const [isLoadingTemplates, setIsLoadingTemplates] = React.useState(false)
+
+ // 비밀유지 계약서 첨부파일 관련 상태
+ const [ndaAttachments, setNdaAttachments] = React.useState<File[]>([])
+ const [isUploadingNdaFiles, setIsUploadingNdaFiles] = React.useState(false)
// 아이템 검색 필터링
React.useEffect(() => {
@@ -172,6 +177,8 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro setItemSearchQuery("")
setFilteredItems([])
setShowItemDropdown(false)
+ setNdaAttachments([])
+ setIsUploadingNdaFiles(false)
}
}, [props.open])
@@ -197,6 +204,30 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro setPqItems(prev => prev.filter(item => item.itemCode !== itemCode))
}
+ // 비밀유지 계약서 첨부파일 추가 함수
+ const handleAddNdaAttachment = (event: React.ChangeEvent<HTMLInputElement>) => {
+ const files = event.target.files
+ if (files) {
+ const newFiles = Array.from(files)
+ setNdaAttachments(prev => [...prev, ...newFiles])
+ }
+ // input 초기화
+ event.target.value = ''
+ }
+
+ // 비밀유지 계약서 첨부파일 제거 함수
+ const handleRemoveNdaAttachment = (fileIndex: number) => {
+ setNdaAttachments(prev => prev.filter((_, index) => index !== fileIndex))
+ }
+
+ // 비밀유지 계약서가 선택되었는지 확인하는 함수
+ const isNdaTemplateSelected = () => {
+ return basicContractTemplates.some(template =>
+ selectedTemplateIds.includes(template.id) &&
+ template.templateName?.includes("비밀유지")
+ )
+ }
+
const onApprove = () => {
if (!type) return toast.error("PQ 유형을 선택하세요.")
if (type === "PROJECT" && !selectedProjectId) return toast.error("프로젝트를 선택하세요.")
@@ -235,6 +266,23 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro console.log("📋 기본계약서 백그라운드 처리 시작", templates.length, "개 템플릿")
await processBasicContractsInBackground(templates, vendors)
}
+
+ // 3단계: 비밀유지 계약서 첨부파일이 있는 경우 저장
+ if (isNdaTemplateSelected() && ndaAttachments.length > 0) {
+ console.log("📎 비밀유지 계약서 첨부파일 처리 시작", ndaAttachments.length, "개 파일")
+
+ const ndaResult = await saveNdaAttachments({
+ vendorIds: vendors.map((v) => v.id),
+ files: ndaAttachments,
+ userId: session.user.id.toString()
+ })
+
+ if (ndaResult.success) {
+ toast.success(`비밀유지 계약서 첨부파일이 모두 저장되었습니다 (${ndaResult.summary?.success}/${ndaResult.summary?.total})`)
+ } else {
+ toast.error(`첨부파일 처리 중 일부 오류가 발생했습니다: ${ndaResult.error}`)
+ }
+ }
// 완료 후 다이얼로그 닫기
props.onOpenChange?.(false)
@@ -262,12 +310,14 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro for (let vendorIndex = 0; vendorIndex < vendors.length; vendorIndex++) {
const vendor = vendors[vendorIndex]
- // 벤더별 템플릿 데이터 생성
+ // 벤더별 템플릿 데이터 생성 (한글 변수명 사용)
const templateData = {
- vendor_name: vendor.vendorName || '협력업체명',
- address: vendor.address || '주소',
+ company_name: vendor.vendorName || '협력업체명',
+ company_address: vendor.address || '주소',
representative_name: vendor.representativeName || '대표자명',
- today_date: new Date().toLocaleDateString('ko-KR'),
+ signature_date: new Date().toLocaleDateString('ko-KR'),
+ tax_id: vendor.taxId || '사업자번호',
+ phone_number: vendor.phone || '전화번호',
}
console.log(`🔄 벤더 ${vendorIndex + 1}/${vendors.length} 템플릿 데이터:`, templateData)
@@ -559,6 +609,76 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro )}
</div>
+ {/* 비밀유지 계약서 첨부파일 */}
+ {isNdaTemplateSelected() && (
+ <div className="space-y-2">
+ <Label>비밀유지 계약서 첨부파일</Label>
+
+ {/* 선택된 파일들 표시 */}
+ {ndaAttachments.length > 0 && (
+ <div className="space-y-2">
+ <div className="text-sm text-muted-foreground">
+ 선택된 파일 ({ndaAttachments.length}개)
+ </div>
+ <div className="space-y-1 max-h-32 overflow-y-auto border rounded-md p-2">
+ {ndaAttachments.map((file, index) => (
+ <div key={index} className="flex items-center justify-between text-sm bg-muted/50 rounded px-2 py-1">
+ <div className="flex-1 truncate">
+ <span className="font-medium">{file.name}</span>
+ <span className="text-muted-foreground ml-2">
+ ({(file.size / 1024 / 1024).toFixed(2)} MB)
+ </span>
+ </div>
+ <Button
+ type="button"
+ variant="ghost"
+ size="sm"
+ className="h-6 w-6 p-0 hover:bg-destructive hover:text-destructive-foreground"
+ onClick={() => handleRemoveNdaAttachment(index)}
+ >
+ <X className="h-3 w-3" />
+ </Button>
+ </div>
+ ))}
+ </div>
+ </div>
+ )}
+
+ {/* 파일 선택 버튼 */}
+ <div className="flex items-center gap-2">
+ <input
+ type="file"
+ multiple
+ accept=".pdf,.doc,.docx,.xlsx,.xls,.png,.jpg,.jpeg"
+ onChange={handleAddNdaAttachment}
+ className="hidden"
+ id="nda-file-input"
+ />
+ <Button
+ type="button"
+ variant="outline"
+ size="sm"
+ className="gap-2"
+ onClick={() => document.getElementById('nda-file-input')?.click()}
+ disabled={isUploadingNdaFiles}
+ >
+ <Plus className="h-4 w-4" />
+ 파일 추가
+ </Button>
+ {isUploadingNdaFiles && (
+ <div className="text-sm text-muted-foreground">
+ 파일 업로드 중...
+ </div>
+ )}
+ </div>
+
+ <div className="text-xs text-muted-foreground">
+ 비밀유지 계약서와 관련된 첨부파일을 업로드하세요.
+ 각 벤더별로 동일한 파일이 저장됩니다.
+ </div>
+ </div>
+ )}
+
{/* <div className="space-y-2">
<Label>계약 항목 선택</Label>
{AGREEMENT_LIST.map((label) => (
|
