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

286 lines
11 KiB
TypeScript

"use client";
import React, { useEffect } from "react";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Globe } from "lucide-react";
// 타입 import
import { RightPanelProps } from "../types/redesigned";
// 컴포넌트 import
import StepProgress from "./StepProgress";
import ConnectionStep from "./ConnectionStep";
import TableStep from "./TableStep";
import FieldMappingStep from "./FieldMappingStep";
import ControlConditionStep from "./ControlConditionStep";
import ActionConfigStep from "./ActionConfigStep";
import MultiActionConfigStep from "./MultiActionConfigStep";
import ExternalCallPanel from "../../../external-call/ExternalCallPanel";
/**
* 🎯 우측 패널 (70% 너비)
* - 단계별 진행 UI
* - 연결 → 테이블 → 필드 매핑
* - 시각적 매핑 영역
*/
const RightPanel: React.FC<RightPanelProps> = ({ state, actions }) => {
console.log("🔄 [RightPanel] 컴포넌트 렌더링 - connectionType:", state.connectionType);
// connectionType 변경 감지
useEffect(() => {
console.log("🔄 [RightPanel] connectionType 변경됨:", state.connectionType);
}, [state.connectionType]);
// 완료된 단계 계산
const completedSteps: number[] = [];
if (state.fromConnection && state.toConnection) {
completedSteps.push(1);
}
if (state.fromTable && state.toTable) {
completedSteps.push(2);
}
// 새로운 단계 순서에 따른 완료 조건
const needsFieldMapping = state.actionType === "insert" || state.actionType === "upsert";
// 3단계: 제어 조건 (테이블 선택 후 바로 접근 가능)
if (state.fromTable && state.toTable) {
completedSteps.push(3);
}
// 4단계: 액션 설정
if (state.actionType) {
completedSteps.push(4);
}
// 5단계: 컬럼 매핑 (INSERT/UPSERT인 경우에만)
if (needsFieldMapping && state.fieldMappings.length > 0) {
completedSteps.push(5);
}
const renderCurrentStep = () => {
try {
// 외부호출인 경우 단계 무시하고 바로 외부호출 설정 화면 표시
console.log("🔍 [RightPanel] renderCurrentStep - connectionType:", state.connectionType);
if (state.connectionType === "external_call") {
console.log("✅ [RightPanel] 외부호출 화면 렌더링");
return (
<div className="flex h-full flex-col">
{/* 헤더 */}
<div className="flex-shrink-0 px-4 py-2">
<div className="flex items-center gap-3 border-b pb-2">
<Globe className="h-5 w-5 text-blue-600" />
<h2 className="text-lg font-semibold"> </h2>
</div>
<p className="text-muted-foreground mt-1 text-sm">
REST API .
</p>
</div>
{/* 관계명 및 설명 입력 */}
<div className="flex-shrink-0 px-4 pb-2">
<div className="space-y-3">
<div>
<label className="text-sm font-medium"> *</label>
<input
type="text"
value={state.relationshipName || ""}
onChange={(e) => actions.setRelationshipName(e.target.value)}
placeholder="외부호출 관계의 이름을 입력하세요"
className="mt-1 w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
/>
</div>
<div>
<label className="text-sm font-medium"></label>
<textarea
value={state.description || ""}
onChange={(e) => actions.setDescription(e.target.value)}
placeholder="외부호출의 용도나 설명을 입력하세요"
rows={2}
className="mt-1 w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
/>
</div>
</div>
</div>
{/* 외부호출 패널 - 공간 최적화 */}
<div className="flex-1 overflow-hidden px-4">
<div className="h-full max-h-[calc(100vh-400px)] overflow-y-auto">
<ExternalCallPanel
relationshipId={`external-call-${Date.now()}`}
readonly={false}
initialSettings={
state.externalCallConfig || {
callType: "rest-api",
restApiSettings: {
apiUrl: "",
httpMethod: "POST",
headers: { "Content-Type": "application/json" },
bodyTemplate: "",
authentication: { type: "none" },
timeout: 30000,
retryCount: 3,
},
}
}
onSettingsChange={actions.updateExternalCallConfig}
/>
</div>
</div>
{/* 하단 버튼 - 바로 붙여서 고정 */}
<div className="flex-shrink-0 border-t bg-white px-4 py-2">
<div className="flex gap-3">
<Button
onClick={actions.saveMappings}
className="w-full bg-blue-600 hover:bg-blue-700"
disabled={state.isLoading}
>
{state.isLoading ? "저장 중..." : "저장"}
</Button>
</div>
</div>
</div>
);
}
// 데이터 저장인 경우에만 단계별 진행
switch (state.currentStep) {
case 1:
return (
<ConnectionStep
connectionType={state.connectionType}
fromConnection={state.fromConnection}
toConnection={state.toConnection}
relationshipName={state.relationshipName}
description={state.description}
diagramId={state.diagramId} // 🔧 수정 모드 감지용
onSelectConnection={actions.selectConnection}
onSetRelationshipName={actions.setRelationshipName}
onSetDescription={actions.setDescription}
onNext={() => actions.goToStep(2)}
/>
);
case 2:
return (
<div className="space-y-4">
<TableStep
fromConnection={state.fromConnection}
toConnection={state.toConnection}
fromTable={state.fromTable}
toTable={state.toTable}
onSelectTable={actions.selectTable}
onNext={() => actions.goToStep(3)} // 3단계(제어 조건)
onBack={() => actions.goToStep(1)}
/>
</div>
);
case 3:
// 데이터 저장인 경우 제어 조건 단계
return (
<ControlConditionStep
state={state}
actions={actions}
onBack={() => actions.goToStep(2)}
onNext={() => {
// 4단계로 넘어가기 전에 컬럼 로드
actions.loadColumns();
actions.goToStep(4);
}}
/>
);
case 4:
// 외부호출인 경우 4단계 없음
if (state.connectionType === "external_call") {
return null;
}
// 4단계: 통합된 멀티 액션 설정 (제어 조건 + 액션 설정 + 컬럼 매핑)
return (
<MultiActionConfigStep
fromTable={state.fromTable}
toTable={state.toTable}
fromConnection={state.fromConnection}
toConnection={state.toConnection}
fromColumns={state.fromColumns} // 🔧 중앙에서 관리되는 컬럼 정보
toColumns={state.toColumns} // 🔧 중앙에서 관리되는 컬럼 정보
controlConditions={state.controlConditions}
onUpdateControlCondition={actions.updateControlCondition}
onDeleteControlCondition={actions.deleteControlCondition}
onAddControlCondition={actions.addControlCondition}
actionGroups={state.actionGroups}
groupsLogicalOperator={state.groupsLogicalOperator}
onUpdateActionGroup={actions.updateActionGroup}
onDeleteActionGroup={actions.deleteActionGroup}
onAddActionGroup={actions.addActionGroup}
onAddActionToGroup={actions.addActionToGroup}
onUpdateActionInGroup={actions.updateActionInGroup}
onDeleteActionFromGroup={actions.deleteActionFromGroup}
onSetGroupsLogicalOperator={actions.setGroupsLogicalOperator}
fieldMappings={state.fieldMappings}
onCreateMapping={actions.createMapping}
onDeleteMapping={actions.deleteMapping}
onLoadColumns={actions.loadColumns}
onNext={() => {
// 완료 처리 - 저장 및 상위 컴포넌트 알림
actions.saveMappings();
}}
onBack={() => actions.goToStep(3)}
/>
);
default:
return null;
}
} catch (error) {
console.error("❌ [RightPanel] renderCurrentStep 에러:", error);
return <div>renderCurrentStep : {String(error)}</div>;
}
};
try {
console.log("🎯 [RightPanel] return 시작 - connectionType:", state.connectionType);
return (
<div className="flex h-full flex-col">
{/* 단계 진행 표시 - 외부호출이 아닐 때만 */}
{state.connectionType !== "external_call" && (
<div className="bg-card/50 border-b p-3">
<StepProgress
currentStep={state.currentStep}
completedSteps={completedSteps}
onStepClick={actions.goToStep}
/>
</div>
)}
{/* 현재 단계 컨텐츠 */}
<div className="min-h-0 flex-1 p-3">
{(() => {
console.log("🎯 [RightPanel] 조건부 렌더링 체크 - connectionType:", state.connectionType);
console.log("🎯 [RightPanel] external_call인가?", state.connectionType === "external_call");
if (state.connectionType === "external_call") {
console.log("🎯 [RightPanel] 외부호출 렌더링 시작");
return renderCurrentStep();
} else {
console.log("🎯 [RightPanel] 데이터 저장 렌더링 시작");
return <Card className="flex h-full flex-col overflow-hidden">{renderCurrentStep()}</Card>;
}
})()}
</div>
</div>
);
} catch (error) {
console.error("❌ [RightPanel] 렌더링 에러:", error);
return <div> : {String(error)}</div>;
}
};
export default RightPanel;