데이터 관계 수정 시 라우트 변경
This commit is contained in:
@@ -826,3 +826,54 @@ export async function deleteDiagram(
|
|||||||
res.status(500).json(response);
|
res.status(500).json(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* relationship_id로 관계도 관계 조회
|
||||||
|
*/
|
||||||
|
export async function getDiagramRelationshipsByRelationshipId(
|
||||||
|
req: AuthenticatedRequest,
|
||||||
|
res: Response
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
const { relationshipId } = req.params;
|
||||||
|
const companyCode = (req.user as any)?.company_code || "*";
|
||||||
|
|
||||||
|
if (!relationshipId) {
|
||||||
|
const response: ApiResponse<null> = {
|
||||||
|
success: false,
|
||||||
|
message: "관계 ID가 필요합니다.",
|
||||||
|
error: {
|
||||||
|
code: "MISSING_RELATIONSHIP_ID",
|
||||||
|
details: "relationshipId 파라미터가 필요합니다.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
res.status(400).json(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataflowService = new DataflowService();
|
||||||
|
const relationships = await dataflowService.getDiagramRelationshipsByRelationshipId(
|
||||||
|
companyCode,
|
||||||
|
parseInt(relationshipId)
|
||||||
|
);
|
||||||
|
|
||||||
|
const response: ApiResponse<any[]> = {
|
||||||
|
success: true,
|
||||||
|
message: "관계도 관계 목록을 성공적으로 조회했습니다.",
|
||||||
|
data: relationships,
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(200).json(response);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("관계도 관계 조회 실패:", error);
|
||||||
|
const response: ApiResponse<null> = {
|
||||||
|
success: false,
|
||||||
|
message: "관계도 관계 조회에 실패했습니다.",
|
||||||
|
error: {
|
||||||
|
code: "DIAGRAM_RELATIONSHIPS_FETCH_FAILED",
|
||||||
|
details: error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
res.status(500).json(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
getTableData,
|
getTableData,
|
||||||
getDataFlowDiagrams,
|
getDataFlowDiagrams,
|
||||||
getDiagramRelationships,
|
getDiagramRelationships,
|
||||||
|
getDiagramRelationshipsByRelationshipId,
|
||||||
copyDiagram,
|
copyDiagram,
|
||||||
deleteDiagram,
|
deleteDiagram,
|
||||||
} from "../controllers/dataflowController";
|
} from "../controllers/dataflowController";
|
||||||
@@ -108,4 +109,10 @@ router.post("/diagrams/:diagramName/copy", copyDiagram);
|
|||||||
*/
|
*/
|
||||||
router.delete("/diagrams/:diagramName", deleteDiagram);
|
router.delete("/diagrams/:diagramName", deleteDiagram);
|
||||||
|
|
||||||
|
// relationship_id로 관계도 관계 조회
|
||||||
|
router.get(
|
||||||
|
"/relationships/:relationshipId/diagram",
|
||||||
|
getDiagramRelationshipsByRelationshipId
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -776,6 +776,7 @@ export class DataflowService {
|
|||||||
const relationships = await prisma.table_relationships.findMany({
|
const relationships = await prisma.table_relationships.findMany({
|
||||||
where: whereCondition,
|
where: whereCondition,
|
||||||
select: {
|
select: {
|
||||||
|
relationship_id: true,
|
||||||
relationship_name: true,
|
relationship_name: true,
|
||||||
from_table_name: true,
|
from_table_name: true,
|
||||||
to_table_name: true,
|
to_table_name: true,
|
||||||
@@ -797,6 +798,7 @@ export class DataflowService {
|
|||||||
|
|
||||||
if (!diagramMap.has(diagramName)) {
|
if (!diagramMap.has(diagramName)) {
|
||||||
diagramMap.set(diagramName, {
|
diagramMap.set(diagramName, {
|
||||||
|
relationshipId: rel.relationship_id, // 첫 번째 관계의 ID를 대표 ID로 사용
|
||||||
diagramName: diagramName,
|
diagramName: diagramName,
|
||||||
connectionType: rel.connection_type,
|
connectionType: rel.connection_type,
|
||||||
relationshipType: rel.relationship_type,
|
relationshipType: rel.relationship_type,
|
||||||
@@ -998,4 +1000,59 @@ export class DataflowService {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* relationship_id로 해당 관계도의 모든 관계 조회
|
||||||
|
*/
|
||||||
|
async getDiagramRelationshipsByRelationshipId(
|
||||||
|
companyCode: string,
|
||||||
|
relationshipId: number
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
logger.info(
|
||||||
|
`DataflowService: relationship_id로 관계도 관계 조회 - ${relationshipId}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 먼저 해당 relationship_id의 관계도명을 찾음
|
||||||
|
const targetRelationship = await prisma.table_relationships.findFirst({
|
||||||
|
where: {
|
||||||
|
relationship_id: relationshipId,
|
||||||
|
company_code: companyCode,
|
||||||
|
is_active: "Y",
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
relationship_name: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!targetRelationship) {
|
||||||
|
throw new Error("해당 관계 ID를 찾을 수 없습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 같은 관계도명을 가진 모든 관계 조회
|
||||||
|
const relationships = await prisma.table_relationships.findMany({
|
||||||
|
where: {
|
||||||
|
relationship_name: targetRelationship.relationship_name,
|
||||||
|
company_code: companyCode,
|
||||||
|
is_active: "Y",
|
||||||
|
},
|
||||||
|
orderBy: [{ relationship_id: "asc" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`DataflowService: relationship_id로 관계도 관계 조회 완료 - ${relationships.length}개 관계`
|
||||||
|
);
|
||||||
|
|
||||||
|
return relationships.map((rel) => ({
|
||||||
|
...rel,
|
||||||
|
settings: rel.settings as any,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(
|
||||||
|
`DataflowService: relationship_id로 관계도 관계 조회 실패 - ${relationshipId}`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useParams, useRouter } from "next/navigation";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { ArrowLeft } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { DataFlowDesigner } from "@/components/dataflow/DataFlowDesigner";
|
||||||
|
import { DataFlowAPI } from "@/lib/api/dataflow";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
export default function DataFlowEditPage() {
|
||||||
|
const params = useParams();
|
||||||
|
const router = useRouter();
|
||||||
|
const [relationshipId, setRelationshipId] = useState<string>("");
|
||||||
|
const [diagramName, setDiagramName] = useState<string>("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (params.relationshipId) {
|
||||||
|
// URL에서 relationship_id 설정
|
||||||
|
const id = params.relationshipId as string;
|
||||||
|
setRelationshipId(id);
|
||||||
|
|
||||||
|
// relationship_id로 관계도명 조회
|
||||||
|
const fetchDiagramName = async () => {
|
||||||
|
try {
|
||||||
|
const relationships = await DataFlowAPI.getDiagramRelationshipsByRelationshipId(id);
|
||||||
|
if (relationships.length > 0) {
|
||||||
|
setDiagramName(relationships[0].relationship_name);
|
||||||
|
} else {
|
||||||
|
setDiagramName(`관계도 ID: ${id}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("관계도명 조회 실패:", error);
|
||||||
|
setDiagramName(`관계도 ID: ${id}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchDiagramName();
|
||||||
|
}
|
||||||
|
}, [params.relationshipId]);
|
||||||
|
|
||||||
|
const handleBackToList = () => {
|
||||||
|
router.push("/admin/dataflow");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!relationshipId || !diagramName) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-64 items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-b-2 border-blue-600"></div>
|
||||||
|
<p className="text-gray-500">관계도 정보를 불러오는 중...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* 헤더 */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<Button variant="outline" size="sm" onClick={handleBackToList} className="flex items-center space-x-2">
|
||||||
|
<ArrowLeft className="h-4 w-4" />
|
||||||
|
<span>목록으로</span>
|
||||||
|
</Button>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold text-gray-900">📊 관계도 편집</h1>
|
||||||
|
<p className="mt-1 text-gray-600">
|
||||||
|
<span className="font-medium text-blue-600">{diagramName}</span> 관계도를 편집하고 있습니다
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 데이터플로우 디자이너 */}
|
||||||
|
<div className="rounded-lg border border-gray-200 bg-white">
|
||||||
|
<DataFlowDesigner
|
||||||
|
selectedDiagram={diagramName}
|
||||||
|
relationshipId={relationshipId}
|
||||||
|
onBackToList={handleBackToList}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
import { DataFlowDesigner } from "@/components/dataflow/DataFlowDesigner";
|
import { DataFlowDesigner } from "@/components/dataflow/DataFlowDesigner";
|
||||||
import DataFlowList from "@/components/dataflow/DataFlowList";
|
import DataFlowList from "@/components/dataflow/DataFlowList";
|
||||||
import { TableRelationship, DataFlowDiagram } from "@/lib/api/dataflow";
|
import { TableRelationship, DataFlowDiagram } from "@/lib/api/dataflow";
|
||||||
@@ -12,6 +13,7 @@ type Step = "list" | "design";
|
|||||||
|
|
||||||
export default function DataFlowPage() {
|
export default function DataFlowPage() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const router = useRouter();
|
||||||
const [currentStep, setCurrentStep] = useState<Step>("list");
|
const [currentStep, setCurrentStep] = useState<Step>("list");
|
||||||
const [selectedDiagram, setSelectedDiagram] = useState<DataFlowDiagram | null>(null);
|
const [selectedDiagram, setSelectedDiagram] = useState<DataFlowDiagram | null>(null);
|
||||||
const [stepHistory, setStepHistory] = useState<Step[]>(["list"]);
|
const [stepHistory, setStepHistory] = useState<Step[]>(["list"]);
|
||||||
@@ -79,8 +81,14 @@ export default function DataFlowPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDesignDiagram = (diagram: DataFlowDiagram | null) => {
|
const handleDesignDiagram = (diagram: DataFlowDiagram | null) => {
|
||||||
setSelectedDiagram(diagram);
|
if (diagram) {
|
||||||
goToNextStep("design");
|
// 기존 관계도 편집 - 새로운 URL로 이동
|
||||||
|
router.push(`/admin/dataflow/edit/${diagram.relationshipId}`);
|
||||||
|
} else {
|
||||||
|
// 새 관계도 생성 - 현재 페이지에서 처리
|
||||||
|
setSelectedDiagram(null);
|
||||||
|
goToNextStep("design");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -50,16 +50,18 @@ const nodeTypes = {
|
|||||||
const edgeTypes = {};
|
const edgeTypes = {};
|
||||||
|
|
||||||
interface DataFlowDesignerProps {
|
interface DataFlowDesignerProps {
|
||||||
companyCode: string;
|
companyCode?: string;
|
||||||
onSave?: (relationships: TableRelationship[]) => void;
|
onSave?: (relationships: TableRelationship[]) => void;
|
||||||
selectedDiagram?: DataFlowDiagram | null;
|
selectedDiagram?: DataFlowDiagram | string | null;
|
||||||
|
relationshipId?: string;
|
||||||
onBackToList?: () => void;
|
onBackToList?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableRelationship 타입은 dataflow.ts에서 import
|
// TableRelationship 타입은 dataflow.ts에서 import
|
||||||
|
|
||||||
export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
||||||
companyCode,
|
companyCode = "*",
|
||||||
|
relationshipId,
|
||||||
onSave,
|
onSave,
|
||||||
selectedDiagram,
|
selectedDiagram,
|
||||||
onBackToList, // eslint-disable-line @typescript-eslint/no-unused-vars
|
onBackToList, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
@@ -167,12 +169,14 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||||||
|
|
||||||
// 선택된 관계도의 관계 로드
|
// 선택된 관계도의 관계 로드
|
||||||
const loadSelectedDiagramRelationships = useCallback(async () => {
|
const loadSelectedDiagramRelationships = useCallback(async () => {
|
||||||
if (!selectedDiagram) return;
|
if (!relationshipId) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log("🔍 관계도 로드 시작:", selectedDiagram.diagramName);
|
console.log("🔍 관계도 로드 시작 (relationshipId):", relationshipId);
|
||||||
toast.loading("관계도를 불러오는 중...", { id: "load-diagram" });
|
toast.loading("관계도를 불러오는 중...", { id: "load-diagram" });
|
||||||
const diagramRelationships = await DataFlowAPI.getDiagramRelationships(selectedDiagram.diagramName);
|
|
||||||
|
// relationshipId로 해당 관계도의 모든 관계 조회
|
||||||
|
const diagramRelationships = await DataFlowAPI.getDiagramRelationshipsByRelationshipId(relationshipId);
|
||||||
console.log("📋 관계도 관계 데이터:", diagramRelationships);
|
console.log("📋 관계도 관계 데이터:", diagramRelationships);
|
||||||
console.log("📋 첫 번째 관계 상세:", diagramRelationships[0]);
|
console.log("📋 첫 번째 관계 상세:", diagramRelationships[0]);
|
||||||
console.log(
|
console.log(
|
||||||
@@ -320,12 +324,12 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||||||
console.log("🔗 생성된 관계 에지 수:", relationshipEdges.length);
|
console.log("🔗 생성된 관계 에지 수:", relationshipEdges.length);
|
||||||
console.log("📍 관계 에지 상세:", relationshipEdges);
|
console.log("📍 관계 에지 상세:", relationshipEdges);
|
||||||
setEdges(relationshipEdges);
|
setEdges(relationshipEdges);
|
||||||
toast.success(`"${selectedDiagram.diagramName}" 관계도를 불러왔습니다.`, { id: "load-diagram" });
|
toast.success("관계도를 불러왔습니다.", { id: "load-diagram" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("선택된 관계도 로드 실패:", error);
|
console.error("선택된 관계도 로드 실패:", error);
|
||||||
toast.error("관계도를 불러오는데 실패했습니다.", { id: "load-diagram" });
|
toast.error("관계도를 불러오는데 실패했습니다.", { id: "load-diagram" });
|
||||||
}
|
}
|
||||||
}, [selectedDiagram, setNodes, setEdges, selectedColumns, handleColumnClick]);
|
}, [relationshipId, setNodes, setEdges, selectedColumns, handleColumnClick]);
|
||||||
|
|
||||||
// 기존 관계 로드 (새 관계도 생성 시)
|
// 기존 관계 로드 (새 관계도 생성 시)
|
||||||
const loadExistingRelationships = useCallback(async () => {
|
const loadExistingRelationships = useCallback(async () => {
|
||||||
@@ -387,13 +391,13 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||||||
// 컴포넌트 마운트 시 관계 로드
|
// 컴포넌트 마운트 시 관계 로드
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (companyCode) {
|
if (companyCode) {
|
||||||
if (selectedDiagram) {
|
if (relationshipId) {
|
||||||
loadSelectedDiagramRelationships();
|
loadSelectedDiagramRelationships();
|
||||||
} else {
|
} else {
|
||||||
loadExistingRelationships();
|
loadExistingRelationships();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [companyCode, selectedDiagram, loadExistingRelationships, loadSelectedDiagramRelationships]);
|
}, [companyCode, relationshipId, loadExistingRelationships, loadSelectedDiagramRelationships]);
|
||||||
|
|
||||||
// 노드 선택 변경 핸들러
|
// 노드 선택 변경 핸들러
|
||||||
const onSelectionChange = useCallback(({ nodes }: { nodes: Node<TableNodeData>[] }) => {
|
const onSelectionChange = useCallback(({ nodes }: { nodes: Node<TableNodeData>[] }) => {
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ export interface TableDataResponse {
|
|||||||
|
|
||||||
// 관계도 정보 인터페이스
|
// 관계도 정보 인터페이스
|
||||||
export interface DataFlowDiagram {
|
export interface DataFlowDiagram {
|
||||||
|
relationshipId: number;
|
||||||
diagramName: string;
|
diagramName: string;
|
||||||
connectionType: string;
|
connectionType: string;
|
||||||
relationshipType: string;
|
relationshipType: string;
|
||||||
@@ -377,7 +378,7 @@ export class DataFlowAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 특정 관계도의 모든 관계 조회
|
// 특정 관계도의 모든 관계 조회 (관계도명으로)
|
||||||
static async getDiagramRelationships(diagramName: string): Promise<TableRelationship[]> {
|
static async getDiagramRelationships(diagramName: string): Promise<TableRelationship[]> {
|
||||||
try {
|
try {
|
||||||
const encodedDiagramName = encodeURIComponent(diagramName);
|
const encodedDiagramName = encodeURIComponent(diagramName);
|
||||||
@@ -433,4 +434,22 @@ export class DataFlowAPI {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 특정 관계도의 모든 관계 조회 (relationship_id로)
|
||||||
|
static async getDiagramRelationshipsByRelationshipId(relationshipId: string): Promise<TableRelationship[]> {
|
||||||
|
try {
|
||||||
|
const response = await apiClient.get<ApiResponse<TableRelationship[]>>(
|
||||||
|
`/dataflow/relationships/${relationshipId}/diagram`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.data.success) {
|
||||||
|
throw new Error(response.data.message || "관계도 관계 조회에 실패했습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data.data || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error("관계도 관계 조회 오류:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user