[agent-pipeline] pipe-20260315110231-zn60 round-2

This commit is contained in:
DDD1542 2026-03-15 20:14:51 +09:00
parent 558acd1f9b
commit 3ef8cebf1a
1 changed files with 56 additions and 46 deletions

View File

@ -506,21 +506,21 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
return (
<div
className={`group relative flex w-[260px] flex-col overflow-visible rounded-xl border border-border/50 bg-card/80 backdrop-blur-sm shadow-lg ${
// 1. 필터 테이블 (마스터-디테일의 디테일 테이블): 항상 primary 테두리
className={`group relative flex w-[260px] flex-col overflow-visible rounded-[10px] border bg-card/80 backdrop-blur-sm shadow-[0_4px_24px_-8px_rgba(0,0,0,0.5)] ${
// 1. 필터 테이블 (마스터-디테일의 디테일 테이블)
isFilterTable
? "border-2 border-primary ring-2 ring-primary/20 shadow-lg bg-primary/5"
// 2. 필터 관련 테이블 (마스터 또는 디테일) 포커스 시: primary 강조
? "border-primary/30 shadow-[0_0_0_1px_hsl(var(--primary)/0.3)]"
// 2. 필터 관련 테이블 포커스 시
: (hasFilterRelation || isFilterSource)
? "border-2 border-primary ring-4 ring-primary/30 shadow-xl bg-primary/5"
// 3. 순수 포커스 (필터 관계 없음): primary
? "border-primary/30 shadow-[0_0_0_1px_hsl(var(--primary)/0.3),0_0_24px_-8px_hsl(var(--primary)/0.1)]"
// 3. 순수 포커스
: isFocused
? "border-2 border-primary ring-4 ring-primary/30 shadow-xl bg-card"
? "border-primary/30 shadow-[0_0_0_1px_hsl(var(--primary)/0.3),0_0_24px_-8px_hsl(var(--primary)/0.1)] bg-card"
// 4. 흐리게 처리
: isFaded
? "opacity-60 bg-card"
? "opacity-60 bg-card border-border/10"
// 5. 기본
: "hover:shadow-xl hover:ring-2 hover:ring-primary/20"
: "border-border/10 hover:border-border/20"
}`}
style={{
filter: isFaded ? "grayscale(80%)" : "none",
@ -548,7 +548,7 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
type="target"
position={Position.Top}
id="top"
className="!h-2 !w-2 !border-2 !border-background !bg-primary opacity-0 transition-opacity group-hover:opacity-100"
className="!h-2 !w-2 !border-[1.5px] !border-card !bg-muted-foreground/40 opacity-0 transition-opacity group-hover:opacity-100"
/>
{/* top source: 메인테이블 → 메인테이블 연결용 (위쪽으로 나가는 선) */}
<Handle
@ -556,25 +556,25 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
position={Position.Top}
id="top_source"
style={{ top: -4 }}
className="!h-2 !w-2 !border-2 !border-background !bg-warning opacity-0 transition-opacity group-hover:opacity-100"
className="!h-2 !w-2 !border-[1.5px] !border-card !bg-muted-foreground/40 opacity-0 transition-opacity group-hover:opacity-100"
/>
<Handle
type="target"
position={Position.Left}
id="left"
className="!h-2 !w-2 !border-2 !border-background !bg-primary opacity-0 transition-opacity group-hover:opacity-100"
className="!h-2 !w-2 !border-[1.5px] !border-card !bg-muted-foreground/40 opacity-0 transition-opacity group-hover:opacity-100"
/>
<Handle
type="source"
position={Position.Right}
id="right"
className="!h-2 !w-2 !border-2 !border-background !bg-primary opacity-0 transition-opacity group-hover:opacity-100"
className="!h-2 !w-2 !border-[1.5px] !border-card !bg-muted-foreground/40 opacity-0 transition-opacity group-hover:opacity-100"
/>
<Handle
type="source"
position={Position.Bottom}
id="bottom"
className="!h-2 !w-2 !border-2 !border-background !bg-warning opacity-0 transition-opacity group-hover:opacity-100"
className="!h-2 !w-2 !border-[1.5px] !border-card !bg-muted-foreground/40 opacity-0 transition-opacity group-hover:opacity-100"
/>
{/* bottom target: 메인테이블 ← 메인테이블 연결용 (아래에서 들어오는 선) */}
<Handle
@ -582,24 +582,18 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
position={Position.Bottom}
id="bottom_target"
style={{ bottom: -4 }}
className="!h-2 !w-2 !border-2 !border-background !bg-warning opacity-0 transition-opacity group-hover:opacity-100"
className="!h-2 !w-2 !border-[1.5px] !border-card !bg-muted-foreground/40 opacity-0 transition-opacity group-hover:opacity-100"
/>
{/* 헤더 (필터 관계: primary, 필터 소스: primary, 메인: primary, 기본: muted-foreground) - 그라데이션 */}
<div className={`flex items-center gap-2 px-3 py-1.5 text-primary-foreground rounded-t-xl transition-colors duration-700 ease-in-out ${
isFaded
? "bg-gradient-to-r from-muted-foreground to-muted-foreground/70"
: (hasFilterRelation || isFilterSource)
? "bg-gradient-to-r from-primary to-primary/70"
: isMain
? "bg-gradient-to-r from-primary to-primary/70"
: "bg-gradient-to-r from-muted-foreground to-muted-foreground/70"
}`}>
<Database className="h-3.5 w-3.5 shrink-0" />
{/* 헤더: 그라디언트 제거, bg-muted/30 + 아이콘 박스 */}
<div className="flex items-center gap-2.5 px-3.5 py-2.5 border-b border-border/10 bg-muted/30 rounded-t-[10px] transition-colors duration-700 ease-in-out">
<div className="flex h-7 w-7 items-center justify-center rounded-[7px] bg-cyan-500/10 shrink-0">
<Database className="h-3.5 w-3.5 text-cyan-400" />
</div>
<div className="flex-1 min-w-0">
<div className="truncate text-[11px] font-semibold">{label}</div>
<div className="truncate text-[11px] font-semibold text-foreground font-mono">{label}</div>
{/* 필터 관계에 따른 문구 변경 */}
<div className="truncate text-[9px] opacity-80">
<div className="truncate text-[9px] font-mono text-muted-foreground/40 tracking-[-0.3px]">
{isFilterSource
? "마스터 테이블 (필터 소스)"
: hasFilterRelation
@ -608,8 +602,8 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
</div>
</div>
{hasActiveColumns && (
<span className="rounded-full bg-primary-foreground/25 backdrop-blur-sm border border-primary-foreground/10 px-1.5 py-0.5 text-[8px] shrink-0">
{displayColumns.length}
<span className="text-[9px] font-mono text-muted-foreground/40 px-1.5 py-0.5 rounded bg-foreground/5 border border-border/10 tracking-[-0.3px] shrink-0">
{displayColumns.length} ref
</span>
)}
</div>
@ -697,18 +691,22 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
opacity: hasActiveColumns ? 0 : 1,
}}
>
{/* PK/FK/조인/필터 아이콘 */}
{isJoinColumn && <Link2 className="h-2.5 w-2.5 text-warning" />}
{(isFilterColumn || isFilterSourceColumn) && !isJoinColumn && <Link2 className="h-2.5 w-2.5 text-primary" />}
{!isJoinColumn && !isFilterColumn && !isFilterSourceColumn && col.isPrimaryKey && <Key className="h-2.5 w-2.5 text-warning" />}
{!isJoinColumn && !isFilterColumn && !isFilterSourceColumn && col.isForeignKey && !col.isPrimaryKey && <Link2 className="h-2.5 w-2.5 text-primary" />}
{!isJoinColumn && !isFilterColumn && !isFilterSourceColumn && !col.isPrimaryKey && !col.isForeignKey && <div className="w-2.5" />}
{/* 3px 세로 마커 (PK/FK/조인/필터) */}
<div
className={`w-[3px] h-[14px] rounded-sm flex-shrink-0 ${
isJoinColumn ? "bg-amber-400"
: (isFilterColumn || isFilterSourceColumn) ? "bg-primary opacity-80"
: col.isPrimaryKey ? "bg-amber-400"
: col.isForeignKey ? "bg-primary opacity-80"
: "bg-muted-foreground/20"
}`}
/>
{/* 컬럼명 */}
<span className={`flex-1 truncate font-mono text-[9px] font-medium ${
isJoinColumn ? "text-warning"
isJoinColumn ? "text-amber-400"
: (isFilterColumn || isFilterSourceColumn) ? "text-primary"
: isHighlighted ? "text-primary"
: isHighlighted ? "text-primary"
: "text-foreground"
}`}>
{col.name}
@ -749,7 +747,7 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
)}
{/* 타입 */}
<span className="text-[8px] text-muted-foreground">{col.type}</span>
<span className="text-[8px] text-muted-foreground/30 font-mono tracking-[-0.3px]">{col.type}</span>
</div>
);
})}
@ -768,13 +766,25 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
)}
</div>
{/* 푸터 (컴팩트) */}
<div className="flex items-center justify-end border-t border-border bg-muted/30 px-2 py-1">
{columns && (
<span className="text-[9px] text-muted-foreground">
{hasActiveColumns ? `${displayColumns.length}/${totalCount}` : totalCount}
</span>
)}
{/* 푸터: cols + PK/FK 카운트 */}
<div className="flex items-center justify-between border-t border-border/10 px-3.5 py-1.5 bg-background/50">
<span className="text-[9px] text-muted-foreground/40 font-mono tracking-[-0.3px]">
{hasActiveColumns ? `${displayColumns.length}/${totalCount}` : totalCount} cols
</span>
<div className="flex gap-2.5 text-[9px] font-mono tracking-[-0.3px]">
{columns?.some(c => c.isPrimaryKey) && (
<span className="flex items-center gap-1">
<span className="w-1 h-1 rounded-full bg-amber-400" />
<span className="text-muted-foreground/40">PK {columns.filter(c => c.isPrimaryKey).length}</span>
</span>
)}
{columns?.some(c => c.isForeignKey) && (
<span className="flex items-center gap-1">
<span className="w-1 h-1 rounded-full bg-primary" />
<span className="text-muted-foreground/40">FK {columns.filter(c => c.isForeignKey).length}</span>
</span>
)}
</div>
</div>
{/* CSS 애니메이션 정의 */}