ERP-node/frontend/components/dataflow/connection/redesigned/RightPanel/FieldMappingStep.tsx

200 lines
6.6 KiB
TypeScript
Raw Normal View History

"use client";
import React, { useState, useEffect } from "react";
import { CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { ArrowLeft, Link, Loader2, CheckCircle } from "lucide-react";
import { toast } from "sonner";
// API import
import { getColumnsFromConnection } from "@/lib/api/multiConnection";
// 타입 import
import { Connection, TableInfo, ColumnInfo } from "@/lib/types/multiConnection";
import { FieldMapping } from "../types/redesigned";
// 컴포넌트 import
import FieldMappingCanvas from "./VisualMapping/FieldMappingCanvas";
interface FieldMappingStepProps {
fromTable?: TableInfo;
toTable?: TableInfo;
fromConnection?: Connection;
toConnection?: Connection;
fieldMappings: FieldMapping[];
onCreateMapping: (fromField: ColumnInfo, toField: ColumnInfo) => void;
onDeleteMapping: (mappingId: string) => void;
onNext: () => void;
onBack: () => void;
}
/**
* 🎯 3단계: 시각적
* - SVG
* - ()
* -
*/
const FieldMappingStep: React.FC<FieldMappingStepProps> = ({
fromTable,
toTable,
fromConnection,
toConnection,
fieldMappings,
onCreateMapping,
onDeleteMapping,
onNext,
onBack,
}) => {
const [fromColumns, setFromColumns] = useState<ColumnInfo[]>([]);
const [toColumns, setToColumns] = useState<ColumnInfo[]>([]);
const [isLoading, setIsLoading] = useState(false);
// 컬럼 정보 로드
useEffect(() => {
const loadColumns = async () => {
console.log("🔍 컬럼 로딩 시작:", {
fromConnection: fromConnection?.id,
toConnection: toConnection?.id,
fromTable: fromTable?.tableName,
toTable: toTable?.tableName,
});
if (!fromConnection || !toConnection || !fromTable || !toTable) {
console.warn("⚠️ 필수 정보 누락:", {
fromConnection: !!fromConnection,
toConnection: !!toConnection,
fromTable: !!fromTable,
toTable: !!toTable,
});
return;
}
try {
setIsLoading(true);
console.log("📡 API 호출 시작:", {
fromAPI: `getColumnsFromConnection(${fromConnection.id}, "${fromTable.tableName}")`,
toAPI: `getColumnsFromConnection(${toConnection.id}, "${toTable.tableName}")`,
});
const [fromCols, toCols] = await Promise.all([
getColumnsFromConnection(fromConnection.id, fromTable.tableName),
getColumnsFromConnection(toConnection.id, toTable.tableName),
]);
console.log("🔍 원본 API 응답 확인:", {
fromCols: fromCols,
toCols: toCols,
fromType: typeof fromCols,
toType: typeof toCols,
fromIsArray: Array.isArray(fromCols),
toIsArray: Array.isArray(toCols),
});
// 안전한 배열 처리
const safeFromCols = Array.isArray(fromCols) ? fromCols : [];
const safeToCols = Array.isArray(toCols) ? toCols : [];
console.log("✅ 컬럼 로딩 성공:", {
fromColumns: safeFromCols.length,
toColumns: safeToCols.length,
fromData: safeFromCols.slice(0, 2), // 처음 2개만 로깅
toData: safeToCols.slice(0, 2),
originalFromType: typeof fromCols,
originalToType: typeof toCols,
});
setFromColumns(safeFromCols);
setToColumns(safeToCols);
} catch (error) {
console.error("❌ 컬럼 정보 로드 실패:", error);
toast.error("필드 정보를 불러오는데 실패했습니다.");
} finally {
setIsLoading(false);
}
};
loadColumns();
}, [fromConnection, toConnection, fromTable, toTable]);
if (isLoading) {
return (
<CardContent className="flex items-center justify-center py-12">
<Loader2 className="mr-2 h-6 w-6 animate-spin" />
<span> ...</span>
</CardContent>
);
}
return (
<>
<CardHeader className="pb-2">
<CardTitle className="flex items-center gap-2">
<Link className="h-5 w-5" />
3단계: 컬럼
</CardTitle>
</CardHeader>
<CardContent className="flex h-full flex-col p-0">
{/* 매핑 캔버스 - 전체 영역 사용 */}
<div className="min-h-0 flex-1 p-4">
{isLoading ? (
<div className="flex h-full items-center justify-center">
<div className="text-muted-foreground"> ...</div>
</div>
) : fromColumns.length > 0 && toColumns.length > 0 ? (
<FieldMappingCanvas
fromFields={fromColumns}
toFields={toColumns}
mappings={fieldMappings}
onCreateMapping={onCreateMapping}
onDeleteMapping={onDeleteMapping}
/>
) : (
<div className="flex h-full flex-col items-center justify-center space-y-3">
<div className="text-muted-foreground"> .</div>
<div className="text-muted-foreground text-xs">
FROM : {fromColumns.length}, TO : {toColumns.length}
</div>
<Button
variant="outline"
size="sm"
onClick={() => {
console.log("🔄 수동 재로딩 시도");
setFromColumns([]);
setToColumns([]);
// useEffect가 재실행되도록 강제 업데이트
setIsLoading(true);
setTimeout(() => setIsLoading(false), 100);
}}
>
</Button>
</div>
)}
</div>
{/* 하단 네비게이션 - 고정 */}
<div className="flex-shrink-0 border-t bg-white p-4">
<div className="flex items-center justify-between">
<Button variant="outline" onClick={onBack} className="flex items-center gap-2">
<ArrowLeft className="h-4 w-4" />
</Button>
<div className="text-muted-foreground text-sm">
{fieldMappings.length > 0 ? `${fieldMappings.length}개 매핑 완료` : "컬럼을 선택해서 매핑하세요"}
</div>
<Button onClick={onNext} disabled={fieldMappings.length === 0} className="flex items-center gap-2">
<CheckCircle className="h-4 w-4" />
</Button>
</div>
</div>
</CardContent>
</>
);
};
export default FieldMappingStep;