summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-07-29 11:47:34 +0000
committerjoonhoekim <26rote@gmail.com>2025-07-29 11:47:34 +0000
commit792fb0c21136eededecf52b5b4aa1a252bdc4bfb (patch)
treeb372e3be7d5b09a8c533ae1f3766b3c49d36f106
parent8ff20b58f595cc15319387b9641340fd3e8725d1 (diff)
(김준회) 파트너 Engineering 메뉴 수정, Knox 결재 로깅 추가
-rw-r--r--components/knox/approval/ApprovalSubmit.tsx39
-rw-r--r--config/menuConfig.ts32
-rw-r--r--lib/knox-api/approval/approval.ts72
-rw-r--r--lib/knox-api/common.ts4
-rw-r--r--lib/users/service.ts3
5 files changed, 113 insertions, 37 deletions
diff --git a/components/knox/approval/ApprovalSubmit.tsx b/components/knox/approval/ApprovalSubmit.tsx
index f3c1fa3d..8b58aba6 100644
--- a/components/knox/approval/ApprovalSubmit.tsx
+++ b/components/knox/approval/ApprovalSubmit.tsx
@@ -17,6 +17,7 @@ import { Checkbox } from '@/components/ui/checkbox';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { toast } from 'sonner';
import { Loader2, Trash2, FileText, AlertCircle, GripVertical } from 'lucide-react';
+import { debugLog, debugError } from '@/lib/debug-utils'
// dnd-kit imports for drag and drop
import {
@@ -760,12 +761,28 @@ export default function ApprovalSubmit({ onSubmitSuccess }: ApprovalSubmitProps)
setSubmitResult(null);
try {
+ // 세션 정보 확인
+ if (!session?.user) {
+ toast.error('로그인이 필요합니다.');
+ return;
+ }
+
+ const currentEmail = session.user.email ?? '';
+ const currentEpId = (session.user as { epId?: string }).epId;
+ const currentUserId = session.user.id ?? '';
+
+ debugLog('Current User', session.user);
+
+ if (!currentEpId) {
+ toast.error('사용자 정보가 올바르지 않습니다.');
+ return;
+ }
+
// 결재 경로 생성 (ID 제거하고 API 호출)
const approvalLines: ApprovalLine[] = await Promise.all(
data.aplns.map((apln) =>
createApprovalLine(
- // userId: apln.userId 는 불필요하므로 제거
- { epId: apln.epId, emailAddress: apln.emailAddress },
+ { epId: apln.epId },
apln.role,
apln.seq,
{ opinion: apln.opinion }
@@ -773,6 +790,8 @@ export default function ApprovalSubmit({ onSubmitSuccess }: ApprovalSubmitProps)
)
);
+ debugLog('Approval Lines', approvalLines);
+
// 상신 요청 생성
const attachmentsArray = data.attachments ? Array.from(data.attachments as FileList) : undefined;
@@ -781,7 +800,7 @@ export default function ApprovalSubmit({ onSubmitSuccess }: ApprovalSubmitProps)
data.subject,
approvalLines,
{
- contentsType: 'HTML',
+ contentsType: data.contentsType,
docSecuType: data.docSecuType,
urgYn: data.urgYn ? 'Y' : 'N',
importantYn: data.importantYn ? 'Y' : 'N',
@@ -796,11 +815,17 @@ export default function ApprovalSubmit({ onSubmitSuccess }: ApprovalSubmitProps)
// API 호출 (보안 등급에 따라 분기)
const isSecure = data.docSecuType === 'CONFIDENTIAL' || data.docSecuType === 'CONFIDENTIAL_STRICT';
- console.log(submitRequest);
+ debugLog('Submit Request', submitRequest);
const response = isSecure
? await submitSecurityApproval(submitRequest)
- : await submitApproval(submitRequest);
+ : await submitApproval(submitRequest, {
+ userId: currentUserId,
+ epId: currentEpId,
+ emailAddress: currentEmail
+ });
+
+ debugLog('Submit Response', response);
if (response.result === 'SUCCESS') {
setSubmitResult({ apInfId: response.data.apInfId });
@@ -808,10 +833,10 @@ export default function ApprovalSubmit({ onSubmitSuccess }: ApprovalSubmitProps)
onSubmitSuccess?.(response.data.apInfId);
form.reset();
} else {
- toast.error('결재 상신에 실패했습니다.');
+ toast.error(`결재 상신에 실패했습니다: ${response.result}`);
}
} catch (error) {
- console.error('결재 상신 오류:', error);
+ debugError('결재 상신 오류', error);
toast.error('결재 상신 중 오류가 발생했습니다.');
} finally {
setIsSubmitting(false);
diff --git a/config/menuConfig.ts b/config/menuConfig.ts
index bebfcb01..59d75d71 100644
--- a/config/menuConfig.ts
+++ b/config/menuConfig.ts
@@ -1019,8 +1019,20 @@ export const mainNavVendor: MenuSection[] = [
title: "데이터 입력",
href: `/partners/vendor-data`,
description: "기준 정보에 입각한 협력업체 데이터 입력",
- group: "벤더 데이터"
+ group: "조선",
+ },
+ {
+ title: "문서/도서 리스트 및 제출(조선)",
+ href: `/partners/document-list-ship`,
+ description: "벤더의 제출 도서/문서의 리스트를 관리하고 문서를 제출",
+ group: "조선",
+ },
+ {
+ title: "데이터 입력",
+ href: `/partners/vendor-data`,
+ description: "기준 정보에 입각한 협력업체 데이터 입력",
+ group: "해양",
},
// {
// title: "데이터 리스트",
@@ -1028,28 +1040,22 @@ export const mainNavVendor: MenuSection[] = [
// description: "입력된 협력업체 데이터를 도서/문서와 연계하여 리스트하여 출력",
// },
{
- title: "문서/도서 리스트 및 제출 (조선)",
- href: `/partners/document-list-ship`,
- description: "벤더의 제출 도서/문서의 리스트를 관리하고 문서를 제출",
- group: "벤더 문서"
- },
- {
- title: "문서/도서 리스트 및 제출 (해양)",
+ title: "문서/도서 리스트 및 제출(해양)",
href: `/partners/document-list`,
description: "벤더의 제출 도서/문서의 리스트를 관리하고 문서를 제출",
- group: "벤더 문서"
+ group: "해양",
},
{
- title: "문서/도서 리스트 관리 (해양)",
+ title: "문서/도서 리스트 관리",
href: `/partners/document-list-only`,
description: "벤더의 제출 도서/문서의 리스트를 관리",
- group: "벤더 문서"
+ group: "해양",
},
{
- title: "문서/도서 제출 (해양)",
+ title: "문서/도서 제출",
href: `/partners/document-upload`,
description: "벤더의 도서/문서를 제출",
- group: "벤더 문서"
+ group: "해양",
},
// {
// title: "문서/도서 업로드",
diff --git a/lib/knox-api/approval/approval.ts b/lib/knox-api/approval/approval.ts
index 5e62382d..080867cd 100644
--- a/lib/knox-api/approval/approval.ts
+++ b/lib/knox-api/approval/approval.ts
@@ -3,6 +3,7 @@
import { getKnoxConfig, createJsonHeaders, createFormHeaders } from '../common';
import { randomUUID } from 'crypto';
import { saveApprovalToDatabase, deleteApprovalFromDatabase } from './service';
+import { debugLog, debugError } from '@/lib/debug-utils'
// Knox API Approval 서버 액션들
// 가이드: lib/knox-api/approval/guide.html
@@ -174,10 +175,28 @@ export async function submitApproval(
});
if (!response.ok) {
- throw new Error(`결재 상신 실패: ${response.status}`);
+ const errorText = await response.text();
+ debugError('API Error Response', errorText);
+ throw new Error(`결재 상신 실패: ${response.status} - ${errorText}`);
}
- const result = await response.json();
+ // 응답의 Content-Type 확인 및 charset 처리
+ const contentType = response.headers.get('content-type');
+ let result;
+
+ if (contentType && contentType.includes('application/json')) {
+ result = await response.json();
+ } else {
+ // JSON이 아닌 경우 텍스트로 읽고 JSON 파싱 시도
+ const text = await response.text();
+ debugLog('Raw response text', text);
+ try {
+ result = JSON.parse(text);
+ } catch (parseError) {
+ console.error('JSON parse error:', parseError);
+ throw new Error(`응답 파싱 실패: ${text}`);
+ }
+ }
// Knox API 성공 시 데이터베이스에 저장
if (result.result === 'SUCCESS') {
@@ -200,7 +219,7 @@ export async function submitApproval(
return result;
} catch (error) {
- console.error('결재 상신 오류:', error);
+ debugError('결재 상신 오류', error);
throw error;
}
}
@@ -249,12 +268,32 @@ export async function submitSecurityApproval(
});
if (!response.ok) {
- throw new Error(`보안 결재 상신 실패: ${response.status}`);
+ const errorText = await response.text();
+ debugError('Security API Error Response', errorText);
+ throw new Error(`보안 결재 상신 실패: ${response.status} - ${errorText}`);
}
- return await response.json();
+ // 응답의 Content-Type 확인 및 charset 처리
+ const contentType = response.headers.get('content-type');
+ let result;
+
+ if (contentType && contentType.includes('application/json')) {
+ result = await response.json();
+ } else {
+ // JSON이 아닌 경우 텍스트로 읽고 JSON 파싱 시도
+ const text = await response.text();
+ debugLog('Raw security response text', text);
+ try {
+ result = JSON.parse(text);
+ } catch (parseError) {
+ console.error('Security JSON parse error:', parseError);
+ throw new Error(`보안 응답 파싱 실패: ${text}`);
+ }
+ }
+
+ return result;
} catch (error) {
- console.error('보안 결재 상신 오류:', error);
+ debugError('보안 결재 상신 오류', error);
throw error;
}
}
@@ -562,22 +601,25 @@ export async function createSubmitApprovalRequest(
// EVCP 접두어 뒤에 28자리 무작위 문자열을 붙여 32byte 고유 ID 생성
const apInfId = `EVCP${randomUUID().replace(/-/g, '').slice(0, 28)}`;
- return {
+ const result = {
contents,
subject,
aplns: approvalLines,
- contentsType: 'TEXT',
- docSecuType: 'PERSONAL',
- notifyOption: '0',
- urgYn: 'N',
+ contentsType: options.contentsType || 'TEXT',
+ docSecuType: options.docSecuType || 'PERSONAL',
+ notifyOption: options.notifyOption || '0',
+ urgYn: options.urgYn || 'N',
sbmDt,
- timeZone: 'GMT+9',
- docMngSaveCode: '0',
- sbmLang: 'ko',
+ timeZone: options.timeZone || 'GMT+9',
+ docMngSaveCode: options.docMngSaveCode || '0',
+ sbmLang: options.sbmLang || 'ko',
apInfId,
- importantYn: 'N',
+ importantYn: options.importantYn || 'N',
...options
};
+
+ debugLog('Created Submit Request', result);
+ return result;
}
/**
diff --git a/lib/knox-api/common.ts b/lib/knox-api/common.ts
index db6910f2..5b13fc0a 100644
--- a/lib/knox-api/common.ts
+++ b/lib/knox-api/common.ts
@@ -31,7 +31,7 @@ export const createHeaders = async (contentType: string = 'application/json'): P
// JSON 전용 헤더
export const createJsonHeaders = async (): Promise<Record<string, string>> => {
- return await createHeaders('application/json');
+ return await createHeaders('application/json; charset=utf-8');
};
// FormData 전용 헤더 (Content-Type 자동 설정)
@@ -40,5 +40,7 @@ export const createFormHeaders = async (): Promise<Record<string, string>> => {
return {
'System-ID': config.systemId,
Authorization: `Bearer ${config.bearerToken}`,
+ 'Accept': 'application/json; charset=utf-8',
+ 'Accept-Charset': 'utf-8',
};
}; \ No newline at end of file
diff --git a/lib/users/service.ts b/lib/users/service.ts
index 90ee170e..7019578f 100644
--- a/lib/users/service.ts
+++ b/lib/users/service.ts
@@ -222,7 +222,8 @@ export async function findEmailTemp(email: string) {
imageUrl:userRecord.imageUrl,
companyId:userRecord.companyId,
techCompanyId:userRecord.techCompanyId,
- domain:userRecord.domain
+ domain:userRecord.domain,
+ epId:userRecord.epId, // Knox 계정인 경우
// 기타 필요한 필드...
}