diff options
| author | joonhoekim <26rote@gmail.com> | 2025-07-28 12:10:39 +0000 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-07-28 12:10:39 +0000 |
| commit | 75249e6fa46864f49d4eb91bd755171b6b65eaae (patch) | |
| tree | f2c021f0fe10b3513d29f05ca15b82e460d79d20 /app | |
| parent | c228a89c2834ee63b209bad608837c39643f350e (diff) | |
(김준회) 공통모듈 - Knox 결재 모듈 구현, 유저 선택기 구현, 상신 결재 저장을 위한 DB 스키마 및 서비스 추가, spreadjs 라이센스 환경변수 통일, 유저 테이블에 epId 컬럼 추가
Diffstat (limited to 'app')
| -rw-r--r-- | app/[lng]/admin/approval-test/page.tsx | 10 | ||||
| -rw-r--r-- | app/[lng]/admin/approval-test/page.tsx.bak | 32 | ||||
| -rw-r--r-- | app/[lng]/admin/mdg/page.tsx.bak | 277 | ||||
| -rw-r--r-- | app/api/auth/[...nextauth]/route.ts | 1 |
4 files changed, 4 insertions, 316 deletions
diff --git a/app/[lng]/admin/approval-test/page.tsx b/app/[lng]/admin/approval-test/page.tsx index f044d87d..ab5654f3 100644 --- a/app/[lng]/admin/approval-test/page.tsx +++ b/app/[lng]/admin/approval-test/page.tsx @@ -2,8 +2,8 @@ import { Metadata } from 'next'; import ApprovalManager from '@/components/knox/approval/ApprovalManager'; export const metadata: Metadata = { - title: 'Knox 결재 시스템 테스트 | Admin', - description: 'Knox API를 사용한 결재 시스템 기능 테스트용', + title: 'Knox 결재 시스템 | Admin', + description: 'Knox API를 사용한 결재 시스템', }; export default function ApprovalTestPage() { @@ -12,18 +12,14 @@ export default function ApprovalTestPage() { <div className="space-y-6"> {/* 페이지 헤더 */} <div className="space-y-2"> - <h1 className="text-3xl font-bold tracking-tight">Knox 결재 시스템 테스트</h1> + <h1 className="text-3xl font-bold tracking-tight">Knox 결재 시스템</h1> <p className="text-muted-foreground"> Knox API를 사용한 결재 시스템 컴포넌트입니다. - <br /> - 테스트 모드가 기본적으로 활성화되어 있으며, 테스트 모드에서는 실제 API 대신 모킹 데이터를 사용 </p> </div> {/* 결재 관리자 컴포넌트 */} <ApprovalManager - useFakeData={true} - systemId="EVCP_TEST_SYSTEM" defaultTab="submit" /> </div> diff --git a/app/[lng]/admin/approval-test/page.tsx.bak b/app/[lng]/admin/approval-test/page.tsx.bak deleted file mode 100644 index de0e2b5a..00000000 --- a/app/[lng]/admin/approval-test/page.tsx.bak +++ /dev/null @@ -1,32 +0,0 @@ -import { Metadata } from 'next'; -import ApprovalManager from '@/components/knox/approval/ApprovalManager'; - -export const metadata: Metadata = { - title: 'Knox 결재 시스템 테스트 | Admin', - description: 'Knox API를 사용한 결재 시스템의 모든 기능을 테스트할 수 있는 페이지입니다.', -}; - -export default function ApprovalTestPage() { - return ( - <div className="container mx-auto py-8"> - <div className="space-y-6"> - {/* 페이지 헤더 */} - <div className="space-y-2"> - <h1 className="text-3xl font-bold tracking-tight">Knox 결재 시스템 테스트</h1> - <p className="text-muted-foreground"> - Knox API를 사용한 결재 시스템의 모든 기능을 테스트할 수 있습니다. - <br /> - 테스트 모드가 기본적으로 활성화되어 있으며, 실제 API 대신 가짜 데이터를 사용합니다. - </p> - </div> - - {/* 결재 관리자 컴포넌트 */} - <ApprovalManager - useFakeData={true} - systemId="EVCP_TEST_SYSTEM" - defaultTab="submit" - /> - </div> - </div> - ); -} diff --git a/app/[lng]/admin/mdg/page.tsx.bak b/app/[lng]/admin/mdg/page.tsx.bak deleted file mode 100644 index e2926deb..00000000 --- a/app/[lng]/admin/mdg/page.tsx.bak +++ /dev/null @@ -1,277 +0,0 @@ -'use client' - -import { useState, useEffect } from 'react' -import { Button } from '@/components/ui/button' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { Badge } from '@/components/ui/badge' -import { toast } from 'sonner' -import { Loader2, Send, RefreshCw } from 'lucide-react' - -// CSV 필드를 정의할 타입 -interface VendorFieldDef { - table: string; - name: string; - mandatory: boolean; - description: string; -} - -// CSV 파싱 함수 (간단 파서) -const parseCSV = (csv: string): VendorFieldDef[] => { - const lines = csv.trim().split('\n'); - // 첫 번째 라인은 헤더이므로 제거 - return lines.slice(1).map((line) => { - const parts = line.split(','); - const table = parts[1]?.trim(); - const name = parts[2]?.trim(); - const mandatory = parts[3]?.trim() === 'M'; - const description = parts.slice(6).join(',').trim(); - return { table, name, mandatory, description } as VendorFieldDef; - }); -}; - -// 기존 샘플 기본값 (필요 시 확장) -const sampleDefaults: Record<string, string> = { - BP_HEADER: 'TEST001', - ZZSRMCD: 'EVCP', - TITLE: 'TEST', - BU_SORT1: 'TEST VENDOR', - NAME_ORG1: '테스트 벤더 회사', - KTOKK: 'Z001', - VEN_KFBUS: '제조업', - VEN_KFIND: 'IT', - MASTERFLAG: 'X', - IBND_TYPE: 'U', - ZZREQID: 'TESTUSER01', - ADDRNO: '0001', - AD_NATION: '1', - COUNTRY: 'KR', - LANGU_COM: 'K', - POST_COD1: '06292', - CITY1: '서울시', - DISTRICT: '강남구', - REGION: '11', - MC_STREET: '테헤란로 123', - T_COUNTRY: 'KR', - T_NUMBER: '02-1234-5678', - F_COUNTRY: 'KR', - F_NUMBER: '02-1234-5679', - U_ADDRESS: 'https://test.vendor.com', - E_ADDRESS: 'contact@test.vendor.com', - BP_TX_TYP: 'KR2', - TAXNUM: '123-45-67890', - AD_CONSNO: '1', -}; - -// XML escape helper -const escapeXml = (unsafe: string) => unsafe.replace(/[<>&'"']/g, (c) => { - switch (c) { - case '<': return '<'; - case '>': return '>'; - case '&': return '&'; - case '"': return '"'; - case "'": return '''; - default: return c; - } -}); - -export default function MDGTestPage() { - const [formData, setFormData] = useState<Record<string, string>>({}); - const [fieldDefs, setFieldDefs] = useState<VendorFieldDef[]>([]); - const [resultXml, setResultXml] = useState<string>(''); - const [isLoading, setIsLoading] = useState(false); - - // CSV 로딩 및 초기 데이터 셋업 - useEffect(() => { - const load = async () => { - const res = await fetch('/wsdl/P2MD3007_AO.csv'); - const csvText = await res.text(); - const defs = parseCSV(csvText); - setFieldDefs(defs); - - const init: Record<string, string> = {}; - defs.forEach((d) => { - init[d.name] = sampleDefaults[d.name] ?? ''; - }); - setFormData(init); - }; - - load(); - }, []); - - // XML 생성 유틸리티 (폼 데이터 -> SOAP Envelope) - const buildEnvelopeXml = (currentForm: Record<string, string>, defs: VendorFieldDef[]) => { - if (defs.length === 0) return ''; - const bodyContent = defs.map((f) => { - const val = currentForm[f.name] ?? ''; - return `<${f.name}>${escapeXml(val)}</${f.name}>`; - }).join('\n '); - - const supplierXml = `<SUPPLIER_MASTER>\n ${bodyContent}\n </SUPPLIER_MASTER>`; - - return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:p1=\"http://shi.samsung.co.kr/P2_MD/MDZ\">\n <soap:Header/>\n <soap:Body>\n <p1:MT_P2MD3007_S>\n <P2MD3007_S>\n ${supplierXml}\n </P2MD3007_S>\n </p1:MT_P2MD3007_S>\n </soap:Body>\n</soap:Envelope>`; - }; - - // 폼 데이터 변경 시 실시간 XML 생성 - useEffect(() => { - const xml = buildEnvelopeXml(formData, fieldDefs); - setResultXml(xml); - }, [formData, fieldDefs]); - - // 폼 데이터 업데이트 - const updateField = (field: string, value: string) => { - setFormData(prev => ({ ...prev, [field]: value })); - }; - - // 기본값으로 리셋 - const resetForm = () => { - const reset: Record<string, string> = {}; - fieldDefs.forEach((d) => { - reset[d.name] = sampleDefaults[d.name] ?? ''; - }); - setFormData(reset); - toast.success('폼이 기본값으로 리셋되었습니다.'); - }; - - // 테스트 송신 실행 (실제 서버 호출) - const handleTestSend = async () => { - try { - setIsLoading(true); - - // 필수 필드 검증 - const requiredFields = fieldDefs.filter(d => d.mandatory).map(d => d.name); - const missingFields = requiredFields.filter(field => !formData[field]?.trim()); - - if (missingFields.length > 0) { - toast.error(`필수 필드가 누락되었습니다: ${missingFields.join(', ')}`); - setIsLoading(false); - return; - } - - // 서버 API 호출해 송신 - const res = await fetch('/api/mdg/send-vendor-xml', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ envelope: resultXml }), - }); - - const json = await res.json(); - - if (!res.ok || !json.success) { - // 상세 오류 메시지 추출 (vendorCode 기반 또는 직접 오류 메시지) - const detailMsg = json?.results?.[0]?.error ?? json?.message ?? json?.responseText ?? '송신 실패'; - toast.error(`송신 실패: ${detailMsg}`); - setIsLoading(false); - return; - } - - toast.success('MDG 송신이 완료되었습니다.'); - - } catch (error) { - console.error('테스트 송신 실패:', error); - toast.error('테스트 송신 중 오류가 발생했습니다.'); - } finally { - setIsLoading(false); - } - }; - - return ( - <div className="container mx-auto p-6 space-y-6"> - <div className="flex items-center justify-between"> - <div> - <h1 className="text-3xl font-bold">MDG VENDOR 마스터 테스트</h1> - <p className="text-muted-foreground mt-2"> - VENDOR 마스터 데이터를 MDG 시스템으로 테스트 송신합니다 - </p> - </div> - <div className="flex gap-2"> - <Button variant="outline" onClick={resetForm}> - <RefreshCw className="w-4 h-4 mr-2" /> - 리셋 - </Button> - <Button onClick={handleTestSend} disabled={isLoading}> - {isLoading ? ( - <Loader2 className="w-4 h-4 mr-2 animate-spin" /> - ) : ( - <Send className="w-4 h-4 mr-2" /> - )} - 테스트 송신 - </Button> - </div> - </div> - - {/* 동적 필드 렌더링 */} - {fieldDefs.length === 0 ? ( - <p className="text-center text-muted-foreground">CSV 로딩 중...</p> - ) : ( - <div className="space-y-6"> - {Object.entries( - fieldDefs.reduce((acc: Record<string, VendorFieldDef[]>, cur) => { - acc[cur.table] = acc[cur.table] ? [...acc[cur.table], cur] : [cur]; - return acc; - }, {}) - ).map(([table, fields]) => ( - <Card key={table}> - <CardHeader> - <CardTitle className="flex items-center gap-2"> - {table} - {fields.some(f => f.mandatory) && ( - <Badge variant="destructive">필수 포함</Badge> - )} - </CardTitle> - <CardDescription>{table} 테이블 입력</CardDescription> - </CardHeader> - <CardContent className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> - {fields.filter((f, idx, arr) => arr.findIndex(x => x.name === f.name) === idx).map((field) => ( - <div key={field.name}> - <Label htmlFor={field.name} className="flex items-center gap-1"> - {field.name} - {field.mandatory && ( - <Badge variant="destructive" className="ml-1">필수</Badge> - )} - </Label> - <Input - id={field.name} - value={formData[field.name] ?? ''} - onChange={(e) => updateField(field.name, e.target.value)} - /> - {field.description && ( - <p className="text-xs text-muted-foreground mt-1">{field.description}</p> - )} - </div> - ))} - </CardContent> - </Card> - ))} - </div> - )} - - {/* 송신 결과 영역 */} - <Card> - <CardHeader> - <CardTitle>송신 결과</CardTitle> - <CardDescription> - MDG 시스템으로의 송신 결과가 여기에 표시됩니다 - </CardDescription> - </CardHeader> - <CardContent> - {resultXml ? ( - <pre className="p-4 bg-muted max-h-96 overflow-auto text-xs whitespace-pre-wrap"> - {resultXml} - </pre> - ) : ( - <div className="p-4 bg-muted rounded-lg"> - <p className="text-sm text-muted-foreground"> - 테스트 송신 버튼을 클릭하면 결과가 표시됩니다. - </p> - </div> - )} - </CardContent> - </Card> - </div> - ); -} - diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts index fe93906d..5896fb90 100644 --- a/app/api/auth/[...nextauth]/route.ts +++ b/app/api/auth/[...nextauth]/route.ts @@ -235,6 +235,7 @@ export const authOptions: NextAuthOptions = { authMethod: tempAuth.authMethod as AuthMethod, dbSessionId: dbSession.id, roles: userRoles, // ✅ roles 배열 추가 + epId: user.epId, // Knox 계정인 경우, epId 추가 (Knox API 사용하는 경우 필요) } } catch (error) { |
