diff options
| -rw-r--r-- | components/knox/approval/ApprovalSubmit.tsx | 39 | ||||
| -rw-r--r-- | config/menuConfig.ts | 32 | ||||
| -rw-r--r-- | lib/knox-api/approval/approval.ts | 72 | ||||
| -rw-r--r-- | lib/knox-api/common.ts | 4 | ||||
| -rw-r--r-- | lib/users/service.ts | 3 |
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 계정인 경우 // 기타 필요한 필드... } |
