"use client"; import React, { useMemo } from "react"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { ColumnInfo } from "@/lib/api/dataflow"; import { getInputTypeForDataType } from "@/utils/connectionUtils"; import { WebTypeInput } from "../condition/WebTypeInput"; interface ColumnMapping { toColumnName: string; fromColumnName?: string; defaultValue?: string; } interface ColumnTableSectionProps { type: "from" | "to"; tableName: string; columns: ColumnInfo[]; selectedColumn: string | null; onColumnClick: (columnName: string) => void; searchTerm: string; onSearchChange: (term: string) => void; dataTypeFilter: string; onDataTypeFilterChange: (filter: string) => void; showMappedOnly: boolean; onShowMappedOnlyChange: (show: boolean) => void; showUnmappedOnly: boolean; onShowUnmappedOnlyChange: (show: boolean) => void; columnMappings: ColumnMapping[]; onDefaultValueChange?: (columnName: string, value: string) => void; onRemoveMapping?: (columnName: string) => void; isColumnClickable: (column: ColumnInfo) => boolean; oppositeSelectedColumn?: string | null; oppositeColumns?: ColumnInfo[]; } export const ColumnTableSection: React.FC = ({ type, tableName, columns, selectedColumn, onColumnClick, searchTerm, onSearchChange, dataTypeFilter, onDataTypeFilterChange, showMappedOnly, onShowMappedOnlyChange, showUnmappedOnly, onShowUnmappedOnlyChange, columnMappings, onDefaultValueChange, onRemoveMapping, isColumnClickable, oppositeSelectedColumn, oppositeColumns, }) => { const isFromTable = type === "from"; // 데이터 타입 목록 추출 const dataTypes = useMemo(() => { const types = new Set(columns.map((col) => col.dataType).filter((type): type is string => !!type)); return Array.from(types).sort(); }, [columns]); // 필터링된 컬럼 목록 const filteredColumns = useMemo(() => { return columns.filter((column) => { // 검색어 필터 const matchesSearch = searchTerm === "" || column.columnName.toLowerCase().includes(searchTerm.toLowerCase()); // 데이터 타입 필터 const matchesDataType = dataTypeFilter === "" || column.dataType === dataTypeFilter; // 매핑 상태 필터 const isMapped = isFromTable ? columnMappings.some((mapping) => mapping.fromColumnName === column.columnName) : (() => { const mapping = columnMappings.find((mapping) => mapping.toColumnName === column.columnName); return !!mapping?.fromColumnName || !!(mapping?.defaultValue && mapping.defaultValue.trim()); })(); const matchesMappingFilter = (!showMappedOnly && !showUnmappedOnly) || (showMappedOnly && isMapped) || (showUnmappedOnly && !isMapped); return matchesSearch && matchesDataType && matchesMappingFilter; }); }, [columns, searchTerm, dataTypeFilter, showMappedOnly, showUnmappedOnly, columnMappings, isFromTable]); const mappedCount = columns.filter((column) => isFromTable ? columnMappings.some((mapping) => mapping.fromColumnName === column.columnName) : (() => { const mapping = columnMappings.find((mapping) => mapping.toColumnName === column.columnName); return !!mapping?.fromColumnName || !!(mapping?.defaultValue && mapping.defaultValue.trim()); })(), ).length; return (
{/* 헤더 */}

{isFromTable ? "From" : "To"}: {tableName} ({columns.length}/{columns.length})

{/* 검색 및 필터 */}
onSearchChange(e.target.value)} className="h-8 text-xs" />
{/* 컬럼 리스트 */}
{filteredColumns.map((column) => { const isSelected = selectedColumn === column.columnName; const isClickable = isColumnClickable(column); if (isFromTable) { // FROM 테이블 렌더링 const isMapped = columnMappings.some((mapping) => mapping.fromColumnName === column.columnName); const mappedToColumn = columnMappings.find( (mapping) => mapping.fromColumnName === column.columnName, )?.toColumnName; // 선택된 TO 컬럼과 타입 호환성 체크 (이미 매핑된 컬럼은 제외) const isTypeCompatible = !oppositeSelectedColumn || isMapped || oppositeColumns?.find((col) => col.columnName === oppositeSelectedColumn)?.dataType === column.dataType; return (
onColumnClick(column.columnName) : undefined} className={`border-b border-gray-200 px-3 py-2 text-xs transition-colors ${ isSelected ? "bg-gray-200 text-gray-800" : isMapped ? "bg-gray-100 text-gray-700" : oppositeSelectedColumn && !isTypeCompatible ? "cursor-not-allowed bg-red-50 text-red-400 opacity-60" : isClickable ? "cursor-pointer hover:bg-gray-50" : "cursor-not-allowed bg-gray-100 text-gray-400" }`} >
{column.displayName && column.displayName !== column.columnName ? column.displayName : column.columnName} {isSelected && } {isMapped && } {oppositeSelectedColumn && !isTypeCompatible && ( )}
{column.dataType} {oppositeSelectedColumn && !isTypeCompatible && ( (호환 불가) )}
{isMapped && mappedToColumn && (
→ {mappedToColumn}
)}
); } else { // TO 테이블 렌더링 const mapping = columnMappings.find((m) => m.toColumnName === column.columnName); const isMapped = !!mapping?.fromColumnName; const hasDefaultValue = !!(mapping?.defaultValue && mapping.defaultValue.trim()); // 선택된 FROM 컬럼과 타입 호환성 체크 (이미 매핑된 컬럼은 제외) const isTypeCompatible = !oppositeSelectedColumn || isMapped || oppositeColumns?.find((col) => col.columnName === oppositeSelectedColumn)?.dataType === column.dataType; return (
{/* 컬럼 정보 행 */}
onColumnClick(column.columnName) : undefined} className={`px-3 py-2 text-xs ${ isClickable && isTypeCompatible ? "cursor-pointer hover:bg-gray-50" : oppositeSelectedColumn && !isTypeCompatible ? "cursor-not-allowed" : hasDefaultValue ? "cursor-not-allowed" : "" }`} >
{column.displayName && column.displayName !== column.columnName ? column.displayName : column.columnName} {isSelected && } {oppositeSelectedColumn && !isTypeCompatible && ( )}
{column.dataType} {oppositeSelectedColumn && !isTypeCompatible && ( (호환 불가) )}
{isMapped && (
← {mapping.fromColumnName}
)} {!isMapped && onDefaultValueChange && (
onDefaultValueChange(column.columnName, value)} className="h-6 border-gray-200 text-xs focus:border-green-400 focus:ring-0" placeholder="기본값 입력..." tableName={tableName} />
)}
); } })}
{/* 하단 통계 */}
{isFromTable ? "매핑됨" : "설정됨"}: {mappedCount}/{columns.length} 표시: {filteredColumns.length}
); };