feat: 자동 채우기 테이블 선택 드롭다운 및 동적 컬럼 로드 추가

- 추가 입력 필드에서 자동 채우기 테이블을 드롭다운으로 선택 가능
- 텍스트 입력 대신 allTables에서 선택하는 방식으로 개선
- 테이블 선택 시 해당 테이블의 컬럼을 자동으로 로드
- autoFillTableColumns 상태로 필드별 테이블 컬럼 관리
- 선택한 테이블에 따라 컬럼 드롭다운이 동적으로 변경됨

사용자 경험 개선:
- 테이블명을 직접 입력하는 대신 목록에서 선택
- 선택한 테이블의 컬럼만 표시되어 혼란 방지
- 원본 테이블(기본) 또는 다른 테이블 선택 가능
This commit is contained in:
kjs 2025-11-20 17:44:33 +09:00
parent 6e92d1855a
commit 86eb9f0425
1 changed files with 134 additions and 31 deletions

View File

@ -73,6 +73,9 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
// 🆕 부모 데이터 매핑: 각 매핑별 소스 테이블 컬럼 상태
const [mappingSourceColumns, setMappingSourceColumns] = useState<Record<number, Array<{ columnName: string; columnLabel?: string; dataType?: string }>>>({});
// 🆕 추가 입력 필드별 자동 채우기 테이블 컬럼 상태
const [autoFillTableColumns, setAutoFillTableColumns] = useState<Record<number, Array<{ columnName: string; columnLabel?: string; dataType?: string }>>>({});
// 🆕 원본/대상 테이블 컬럼 상태 (내부에서 로드)
const [loadedSourceTableColumns, setLoadedSourceTableColumns] = useState<Array<{ columnName: string; columnLabel?: string; dataType?: string }>>([]);
const [loadedTargetTableColumns, setLoadedTargetTableColumns] = useState<Array<{ columnName: string; columnLabel?: string; dataType?: string }>>([]);
@ -137,6 +140,38 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
loadColumns();
}, [config.targetTable]);
// 🆕 자동 채우기 테이블 선택 시 컬럼 로드
const loadAutoFillTableColumns = async (tableName: string, fieldIndex: number) => {
if (!tableName) {
setAutoFillTableColumns(prev => ({ ...prev, [fieldIndex]: [] }));
return;
}
try {
console.log(`🔍 [필드 ${fieldIndex}] 자동 채우기 테이블 컬럼 로드:`, tableName);
const { tableManagementApi } = await import("@/lib/api/tableManagement");
const response = await tableManagementApi.getColumnList(tableName);
if (response.success && response.data) {
const columns = response.data.columns || [];
setAutoFillTableColumns(prev => ({
...prev,
[fieldIndex]: columns.map((col: any) => ({
columnName: col.columnName,
columnLabel: col.displayName || col.columnLabel || col.columnName,
dataType: col.dataType,
}))
}));
console.log(`✅ [필드 ${fieldIndex}] 컬럼 로드 성공:`, columns.length);
} else {
console.error(`❌ [필드 ${fieldIndex}] 컬럼 로드 실패:`, response);
}
} catch (error) {
console.error(`❌ [필드 ${fieldIndex}] 컬럼 로드 오류:`, error);
}
};
// 🆕 소스 테이블 선택 시 컬럼 로드
const loadMappingSourceColumns = async (tableName: string, mappingIndex: number) => {
try {
@ -745,15 +780,66 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
<div className="space-y-2">
<Label className="text-[10px] sm:text-xs"> ()</Label>
{/* 테이블명 입력 */}
<Input
value={field.autoFillFromTable || ""}
onChange={(e) => updateField(index, { autoFillFromTable: e.target.value })}
placeholder="비워두면 주 데이터 (예: item_price)"
className="h-6 w-full text-[10px] sm:h-7 sm:text-xs"
/>
{/* 테이블 선택 드롭다운 */}
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
className="h-6 w-full justify-between text-[10px] sm:h-7 sm:text-xs"
>
{field.autoFillFromTable
? allTables.find(t => t.tableName === field.autoFillFromTable)?.displayName || field.autoFillFromTable
: "원본 테이블 (기본)"}
<ChevronsUpDown className="ml-1 h-2 w-2 shrink-0 opacity-50 sm:h-3 sm:w-3" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[250px] p-0 sm:w-[300px]">
<Command>
<CommandInput placeholder="테이블 검색..." className="h-6 text-[10px] sm:h-7 sm:text-xs" />
<CommandEmpty className="text-[10px] sm:text-xs"> .</CommandEmpty>
<CommandGroup className="max-h-[200px] overflow-auto">
<CommandItem
value=""
onSelect={() => {
updateField(index, { autoFillFromTable: undefined, autoFillFrom: undefined });
setAutoFillTableColumns(prev => ({ ...prev, [index]: [] }));
}}
className="text-[10px] sm:text-xs"
>
<Check
className={cn(
"mr-1 h-2 w-2 sm:mr-2 sm:h-3 sm:w-3",
!field.autoFillFromTable ? "opacity-100" : "opacity-0",
)}
/>
({config.sourceTable || "미설정"})
</CommandItem>
{allTables.map((table) => (
<CommandItem
key={table.tableName}
value={table.tableName}
onSelect={(value) => {
updateField(index, { autoFillFromTable: value, autoFillFrom: undefined });
loadAutoFillTableColumns(value, index);
}}
className="text-[10px] sm:text-xs"
>
<Check
className={cn(
"mr-1 h-2 w-2 sm:mr-2 sm:h-3 sm:w-3",
field.autoFillFromTable === table.tableName ? "opacity-100" : "opacity-0",
)}
/>
{table.displayName || table.tableName}
</CommandItem>
))}
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
<p className="text-[9px] text-gray-500 sm:text-[10px]">
</p>
{/* 필드 선택 */}
@ -764,16 +850,26 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
role="combobox"
className="h-6 w-full justify-between text-[10px] sm:h-7 sm:text-xs"
>
{field.autoFillFrom
? sourceTableColumns.find(c => c.columnName === field.autoFillFrom)?.columnLabel || field.autoFillFrom
: "필드 선택 안 함"}
{(() => {
if (!field.autoFillFrom) return "필드 선택 안 함";
// 선택된 테이블의 컬럼에서 찾기
const columns = field.autoFillFromTable
? (autoFillTableColumns[index] || [])
: (loadedSourceTableColumns.length > 0 ? loadedSourceTableColumns : sourceTableColumns);
const found = columns.find(c => c.columnName === field.autoFillFrom);
return found?.columnLabel || field.autoFillFrom;
})()}
<ChevronsUpDown className="ml-1 h-2 w-2 shrink-0 opacity-50 sm:h-3 sm:w-3" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[180px] p-0 sm:w-[200px]">
<Command>
<CommandInput placeholder="컬럼 검색..." className="h-6 text-[10px] sm:h-7 sm:text-xs" />
<CommandEmpty className="text-[10px] sm:text-xs"> .</CommandEmpty>
<CommandEmpty className="text-[10px] sm:text-xs">
{field.autoFillFromTable ? "컬럼을 찾을 수 없습니다" : "원본 테이블을 먼저 선택하세요"}
</CommandEmpty>
<CommandGroup className="max-h-[150px] overflow-auto sm:max-h-[200px]">
<CommandItem
value=""
@ -788,25 +884,32 @@ export const SelectedItemsDetailInputConfigPanel: React.FC<SelectedItemsDetailIn
/>
</CommandItem>
{sourceTableColumns.map((column) => (
<CommandItem
key={column.columnName}
value={column.columnName}
onSelect={() => updateField(index, { autoFillFrom: column.columnName })}
className="text-[10px] sm:text-xs"
>
<Check
className={cn(
"mr-1 h-2 w-2 sm:mr-2 sm:h-3 sm:w-3",
field.autoFillFrom === column.columnName ? "opacity-100" : "opacity-0",
)}
/>
<div>
<div className="font-medium">{column.columnLabel}</div>
<div className="text-[9px] text-gray-500">{column.columnName}</div>
</div>
</CommandItem>
))}
{(() => {
// 선택된 테이블의 컬럼 또는 기본 원본 테이블 컬럼
const columns = field.autoFillFromTable
? (autoFillTableColumns[index] || [])
: (loadedSourceTableColumns.length > 0 ? loadedSourceTableColumns : sourceTableColumns);
return columns.map((column) => (
<CommandItem
key={column.columnName}
value={column.columnName}
onSelect={(value) => updateField(index, { autoFillFrom: value })}
className="text-[10px] sm:text-xs"
>
<Check
className={cn(
"mr-1 h-2 w-2 sm:mr-2 sm:h-3 sm:w-3",
field.autoFillFrom === column.columnName ? "opacity-100" : "opacity-0",
)}
/>
<div>
<div className="font-medium">{column.columnLabel || column.columnName}</div>
{column.dataType && <div className="text-[8px] text-gray-500">{column.dataType}</div>}
</div>
</CommandItem>
));
})()}
</CommandGroup>
</Command>
</PopoverContent>