From e270e477f362dd68249bb4a013c66eab293bba82 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 7 Aug 2025 05:04:39 +0000 Subject: (최겸) PQ요청+기본계약 로직 수정(한글화 미적용) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/basic-contract/service.ts | 12 +++ .../template/template-editor-wrapper.tsx | 103 ++++++++++++++++----- .../vendor-table/basic-contract-columns.tsx | 38 ++++---- .../vendor-table/basic-contract-sign-dialog.tsx | 10 +- .../viewer/basic-contract-sign-viewer.tsx | 11 ++- 5 files changed, 125 insertions(+), 49 deletions(-) (limited to 'lib/basic-contract') diff --git a/lib/basic-contract/service.ts b/lib/basic-contract/service.ts index 9b5505b5..03b27f96 100644 --- a/lib/basic-contract/service.ts +++ b/lib/basic-contract/service.ts @@ -1129,4 +1129,16 @@ export async function getExistingTemplateNames(): Promise { .groupBy(basicContractTemplates.templateName); return rows.map((r) => r.templateName); +} + +export async function getExistingTemplateNamesById(id:number): Promise { + const rows = await db + .select({ + templateName: basicContractTemplates.templateName, + }) + .from(basicContractTemplates) + .where(and(eq(basicContractTemplates.status,"ACTIVE"),eq(basicContractTemplates.id,id))) + .limit(1) + + return rows[0].templateName; } \ No newline at end of file diff --git a/lib/basic-contract/template/template-editor-wrapper.tsx b/lib/basic-contract/template/template-editor-wrapper.tsx index ea353b91..96e2330f 100644 --- a/lib/basic-contract/template/template-editor-wrapper.tsx +++ b/lib/basic-contract/template/template-editor-wrapper.tsx @@ -7,7 +7,7 @@ import { Save, RefreshCw, Type, FileText, AlertCircle } from "lucide-react"; import type { WebViewerInstance } from "@pdftron/webviewer"; import { Badge } from "@/components/ui/badge"; import { BasicContractTemplateViewer } from "./basic-contract-template-viewer"; -import { saveTemplateFile } from "../service"; +import { getExistingTemplateNamesById, saveTemplateFile } from "../service"; interface TemplateEditorWrapperProps { templateId: string | number; @@ -16,9 +16,27 @@ interface TemplateEditorWrapperProps { refreshAction?: () => Promise; } +// 템플릿 이름별 변수 매핑 (영문 변수명 사용) +const TEMPLATE_VARIABLES_MAP = { + "준법서약 (한글)": ["vendor_name", "address", "representative_name", "today_date"], + "준법서약 (영문)": ["vendor_name", "address", "representative_name", "today_date"], + "기술자료 요구서": ["vendor_name", "address", "representative_name", "today_date"], + "비밀유지 계약서": ["vendor_name", "address", "representative_name", "today_date"], + "표준하도급기본 계약서": ["vendor_name", "address", "representative_name", "today_date"], + "GTC": ["vendor_name", "address", "representative_name", "today_date"], + "안전보건관리 약정서": ["vendor_name", "address", "representative_name", "today_date"], + "동반성장": ["vendor_name", "address", "representative_name", "today_date"], + "윤리규범 준수 서약서": ["vendor_name", "address", "representative_name", "today_date"], + "기술자료 동의서": ["vendor_name", "address", "representative_name", "today_date"], + "내국신용장 미개설 합의서": ["vendor_name", "address", "representative_name", "today_date"], + "직납자재 하도급대급등 연동제 의향서": ["vendor_name", "address", "representative_name", "today_date"] +} as const; + // 변수 패턴 감지를 위한 정규식 const VARIABLE_PATTERN = /\{\{([^}]+)\}\}/g; + + export function TemplateEditorWrapper({ templateId, filePath, @@ -28,6 +46,35 @@ export function TemplateEditorWrapper({ const [instance, setInstance] = React.useState(null); const [isSaving, setIsSaving] = React.useState(false); const [documentVariables, setDocumentVariables] = React.useState([]); + const [templateName, setTemplateName] = React.useState(""); + const [predefinedVariables, setPredefinedVariables] = React.useState([]); + + console.log(templateId, "templateId"); + + // 템플릿 이름 로드 및 변수 설정 + React.useEffect(() => { + const loadTemplateInfo = async () => { + try { + const name = await getExistingTemplateNamesById(Number(templateId)); + setTemplateName(name); + + // 템플릿 이름에 따른 변수 설정 + const variables = TEMPLATE_VARIABLES_MAP[name as keyof typeof TEMPLATE_VARIABLES_MAP] || []; + setPredefinedVariables(variables); + + console.log("🏷️ 템플릿 이름:", name); + console.log("📝 할당된 변수들:", variables); + } catch (error) { + console.error("템플릿 정보 로드 오류:", error); + // 기본 변수 설정 + setPredefinedVariables(["회사명", "주소", "대표자명", "오늘날짜"]); + } + }; + + if (templateId) { + loadTemplateInfo(); + } + }, [templateId]); // 문서에서 변수 추출 const extractVariablesFromDocument = async () => { @@ -220,13 +267,6 @@ export function TemplateEditorWrapper({ window.location.reload(); }; - // 미리 정의된 변수들 - const predefinedVariables = [ - "회사명", "계약자명", "계약일자", "계약금액", - "납기일자", "담당자명", "담당자연락처", "프로젝트명", - "계약번호", "사업부", "부서명", "승인자명" - ]; - return (
{/* 상단 도구 모음 */} @@ -267,6 +307,12 @@ export function TemplateEditorWrapper({ {fileName} + {templateName && ( + + + {templateName} + + )} {documentVariables.length > 0 && ( @@ -283,6 +329,11 @@ export function TemplateEditorWrapper({

변수 관리 + {templateName && ( + + ({templateName}) + + )}

@@ -301,23 +352,27 @@ export function TemplateEditorWrapper({ )} - {/* 미리 정의된 변수들 */} -
-

자주 사용하는 변수 (클릭하여 복사):

-
- {predefinedVariables.map((variable, index) => ( - - ))} + {/* 템플릿별 미리 정의된 변수들 */} + {predefinedVariables.length > 0 && ( +
+

+ {templateName ? `${templateName}에 권장되는 변수` : "자주 사용하는 변수"} (클릭하여 복사): +

+
+ {predefinedVariables.map((variable, index) => ( + + ))} +
-
+ )}
)} diff --git a/lib/basic-contract/vendor-table/basic-contract-columns.tsx b/lib/basic-contract/vendor-table/basic-contract-columns.tsx index b79487d7..c9e8da53 100644 --- a/lib/basic-contract/vendor-table/basic-contract-columns.tsx +++ b/lib/basic-contract/vendor-table/basic-contract-columns.tsx @@ -8,6 +8,7 @@ import { toast } from "sonner" import { getErrorMessage } from "@/lib/handle-error" import { formatDate, formatDateTime } from "@/lib/utils" +import { downloadFile } from "@/lib/file-download" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" @@ -36,28 +37,24 @@ interface GetColumnsProps { /** * 파일 다운로드 함수 */ -/** - * 파일 다운로드 함수 - */ -const handleFileDownload = (filePath: string | null, fileName: string | null) => { +const handleFileDownload = async (filePath: string | null, fileName: string | null) => { if (!filePath || !fileName) { toast.error("파일 정보가 없습니다."); return; } try { - // 전체 URL 생성 - const fullUrl = `${window.location.origin}${filePath}`; + // /api/files/ 엔드포인트를 통한 안전한 다운로드 + const apiFilePath = `/api/files/${filePath.startsWith('/') ? filePath.substring(1) : filePath}`; - // a 태그를 생성하여 다운로드 실행 - const link = document.createElement('a'); - link.href = fullUrl; - link.download = fileName; // 다운로드될 파일명 설정 - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - - toast.success("파일 다운로드를 시작합니다."); + const result = await downloadFile(apiFilePath, fileName, { + action: 'download', + showToast: true + }); + + if (!result.success) { + console.error("파일 다운로드 실패:", result.error); + } } catch (error) { console.error("파일 다운로드 오류:", error); toast.error("파일 다운로드 중 오류가 발생했습니다."); @@ -104,16 +101,19 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef { - const template = row.original; - const filePath = template.status === "PENDING" ? template.filePath : template.signedFilePath + const contract = row.original; + // PENDING 상태일 때는 원본 PDF 파일 (signedFilePath), COMPLETED일 때는 서명된 파일 (signedFilePath) + const filePath = contract.signedFilePath; + const fileName = contract.signedFileName; return (