diff --git a/frontend/app/(main)/admin/batchmng/edit/[id]/page.tsx b/frontend/app/(main)/admin/batchmng/edit/[id]/page.tsx index f2a7f2e9..dbdf997e 100644 --- a/frontend/app/(main)/admin/batchmng/edit/[id]/page.tsx +++ b/frontend/app/(main)/admin/batchmng/edit/[id]/page.tsx @@ -62,6 +62,7 @@ export default function BatchEditPage() { const [conflictKey, setConflictKey] = useState(""); const [authServiceName, setAuthServiceName] = useState(""); const [authServiceNames, setAuthServiceNames] = useState([]); + const [dataArrayPath, setDataArrayPath] = useState(""); // 연결 정보 const [connections, setConnections] = useState([]); @@ -84,7 +85,27 @@ export default function BatchEditPage() { // REST API 미리보기 상태 const [apiPreviewData, setApiPreviewData] = useState([]); + const [fromApiFields, setFromApiFields] = useState([]); + // 인증 토큰 모드 (직접 입력 / DB에서 선택) + const [authTokenMode, setAuthTokenMode] = useState<"direct" | "db">("direct"); + const [fromApiKey, setFromApiKey] = useState(""); + + // API 파라미터 설정 + const [apiParamType, setApiParamType] = useState<"none" | "url" | "query">("none"); + const [apiParamName, setApiParamName] = useState(""); + const [apiParamValue, setApiParamValue] = useState(""); + const [apiParamSource, setApiParamSource] = useState<"static" | "dynamic">("static"); + + // 매핑 리스트 (새로운 UI용) + interface MappingItem { + id: string; + dbColumn: string; + sourceType: "api" | "fixed"; + apiField: string; + fixedValue: string; + } + const [mappingList, setMappingList] = useState([]); // 페이지 로드 시 배치 정보 조회 useEffect(() => { @@ -202,8 +223,21 @@ export default function BatchEditPage() { setSaveMode((config as any).save_mode || "INSERT"); setConflictKey((config as any).conflict_key || ""); setAuthServiceName((config as any).auth_service_name || ""); + setDataArrayPath((config as any).data_array_path || ""); + + // 인증 토큰 모드 설정 + if ((config as any).auth_service_name) { + setAuthTokenMode("db"); + } else { + setAuthTokenMode("direct"); + } if (config.batch_mappings && config.batch_mappings.length > 0) { + // API 키 설정 (첫 번째 매핑에서) + const firstMappingForApiKey = config.batch_mappings[0]; + if (firstMappingForApiKey.from_api_key) { + setFromApiKey(firstMappingForApiKey.from_api_key); + } console.log("📊 매핑 정보:", config.batch_mappings); console.log("📊 매핑 개수:", config.batch_mappings.length); config.batch_mappings.forEach((mapping, idx) => { @@ -257,6 +291,17 @@ export default function BatchEditPage() { fromConnectionType: firstMapping.from_connection_type, toConnectionType: firstMapping.to_connection_type }); + + // 기존 매핑을 mappingList로 변환 + const convertedMappingList: MappingItem[] = config.batch_mappings.map((mapping, index) => ({ + id: `mapping-${index}-${Date.now()}`, + dbColumn: mapping.to_column_name || "", + sourceType: (mapping as any).mapping_type === "fixed" ? "fixed" as const : "api" as const, + apiField: (mapping as any).mapping_type === "fixed" ? "" : mapping.from_column_name || "", + fixedValue: (mapping as any).mapping_type === "fixed" ? mapping.from_column_name || "" : "", + })); + setMappingList(convertedMappingList); + console.log("🔄 변환된 mappingList:", convertedMappingList); } } catch (error) { @@ -396,6 +441,29 @@ export default function BatchEditPage() { setMappings((prev) => [...prev, newMapping]); }; + // mappingList 관련 함수들 (새로운 UI용) + const addMappingListItem = () => { + const newId = `mapping-${Date.now()}`; + setMappingList((prev) => [ + ...prev, + { + id: newId, + dbColumn: "", + sourceType: "api", + apiField: "", + fixedValue: "", + }, + ]); + }; + + const removeMappingListItem = (id: string) => { + setMappingList((prev) => prev.filter((m) => m.id !== id)); + }; + + const updateMappingListItem = (id: string, updates: Partial) => { + setMappingList((prev) => prev.map((m) => (m.id === id ? { ...m, ...updates } : m))); + }; + // REST API 데이터 미리보기 (수정 화면용) const previewRestApiData = async () => { if (!mappings || mappings.length === 0) { @@ -428,23 +496,27 @@ export default function BatchEditPage() { } : undefined; + // authServiceName과 dataArrayPath 전달 const result = await BatchManagementAPI.previewRestApiData( first.from_api_url, first.from_api_key || "", first.from_table_name, method, paramInfo, - first.from_api_body || undefined + first.from_api_body || undefined, + authServiceName || undefined, + dataArrayPath || undefined ); setApiPreviewData(result.samples || []); + setFromApiFields(result.fields || []); toast.success( `API 데이터 미리보기 완료! ${result.fields.length}개 필드, ${result.samples.length}개 레코드` ); - } catch (error) { + } catch (error: any) { console.error("REST API 미리보기 오류:", error); - toast.error("API 데이터 미리보기에 실패했습니다."); + toast.error(error?.message || "API 데이터 미리보기에 실패했습니다."); } }; @@ -465,7 +537,10 @@ export default function BatchEditPage() { // 배치 설정 저장 const saveBatchConfig = async () => { - if (!batchName || !cronSchedule || mappings.length === 0) { + // restapi-to-db인 경우 mappingList 사용, 아닌 경우 mappings 사용 + const effectiveMappings = batchType === "restapi-to-db" ? mappingList : mappings; + + if (!batchName || !cronSchedule || effectiveMappings.length === 0) { toast.error("필수 항목을 모두 입력해주세요."); return; } @@ -473,15 +548,45 @@ export default function BatchEditPage() { try { setLoading(true); + // restapi-to-db인 경우 mappingList를 mappings 형식으로 변환 + let finalMappings: BatchMapping[] = mappings; + + if (batchType === "restapi-to-db" && batchConfig?.batch_mappings?.[0]) { + const first = batchConfig.batch_mappings[0] as any; + finalMappings = mappingList + .filter((m) => m.dbColumn) // DB 컬럼이 선택된 것만 + .map((m, index) => ({ + // FROM: REST API (기존 설정 복사) + from_connection_type: "restapi" as any, + from_connection_id: first.from_connection_id, + from_table_name: first.from_table_name, + from_column_name: m.sourceType === "fixed" ? m.fixedValue : m.apiField, + from_column_type: m.sourceType === "fixed" ? "text" : "text", + from_api_url: mappings[0]?.from_api_url || first.from_api_url, + from_api_key: authTokenMode === "direct" ? fromApiKey : first.from_api_key, + from_api_method: mappings[0]?.from_api_method || first.from_api_method, + from_api_body: mappings[0]?.from_api_body || first.from_api_body, + // TO: DB (기존 설정 복사) + to_connection_type: first.to_connection_type as any, + to_connection_id: first.to_connection_id, + to_table_name: toTable || first.to_table_name, + to_column_name: m.dbColumn, + to_column_type: toColumns.find((c) => c.column_name === m.dbColumn)?.data_type || "text", + mapping_type: m.sourceType === "fixed" ? "fixed" : "direct", + mapping_order: index + 1, + })) as BatchMapping[]; + } + await BatchAPI.updateBatchConfig(batchId, { batchName, description, cronSchedule, isActive, - mappings, + mappings: finalMappings, saveMode, conflictKey: saveMode === "UPSERT" ? conflictKey : undefined, - authServiceName: authServiceName || undefined + authServiceName: authTokenMode === "db" ? authServiceName : undefined, + dataArrayPath: dataArrayPath || undefined }); toast.success("배치 설정이 성공적으로 수정되었습니다."); @@ -507,39 +612,36 @@ export default function BatchEditPage() { } return ( -
-
-
- -

배치 설정 수정

-
-
- -
+
+ {/* 페이지 헤더 */} +
+ +

배치 설정 수정

{/* 기본 정보 */} - 기본 정보 + + 기본 정보 + {batchType && ( + + {batchType === "db-to-db" && "DB -> DB"} + {batchType === "restapi-to-db" && "REST API -> DB"} + {batchType === "db-to-restapi" && "DB -> REST API"} + + )} + -
+
- +