summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/[lng]/admin/mdg/page.tsx256
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>
+ );
+}
+