restapi 버튼 동작
This commit is contained in:
@@ -12,6 +12,7 @@ import { cn } from "@/lib/utils";
|
||||
import { ComponentData } from "@/types/screen";
|
||||
import { apiClient } from "@/lib/api/client";
|
||||
import { ButtonDataflowConfigPanel } from "./ButtonDataflowConfigPanel";
|
||||
import { ImprovedButtonControlConfigPanel } from "./ImprovedButtonControlConfigPanel";
|
||||
|
||||
interface ButtonConfigPanelProps {
|
||||
component: ComponentData;
|
||||
@@ -526,7 +527,7 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({ component,
|
||||
<p className="mt-1 text-sm text-gray-600">버튼 액션과 함께 실행될 추가 기능을 설정합니다</p>
|
||||
</div>
|
||||
|
||||
<ButtonDataflowConfigPanel component={component} onUpdateProperty={onUpdateProperty} />
|
||||
<ImprovedButtonControlConfigPanel component={component} onUpdateProperty={onUpdateProperty} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -37,7 +37,7 @@ interface RelationshipOption {
|
||||
* 🔥 버튼 제어관리 설정 패널 (Phase 1: 간편 모드만)
|
||||
*
|
||||
* 성능 최적화를 위해 간편 모드만 구현:
|
||||
* - 기존 관계도 선택
|
||||
* - 기존 관계 선택
|
||||
* - "after" 타이밍만 지원
|
||||
* - 복잡한 고급 모드는 Phase 2에서
|
||||
*/
|
||||
@@ -57,14 +57,14 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
const [relationshipOpen, setRelationshipOpen] = useState(false);
|
||||
const [previewData, setPreviewData] = useState<any>(null);
|
||||
|
||||
// 🔥 관계도 목록 로딩
|
||||
// 🔥 관계 목록 로딩
|
||||
useEffect(() => {
|
||||
if (config.enableDataflowControl) {
|
||||
loadDiagrams();
|
||||
}
|
||||
}, [config.enableDataflowControl]);
|
||||
|
||||
// 🔥 관계도 변경 시 관계 목록 로딩
|
||||
// 🔥 관계 변경 시 관계 목록 로딩
|
||||
useEffect(() => {
|
||||
if (dataflowConfig.selectedDiagramId) {
|
||||
loadRelationships(dataflowConfig.selectedDiagramId);
|
||||
@@ -72,12 +72,12 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
}, [dataflowConfig.selectedDiagramId]);
|
||||
|
||||
/**
|
||||
* 🔥 관계도 목록 로딩 (캐시 활용)
|
||||
* 🔥 관계 목록 로딩 (캐시 활용)
|
||||
*/
|
||||
const loadDiagrams = async () => {
|
||||
try {
|
||||
setDiagramsLoading(true);
|
||||
console.log("🔍 데이터플로우 관계도 목록 로딩...");
|
||||
console.log("🔍 데이터플로우 관계 목록 로딩...");
|
||||
|
||||
const response = await apiClient.get("/test-button-dataflow/diagrams");
|
||||
|
||||
@@ -90,10 +90,10 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
}));
|
||||
|
||||
setDiagrams(diagramList);
|
||||
console.log(`✅ 관계도 ${diagramList.length}개 로딩 완료`);
|
||||
console.log(`✅ 관계 ${diagramList.length}개 로딩 완료`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 관계도 목록 로딩 실패:", error);
|
||||
console.error("❌ 관계 목록 로딩 실패:", error);
|
||||
setDiagrams([]);
|
||||
} finally {
|
||||
setDiagramsLoading(false);
|
||||
@@ -106,7 +106,7 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
const loadRelationships = async (diagramId: number) => {
|
||||
try {
|
||||
setRelationshipsLoading(true);
|
||||
console.log(`🔍 관계도 ${diagramId} 관계 목록 로딩...`);
|
||||
console.log(`🔍 관계 ${diagramId} 관계 목록 로딩...`);
|
||||
|
||||
const response = await apiClient.get(`/test-button-dataflow/diagrams/${diagramId}/relationships`);
|
||||
|
||||
@@ -216,7 +216,7 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
}
|
||||
};
|
||||
|
||||
// 선택된 관계도 정보
|
||||
// 선택된 관계 정보
|
||||
const selectedDiagram = diagrams.find((d) => d.id === dataflowConfig.selectedDiagramId);
|
||||
const selectedRelationship = relationships.find((r) => r.id === dataflowConfig.selectedRelationshipId);
|
||||
|
||||
@@ -324,7 +324,7 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
<SelectValue placeholder="제어 모드를 선택하세요" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="simple">간편 모드 (관계도 선택)</SelectItem>
|
||||
<SelectItem value="simple">간편 모드 (관계 선택)</SelectItem>
|
||||
<SelectItem value="advanced" disabled>
|
||||
고급 모드 (개발중)
|
||||
</SelectItem>
|
||||
@@ -335,11 +335,11 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
{/* 간편 모드 설정 */}
|
||||
{(dataflowConfig.controlMode === "simple" || !dataflowConfig.controlMode) && (
|
||||
<div className="space-y-3 rounded border bg-gray-50 p-3">
|
||||
<h4 className="text-sm font-medium text-gray-700">관계도 선택</h4>
|
||||
<h4 className="text-sm font-medium text-gray-700">관계 선택</h4>
|
||||
|
||||
{/* 관계도 선택 */}
|
||||
{/* 관계 선택 */}
|
||||
<div>
|
||||
<Label className="text-xs">관계도</Label>
|
||||
<Label className="text-xs">관계</Label>
|
||||
<Popover open={diagramOpen} onOpenChange={setDiagramOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
@@ -357,7 +357,7 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
</Badge>
|
||||
</div>
|
||||
) : (
|
||||
"관계도를 선택하세요"
|
||||
"관계를 선택하세요"
|
||||
)}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
@@ -365,9 +365,9 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
<PopoverContent className="w-80 p-0">
|
||||
<div className="p-2">
|
||||
{diagramsLoading ? (
|
||||
<div className="p-4 text-center text-sm text-gray-500">관계도 목록을 불러오는 중...</div>
|
||||
<div className="p-4 text-center text-sm text-gray-500">관계 목록을 불러오는 중...</div>
|
||||
) : diagrams.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-gray-500">사용 가능한 관계도가 없습니다</div>
|
||||
<div className="p-4 text-center text-sm text-gray-500">사용 가능한 관계가 없습니다</div>
|
||||
) : (
|
||||
<div className="max-h-60 overflow-y-auto">
|
||||
{diagrams.map((diagram) => (
|
||||
@@ -377,7 +377,7 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
className="h-auto w-full justify-start p-2"
|
||||
onClick={() => {
|
||||
onUpdateProperty("webTypeConfig.dataflowConfig.selectedDiagramId", diagram.id);
|
||||
// 관계도 변경 시 기존 관계 선택 초기화
|
||||
// 관계 변경 시 기존 관계 선택 초기화
|
||||
onUpdateProperty("webTypeConfig.dataflowConfig.selectedRelationshipId", null);
|
||||
setDiagramOpen(false);
|
||||
}}
|
||||
@@ -435,7 +435,7 @@ export const ButtonDataflowConfigPanel: React.FC<ButtonDataflowConfigPanelProps>
|
||||
<div className="p-4 text-center text-sm text-gray-500">관계 목록을 불러오는 중...</div>
|
||||
) : relationships.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-gray-500">
|
||||
이 관계도에는 사용 가능한 관계가 없습니다
|
||||
이 관계에는 사용 가능한 관계가 없습니다
|
||||
</div>
|
||||
) : (
|
||||
<div className="max-h-60 overflow-y-auto">
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import {
|
||||
Settings,
|
||||
GitBranch,
|
||||
Clock,
|
||||
Zap,
|
||||
Info
|
||||
} from "lucide-react";
|
||||
import { ComponentData, ButtonDataflowConfig } from "@/types/screen";
|
||||
import { apiClient } from "@/lib/api/client";
|
||||
|
||||
interface ImprovedButtonControlConfigPanelProps {
|
||||
component: ComponentData;
|
||||
onUpdateProperty: (path: string, value: any) => void;
|
||||
}
|
||||
|
||||
interface RelationshipOption {
|
||||
id: string;
|
||||
name: string;
|
||||
sourceTable: string;
|
||||
targetTable: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 🔥 단순화된 버튼 제어 설정 패널
|
||||
*
|
||||
* 관계 실행만 지원:
|
||||
* - 관계 선택 및 실행 타이밍 설정
|
||||
* - 관계 내부에 데이터 저장/외부호출 로직 포함
|
||||
*/
|
||||
export const ImprovedButtonControlConfigPanel: React.FC<ImprovedButtonControlConfigPanelProps> = ({
|
||||
component,
|
||||
onUpdateProperty,
|
||||
}) => {
|
||||
const config = component.webTypeConfig || {};
|
||||
const dataflowConfig = config.dataflowConfig || {};
|
||||
|
||||
// 🔥 State 관리
|
||||
const [relationships, setRelationships] = useState<RelationshipOption[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// 🔥 관계 목록 로딩
|
||||
useEffect(() => {
|
||||
if (config.enableDataflowControl) {
|
||||
loadRelationships();
|
||||
}
|
||||
}, [config.enableDataflowControl]);
|
||||
|
||||
/**
|
||||
* 🔥 전체 관계 목록 로드 (관계도별 구분 없이)
|
||||
*/
|
||||
const loadRelationships = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
console.log("🔍 전체 관계 목록 로딩...");
|
||||
|
||||
const response = await apiClient.get("/test-button-dataflow/relationships/all");
|
||||
|
||||
if (response.data.success && Array.isArray(response.data.data)) {
|
||||
const relationshipList = response.data.data.map((rel: any) => ({
|
||||
id: rel.id,
|
||||
name: rel.name || `${rel.sourceTable} → ${rel.targetTable}`,
|
||||
sourceTable: rel.sourceTable,
|
||||
targetTable: rel.targetTable,
|
||||
category: rel.category || "데이터 흐름",
|
||||
}));
|
||||
|
||||
setRelationships(relationshipList);
|
||||
console.log(`✅ 관계 ${relationshipList.length}개 로딩 완료`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 관계 목록 로딩 실패:", error);
|
||||
setRelationships([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 🔥 관계 선택 핸들러
|
||||
*/
|
||||
const handleRelationshipSelect = (relationshipId: string) => {
|
||||
const selectedRelationship = relationships.find(r => r.id === relationshipId);
|
||||
if (selectedRelationship) {
|
||||
onUpdateProperty("webTypeConfig.dataflowConfig.relationshipConfig", {
|
||||
relationshipId: selectedRelationship.id,
|
||||
relationshipName: selectedRelationship.name,
|
||||
executionTiming: "after", // 기본값
|
||||
contextData: {},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 🔥 제어 타입 변경 핸들러
|
||||
*/
|
||||
const handleControlTypeChange = (controlType: string) => {
|
||||
// 기존 설정 초기화
|
||||
onUpdateProperty("webTypeConfig.dataflowConfig", {
|
||||
controlMode: controlType,
|
||||
relationshipConfig: controlType === "relationship" ? undefined : null,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* 🔥 제어관리 활성화 스위치 */}
|
||||
<div className="flex items-center justify-between rounded-lg border bg-blue-50 p-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Settings className="h-4 w-4 text-blue-600" />
|
||||
<div>
|
||||
<Label className="text-sm font-medium">🎮 고급 제어 기능</Label>
|
||||
<p className="mt-1 text-xs text-gray-600">버튼 클릭 시 추가 작업을 자동으로 실행합니다</p>
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={config.enableDataflowControl || false}
|
||||
onCheckedChange={(checked) => onUpdateProperty("webTypeConfig.enableDataflowControl", checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 🔥 제어관리가 활성화된 경우에만 설정 표시 */}
|
||||
{config.enableDataflowControl && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>버튼 제어 설정</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Tabs
|
||||
value={dataflowConfig.controlMode || "none"}
|
||||
onValueChange={handleControlTypeChange}
|
||||
>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="none">제어 없음</TabsTrigger>
|
||||
<TabsTrigger value="relationship">관계 실행</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="none" className="mt-4">
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
<Zap className="h-8 w-8 mx-auto mb-2 opacity-50" />
|
||||
<p>추가 제어 없이 기본 버튼 액션만 실행됩니다.</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="relationship" className="mt-4">
|
||||
<RelationshipSelector
|
||||
relationships={relationships}
|
||||
selectedRelationshipId={dataflowConfig.relationshipConfig?.relationshipId}
|
||||
onSelect={handleRelationshipSelect}
|
||||
loading={loading}
|
||||
/>
|
||||
|
||||
{dataflowConfig.relationshipConfig && (
|
||||
<div className="mt-4 space-y-4">
|
||||
<Separator />
|
||||
<ExecutionTimingSelector
|
||||
value={dataflowConfig.relationshipConfig.executionTiming}
|
||||
onChange={(timing) =>
|
||||
onUpdateProperty("webTypeConfig.dataflowConfig.relationshipConfig.executionTiming", timing)
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="rounded bg-blue-50 p-3">
|
||||
<div className="flex items-start space-x-2">
|
||||
<Info className="h-4 w-4 text-blue-600 mt-0.5" />
|
||||
<div className="text-xs text-blue-800">
|
||||
<p className="font-medium">관계 실행 정보:</p>
|
||||
<p className="mt-1">선택한 관계에 설정된 데이터 저장, 외부호출 등의 모든 액션이 자동으로 실행됩니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 🔥 관계 선택 컴포넌트
|
||||
*/
|
||||
const RelationshipSelector: React.FC<{
|
||||
relationships: RelationshipOption[];
|
||||
selectedRelationshipId?: string;
|
||||
onSelect: (relationshipId: string) => void;
|
||||
loading: boolean;
|
||||
}> = ({ relationships, selectedRelationshipId, onSelect, loading }) => {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<GitBranch className="h-4 w-4 text-blue-600" />
|
||||
<Label>실행할 관계 선택</Label>
|
||||
</div>
|
||||
|
||||
<Select value={selectedRelationshipId || ""} onValueChange={onSelect}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="관계를 선택하세요" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{loading ? (
|
||||
<div className="p-4 text-center text-sm text-gray-500">관계 목록을 불러오는 중...</div>
|
||||
) : relationships.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-gray-500">사용 가능한 관계가 없습니다</div>
|
||||
) : (
|
||||
relationships.map((rel) => (
|
||||
<SelectItem key={rel.id} value={rel.id}>
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">{rel.name}</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{rel.sourceTable} → {rel.targetTable}
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 🔥 실행 타이밍 선택 컴포넌트
|
||||
*/
|
||||
const ExecutionTimingSelector: React.FC<{
|
||||
value: string;
|
||||
onChange: (timing: "before" | "after" | "replace") => void;
|
||||
}> = ({ value, onChange }) => {
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Clock className="h-4 w-4 text-orange-600" />
|
||||
<Label>실행 타이밍</Label>
|
||||
</div>
|
||||
|
||||
<Select value={value} onValueChange={onChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="실행 타이밍을 선택하세요" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="before">
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">Before (사전 실행)</span>
|
||||
<span className="text-xs text-muted-foreground">버튼 액션 실행 전에 제어를 실행합니다</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="after">
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">After (사후 실행)</span>
|
||||
<span className="text-xs text-muted-foreground">버튼 액션 실행 후에 제어를 실행합니다</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="replace">
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">Replace (대체 실행)</span>
|
||||
<span className="text-xs text-muted-foreground">버튼 액션 대신 제어만 실행합니다</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user