'use client'; import React from 'react'; import styled, { keyframes } from 'styled-components'; export interface PipelineNode { id: string; label: string; role: string; state: 'idle' | 'active' | 'review' | 'blocked' | 'ready'; detail: string; } interface ActivePipelineProps { activeTask: string; focus: string; reviewLoopCount: number; escalationCount: number; deployState: string; nodes: PipelineNode[]; } const flow = keyframes` 0% { transform: translateX(0); opacity: 0.15; } 40% { opacity: 0.8; } 100% { transform: translateX(20px); opacity: 0; } `; const PipelineCard = styled.section` border: 1px solid var(--border-color); background: var(--bg-surface); padding: var(--space-lg); display: flex; flex-direction: column; gap: var(--space-lg); `; const Header = styled.div` display: flex; justify-content: space-between; gap: var(--space-md); align-items: flex-start; flex-wrap: wrap; `; const Eyebrow = styled.div` font-family: var(--font-mono); font-size: 11px; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; `; const Title = styled.h2` font-size: 24px; font-weight: 600; color: var(--text-primary); letter-spacing: -0.02em; margin-top: 6px; `; const Summary = styled.p` font-size: 13px; color: var(--text-secondary); line-height: 1.6; max-width: 780px; text-transform: uppercase; `; const HeaderStats = styled.div` display: grid; grid-template-columns: repeat(3, minmax(110px, 1fr)); gap: var(--space-sm); @media (max-width: 767px) { width: 100%; grid-template-columns: repeat(3, minmax(0, 1fr)); } `; const StatBox = styled.div` border: 1px solid var(--border-color); padding: 10px 12px; display: flex; flex-direction: column; gap: 4px; min-width: 0; `; const StatLabel = styled.span` font-family: var(--font-mono); font-size: 10px; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; `; const StatValue = styled.span` font-size: 15px; color: var(--text-primary); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; `; const FlowMap = styled.div` display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 14px; align-items: stretch; @media (max-width: 1199px) { grid-template-columns: 1fr; } `; const NodeWrap = styled.div` display: flex; align-items: center; gap: 10px; @media (max-width: 1199px) { flex-direction: column; align-items: stretch; } `; const Connector = styled.div<{ $active?: boolean }>` flex: 1; min-width: 36px; height: 1px; background: ${({ $active }) => ($active ? 'rgba(245,245,245,0.45)' : 'var(--border-color)')}; position: relative; overflow: hidden; &::after { content: ''; position: absolute; top: -2px; left: -18px; width: 18px; height: 5px; background: ${({ $active }) => ($active ? 'rgba(245,245,245,0.85)' : 'transparent')}; animation: ${({ $active }) => ($active ? flow : 'none')} 1.6s linear infinite; } @media (max-width: 1199px) { width: 1px; height: 24px; min-width: 1px; margin: 0 auto; &::after { top: -8px; left: -2px; width: 5px; height: 10px; animation: none; } } `; const stateBorder: Record = { idle: 'var(--border-color)', active: '#6fc3ff', review: '#ff7ac6', blocked: '#ff8d7a', ready: '#8dffb2', }; const stateText: Record = { idle: 'IDLE', active: 'ACTIVE', review: 'REVIEW', blocked: 'BLOCKED', ready: 'READY', }; const NodeCard = styled.div<{ $state: PipelineNode['state'] }>` border: 1px solid ${({ $state }) => stateBorder[$state]}; padding: 14px; display: flex; flex-direction: column; gap: 10px; min-height: 148px; background: linear-gradient(180deg, rgba(255,255,255,0.015) 0%, rgba(255,255,255,0) 100%); `; const NodeTop = styled.div` display: flex; justify-content: space-between; gap: var(--space-sm); align-items: flex-start; `; const NodeLabel = styled.div` display: flex; flex-direction: column; gap: 4px; `; const NodeName = styled.div` font-size: 16px; font-weight: 600; color: var(--text-primary); `; const NodeRole = styled.div` font-size: 12px; color: var(--text-secondary); `; const StateBadge = styled.span<{ $state: PipelineNode['state'] }>` font-family: var(--font-mono); font-size: 10px; color: ${({ $state }) => stateBorder[$state]}; border: 1px solid ${({ $state }) => stateBorder[$state]}; padding: 4px 6px; text-transform: uppercase; letter-spacing: 0.08em; white-space: nowrap; `; const NodeDetail = styled.div` font-size: 13px; line-height: 1.6; color: var(--text-primary); `; const NodeHint = styled.div` margin-top: auto; font-family: var(--font-mono); font-size: 11px; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; `; export default function ActivePipeline({ activeTask, focus, reviewLoopCount, escalationCount, deployState, nodes, }: ActivePipelineProps) { return (
Active pipeline / snapshot {activeTask} {focus}
review loop {reviewLoopCount}x escalations {escalationCount} deploy state {deployState}
{nodes.map((node, index) => { const connectorActive = node.state === 'active' || node.state === 'review' || node.state === 'ready'; return ( {node.label} {node.role} {stateText[node.state]} {node.detail} snapshot {String(index + 1).padStart(2, '0')} {index < nodes.length - 1 && } ); })}
); }