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