diff options
Diffstat (limited to 'app')
| -rw-r--r-- | app/[lng]/admin/mdg/page.tsx | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/app/[lng]/admin/mdg/page.tsx b/app/[lng]/admin/mdg/page.tsx new file mode 100644 index 00000000..8499f103 --- /dev/null +++ b/app/[lng]/admin/mdg/page.tsx @@ -0,0 +1,256 @@ +'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' +import { sendTestVendorDataToMDG } from '@/lib/soap/mdg/send/vendor-master/action' + +// 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', +}; + +export default function MDGTestPage() { + const [formData, setFormData] = useState<Record<string, string>>({}); + const [fieldDefs, setFieldDefs] = useState<VendorFieldDef[]>([]); + const [isLoading, setIsLoading] = useState(false); + const [lastResult, setLastResult] = useState<string>(''); + + // 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(); + }, []); + + // 폼 데이터 업데이트 + 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); + setLastResult(''); + toast.success('폼이 기본값으로 리셋되었습니다.'); + }; + + // 테스트 송신 실행 (SOAP 라이브러리 사용) + const handleTestSend = async () => { + try { + setIsLoading(true); + setLastResult(''); + + // 필수 필드 검증 + 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; + } + + // fast-xml-parser 기반 송신 함수 호출 + const result = await sendTestVendorDataToMDG(formData); + + if (!result.success) { + toast.error(`송신 실패: ${result.message}`); + setLastResult(`❌ [SAP XI] 송신 실패: ${result.message}`); + setIsLoading(false); + return; + } + + toast.success('MDG 송신이 완료되었습니다.'); + setLastResult(`✅ [SAP XI] 송신 성공: ${result.message} + +🔍 생성된 XML: +${result.generatedXML} + +📄 응답 데이터: +${typeof result.responseData === 'string' ? result.responseData : JSON.stringify(result.responseData, null, 2)}`); + + } catch (error) { + console.error('테스트 송신 실패:', error); + const errorMessage = error instanceof Error ? error.message : '알 수 없는 오류'; + toast.error(`테스트 송신 중 오류가 발생했습니다: ${errorMessage}`); + setLastResult(`❌ [SAP XI] 송신 오류: ${errorMessage}`); + } 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 시스템으로 테스트 송신합니다.<br /> + <strong>fast-xml-parser</strong>를 사용하여 SAP XI 호환 XML을 생성하고 전송합니다. + </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" /> + )} + MDG 테스트 송신 + </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 시스템 송신 결과가 여기에 표시됩니다 (node-soap 또는 fetch 방식) + </CardDescription> + </CardHeader> + <CardContent> + {lastResult ? ( + <pre className="p-4 bg-muted max-h-96 overflow-auto text-xs whitespace-pre-wrap"> + {lastResult} + </pre> + ) : ( + <div className="p-4 bg-muted rounded-lg"> + <p className="text-sm text-muted-foreground"> + 테스트 송신 버튼을 클릭하면 결과가 표시됩니다.<br /> + - <strong>node-soap</strong>: 개선된 타임아웃 설정으로 테스트<br /> + - <strong>fetch</strong>: 기존 정상 동작 방식으로 테스트 + </p> + </div> + )} + </CardContent> + </Card> + </div> + ); +} + |
