+
e.stopPropagation()}>
+
+ {pipeline.compile_title || pipeline.track_title || `Pipeline #${pipeline.id}`}
+ {pipeline.visual_style || 'essential'}
+
+
+
+
+ {pipeline.cover_url && (
+
+
+ 커버 (배경)
+
+ )}
+ {pipeline.thumbnail_url && (
+
+
+ 썸네일
+
+ )}
+
+
+ {pipeline.video_url && (
+
+
+
+ )}
+
+ {meta.title && (
+
+ 메타데이터
+ 제목: {meta.title}
+
+ 설명 ({(meta.description || '').length}자)
+ {meta.description}
+
+ 태그: {(meta.tags || []).join(', ')}
+
+ )}
+
+ {review.weighted_total != null && (
+
+ AI 검토
+ {review.verdict}
+ ({review.weighted_total}/100)
+
+
+
+ | 메타데이터 품질 | {review.metadata_quality?.score} |
+ | 콘텐츠 정책 | {review.policy_compliance?.score} |
+ | 시청 경험 | {review.viewer_experience?.score} |
+ | 트렌드 정렬 | {review.trend_alignment?.score} |
+
+
+ {review.summary && {review.summary}
}
+
+ )}
+
+ {pipeline.tracks && pipeline.tracks.length > 1 && (
+
+ 트랙 리스트 ({pipeline.tracks.length})
+
+ {pipeline.tracks.map(t => (
+ -
+ [{fmtTimestamp(t.start_offset_sec)}]
+ {' '}{t.title}
+ ({fmtDuration(t.duration_sec)})
+
+ ))}
+
+
+ )}
+
+ {pipeline.feedback && pipeline.feedback.length > 0 && (
+
+ 피드백 히스토리 ({pipeline.feedback.length})
+
+ {pipeline.feedback.map(f => (
+ -
+
[{f.step}] {f.feedback_text}
+ {f.received_at?.replace('T', ' ')}
+
+ ))}
+
+
+ )}
+
+ {pipeline.youtube_video_id && (
+
+ 🎬 YouTube에서 보기
+
+ )}
+
+
+ );
+}
+```
+
+- [ ] **Step 2: PipelineCard.jsx — 미리보기 + 클릭 핸들러**
+
+```jsx
+import { useState } from 'react';
+import { cancelPipeline, publishPipeline } from '../../../api';
+import PipelineDetailModal from './PipelineDetailModal';
+
+const STEP_LABELS = ['커버','영상','썸네','메타','검토','발행'];
+
+function stepIndex(state) {
+ if (!state) return -1;
+ if (state.startsWith('cover')) return 0;
+ if (state.startsWith('video')) return 1;
+ if (state.startsWith('thumb')) return 2;
+ if (state.startsWith('meta')) return 3;
+ if (state.startsWith('ai_review') || state === 'publish_pending') return 4;
+ if (state.startsWith('publish')) return 5;
+ if (state === 'published') return 6;
+ return -1;
+}
+
+export default function PipelineCard({ pipeline, onChanged }) {
+ const [showDetail, setShowDetail] = useState(false);
+ const i = stepIndex(pipeline.state);
+ const title = pipeline.compile_title || pipeline.track_title || `Pipeline #${pipeline.id}`;
+
+ const handleCardClick = (e) => {
+ // 버튼/링크 클릭은 무시
+ if (e.target.closest('button') || e.target.closest('a')) return;
+ setShowDetail(true);
+ };
+
+ return (
+ <>
+
+
+
{title}
+ {pipeline.visual_style && (
+ {pipeline.visual_style}
+ )}
+ {!['published','cancelled','failed'].includes(pipeline.state) && (
+
+ )}
+
+
+ {/* 미니 미리보기 */}
+
+ {pipeline.cover_url && (
+

+ )}
+ {pipeline.thumbnail_url && (
+

+ )}
+ {pipeline.video_url &&
▶}
+
+
+
+ {STEP_LABELS.map((lbl, idx) => (
+
+ {lbl}
+
+ ))}
+
+
+
현재: {pipeline.state}
+
+ {pipeline.review && (
+
+ AI 검토: {pipeline.review.verdict}
+ ({pipeline.review.weighted_total}/100)
+
+ )}
+
+ {pipeline.state === 'publish_pending' && (
+
+ )}
+
+ {pipeline.youtube_video_id && (
+
+ 유튜브에서 보기
+
+ )}
+
+
+ {showDetail &&