279 lines
10 KiB
TypeScript
279 lines
10 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { ScreenDefinition } from "@/types/screen";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Separator } from "@/components/ui/separator";
|
|
import { Database, Monitor, ArrowRight, Link2, Table, Columns, ExternalLink, Layers, GitBranch } from "lucide-react";
|
|
import {
|
|
getFieldJoins,
|
|
getDataFlows,
|
|
getTableRelations,
|
|
FieldJoin,
|
|
DataFlow,
|
|
TableRelation,
|
|
} from "@/lib/api/screenGroup";
|
|
import { screenApi } from "@/lib/api/screen";
|
|
|
|
interface ScreenRelationViewProps {
|
|
screen: ScreenDefinition | null;
|
|
}
|
|
|
|
export function ScreenRelationView({ screen }: ScreenRelationViewProps) {
|
|
const [loading, setLoading] = useState(false);
|
|
const [fieldJoins, setFieldJoins] = useState<FieldJoin[]>([]);
|
|
const [dataFlows, setDataFlows] = useState<DataFlow[]>([]);
|
|
const [tableRelations, setTableRelations] = useState<TableRelation[]>([]);
|
|
const [layoutInfo, setLayoutInfo] = useState<any>(null);
|
|
|
|
useEffect(() => {
|
|
const loadRelations = async () => {
|
|
if (!screen) {
|
|
setFieldJoins([]);
|
|
setDataFlows([]);
|
|
setTableRelations([]);
|
|
setLayoutInfo(null);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setLoading(true);
|
|
|
|
// 병렬로 데이터 로드
|
|
const [joinsRes, flowsRes, relationsRes, layoutRes] = await Promise.all([
|
|
getFieldJoins(screen.screenId),
|
|
getDataFlows(screen.screenId),
|
|
getTableRelations(screen.screenId),
|
|
screenApi.getLayout(screen.screenId).catch(() => null),
|
|
]);
|
|
|
|
if (joinsRes.success && joinsRes.data) {
|
|
setFieldJoins(joinsRes.data);
|
|
}
|
|
if (flowsRes.success && flowsRes.data) {
|
|
setDataFlows(flowsRes.data);
|
|
}
|
|
if (relationsRes.success && relationsRes.data) {
|
|
setTableRelations(relationsRes.data);
|
|
}
|
|
if (layoutRes) {
|
|
setLayoutInfo(layoutRes);
|
|
}
|
|
} catch (error) {
|
|
console.error("관계 정보 로드 실패:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
loadRelations();
|
|
}, [screen?.screenId]);
|
|
|
|
if (!screen) {
|
|
return (
|
|
<div className="flex h-full flex-col items-center justify-center py-12 text-center">
|
|
<Layers className="text-muted-foreground/30 mb-4 h-16 w-16" />
|
|
<h3 className="text-muted-foreground mb-2 text-lg font-medium">화면을 선택하세요</h3>
|
|
<p className="text-muted-foreground/70 text-sm">왼쪽 트리에서 화면을 선택하면 데이터 관계가 표시됩니다</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center p-8">
|
|
<div className="text-muted-foreground text-sm">로딩 중...</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 컴포넌트에서 사용하는 테이블 분석
|
|
const getUsedTables = () => {
|
|
const tables = new Set<string>();
|
|
if (screen.tableName) {
|
|
tables.add(screen.tableName);
|
|
}
|
|
if (layoutInfo?.components) {
|
|
layoutInfo.components.forEach((comp: any) => {
|
|
if (comp.properties?.tableName) {
|
|
tables.add(comp.properties.tableName);
|
|
}
|
|
if (comp.properties?.dataSource?.tableName) {
|
|
tables.add(comp.properties.dataSource.tableName);
|
|
}
|
|
});
|
|
}
|
|
return Array.from(tables);
|
|
};
|
|
|
|
const usedTables = getUsedTables();
|
|
|
|
return (
|
|
<div className="h-full space-y-4 overflow-auto p-4">
|
|
{/* 화면 기본 정보 */}
|
|
<div className="flex items-start gap-4">
|
|
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-blue-500/10">
|
|
<Monitor className="h-6 w-6 text-blue-500" />
|
|
</div>
|
|
<div className="min-w-0 flex-1">
|
|
<h3 className="truncate text-lg font-semibold">{screen.screenName}</h3>
|
|
<div className="mt-1 flex items-center gap-2">
|
|
<Badge variant="outline">{screen.screenCode}</Badge>
|
|
<Badge variant="secondary">{screen.screenType}</Badge>
|
|
</div>
|
|
{screen.description && (
|
|
<p className="text-muted-foreground mt-2 line-clamp-2 text-sm">{screen.description}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* 연결된 테이블 */}
|
|
<Card>
|
|
<CardHeader className="px-4 py-3">
|
|
<CardTitle className="flex items-center gap-2 text-sm font-medium">
|
|
<Database className="h-4 w-4 text-green-500" />
|
|
연결된 테이블
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="px-4 pt-0 pb-4">
|
|
{usedTables.length > 0 ? (
|
|
<div className="space-y-2">
|
|
{usedTables.map((tableName, index) => (
|
|
<div key={index} className="bg-muted/50 flex items-center gap-2 rounded-md p-2">
|
|
<Table className="text-muted-foreground h-4 w-4" />
|
|
<span className="font-mono text-sm">{tableName}</span>
|
|
{tableName === screen.tableName && (
|
|
<Badge variant="default" className="ml-auto text-xs">
|
|
메인
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-muted-foreground text-sm">연결된 테이블이 없습니다</p>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 필드 조인 관계 */}
|
|
<Card>
|
|
<CardHeader className="px-4 py-3">
|
|
<CardTitle className="flex items-center gap-2 text-sm font-medium">
|
|
<Link2 className="h-4 w-4 text-purple-500" />
|
|
필드 조인 관계
|
|
{fieldJoins.length > 0 && (
|
|
<Badge variant="secondary" className="ml-auto">
|
|
{fieldJoins.length}
|
|
</Badge>
|
|
)}
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="px-4 pt-0 pb-4">
|
|
{fieldJoins.length > 0 ? (
|
|
<div className="space-y-2">
|
|
{fieldJoins.map((join) => (
|
|
<div key={join.id} className="bg-muted/50 flex items-center gap-2 rounded-md p-2 text-sm">
|
|
<div className="flex items-center gap-1">
|
|
<span className="font-mono text-xs">{join.sourceTable}</span>
|
|
<span className="text-muted-foreground">.</span>
|
|
<span className="font-mono text-xs text-blue-600">{join.sourceColumn}</span>
|
|
</div>
|
|
<ArrowRight className="text-muted-foreground h-3 w-3" />
|
|
<div className="flex items-center gap-1">
|
|
<span className="font-mono text-xs">{join.targetTable}</span>
|
|
<span className="text-muted-foreground">.</span>
|
|
<span className="font-mono text-xs text-green-600">{join.targetColumn}</span>
|
|
</div>
|
|
<Badge variant="outline" className="ml-auto text-xs">
|
|
{join.joinType}
|
|
</Badge>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-muted-foreground text-sm">설정된 조인이 없습니다</p>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 데이터 흐름 */}
|
|
<Card>
|
|
<CardHeader className="px-4 py-3">
|
|
<CardTitle className="flex items-center gap-2 text-sm font-medium">
|
|
<GitBranch className="h-4 w-4 text-orange-500" />
|
|
데이터 흐름
|
|
{dataFlows.length > 0 && (
|
|
<Badge variant="secondary" className="ml-auto">
|
|
{dataFlows.length}
|
|
</Badge>
|
|
)}
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="px-4 pt-0 pb-4">
|
|
{dataFlows.length > 0 ? (
|
|
<div className="space-y-2">
|
|
{dataFlows.map((flow) => (
|
|
<div key={flow.id} className="bg-muted/50 flex items-center gap-2 rounded-md p-2 text-sm">
|
|
<Monitor className="h-4 w-4 text-blue-500" />
|
|
<span className="truncate">{flow.flowName || "이름 없음"}</span>
|
|
<ArrowRight className="text-muted-foreground h-3 w-3" />
|
|
<Monitor className="h-4 w-4 text-green-500" />
|
|
<span className="text-muted-foreground truncate">화면 #{flow.targetScreenId}</span>
|
|
<Badge variant="outline" className="ml-auto text-xs">
|
|
{flow.flowType}
|
|
</Badge>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-muted-foreground text-sm">설정된 데이터 흐름이 없습니다</p>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 테이블 관계 */}
|
|
<Card>
|
|
<CardHeader className="px-4 py-3">
|
|
<CardTitle className="flex items-center gap-2 text-sm font-medium">
|
|
<Columns className="h-4 w-4 text-cyan-500" />
|
|
테이블 관계
|
|
{tableRelations.length > 0 && (
|
|
<Badge variant="secondary" className="ml-auto">
|
|
{tableRelations.length}
|
|
</Badge>
|
|
)}
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="px-4 pt-0 pb-4">
|
|
{tableRelations.length > 0 ? (
|
|
<div className="space-y-2">
|
|
{tableRelations.map((relation) => (
|
|
<div key={relation.id} className="bg-muted/50 flex items-center gap-2 rounded-md p-2 text-sm">
|
|
<Table className="text-muted-foreground h-4 w-4" />
|
|
<span className="font-mono text-xs">{relation.parentTable}</span>
|
|
<ArrowRight className="text-muted-foreground h-3 w-3" />
|
|
<span className="font-mono text-xs">{relation.childTable}</span>
|
|
<Badge variant="outline" className="ml-auto text-xs">
|
|
{relation.relationType}
|
|
</Badge>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-muted-foreground text-sm">설정된 테이블 관계가 없습니다</p>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 빠른 작업 */}
|
|
<div className="border-t pt-2">
|
|
<p className="text-muted-foreground mb-2 text-xs">더블클릭하면 화면 디자이너로 이동합니다</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|