118 lines
5.5 KiB
JavaScript
118 lines
5.5 KiB
JavaScript
const fmtTimestamp = (sec) => {
|
||
if (sec == null) return '';
|
||
const total = Math.floor(sec);
|
||
const h = Math.floor(total / 3600);
|
||
const m = Math.floor((total % 3600) / 60);
|
||
const s = total % 60;
|
||
if (h) return `${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`;
|
||
return `${m}:${String(s).padStart(2,'0')}`;
|
||
};
|
||
|
||
export default function PipelineDetailModal({ pipeline, onClose }) {
|
||
if (!pipeline) return null;
|
||
const meta = pipeline.metadata || {};
|
||
const review = pipeline.review || {};
|
||
|
||
return (
|
||
<div className="modal-overlay" onClick={onClose}>
|
||
<div className="modal-body modal-body--lg" onClick={e => e.stopPropagation()}>
|
||
<header className="pdm-header">
|
||
<h3>{pipeline.compile_title || pipeline.track_title || `Pipeline #${pipeline.id}`}</h3>
|
||
<span className="pdm-badge">{pipeline.visual_style || 'essential'}</span>
|
||
<button onClick={onClose} className="pdm-close" aria-label="close">×</button>
|
||
</header>
|
||
|
||
<div className="pdm-grid">
|
||
{pipeline.cover_url && (
|
||
<figure className="pdm-figure">
|
||
<img src={pipeline.cover_url} alt="cover" />
|
||
<figcaption>커버 (배경)</figcaption>
|
||
</figure>
|
||
)}
|
||
{pipeline.thumbnail_url && (
|
||
<figure className="pdm-figure">
|
||
<img src={pipeline.thumbnail_url} alt="thumbnail" />
|
||
<figcaption>썸네일</figcaption>
|
||
</figure>
|
||
)}
|
||
</div>
|
||
|
||
{pipeline.video_url && (
|
||
<div className="pdm-video">
|
||
<video src={pipeline.video_url} controls preload="metadata" width="100%" />
|
||
</div>
|
||
)}
|
||
|
||
{meta.title && (
|
||
<section className="pdm-section">
|
||
<h4>메타데이터</h4>
|
||
<p><strong>제목:</strong> {meta.title}</p>
|
||
<details>
|
||
<summary>설명 ({(meta.description || '').length}자)</summary>
|
||
<pre className="pdm-pre">{meta.description}</pre>
|
||
</details>
|
||
<p><strong>태그:</strong> {(meta.tags || []).join(', ')}</p>
|
||
</section>
|
||
)}
|
||
|
||
{review.weighted_total != null && (
|
||
<section className="pdm-section">
|
||
<h4>
|
||
AI 검토
|
||
<span className={`pdm-verdict pdm-verdict--${review.verdict}`}>
|
||
{review.verdict}
|
||
</span>
|
||
<span className="pdm-score">({review.weighted_total}/100)</span>
|
||
</h4>
|
||
<table className="pdm-review-table">
|
||
<tbody>
|
||
<tr><td>메타데이터 품질</td><td>{review.metadata_quality?.score}</td></tr>
|
||
<tr><td>콘텐츠 정책</td><td>{review.policy_compliance?.score}</td></tr>
|
||
<tr><td>시청 경험</td><td>{review.viewer_experience?.score}</td></tr>
|
||
<tr><td>트렌드 정렬</td><td>{review.trend_alignment?.score}</td></tr>
|
||
</tbody>
|
||
</table>
|
||
{review.summary && <p className="pdm-summary"><em>{review.summary}</em></p>}
|
||
</section>
|
||
)}
|
||
|
||
{pipeline.tracks && pipeline.tracks.length > 1 && (
|
||
<section className="pdm-section">
|
||
<h4>트랙 리스트 ({pipeline.tracks.length})</h4>
|
||
<ol className="pdm-tracks">
|
||
{pipeline.tracks.map(t => (
|
||
<li key={t.id}>
|
||
<span className="pdm-track-time">[{fmtTimestamp(t.start_offset_sec)}]</span>
|
||
{' '}{t.title}
|
||
<span className="pdm-track-dur"> ({fmtTimestamp(t.duration_sec)})</span>
|
||
</li>
|
||
))}
|
||
</ol>
|
||
</section>
|
||
)}
|
||
|
||
{pipeline.feedback && pipeline.feedback.length > 0 && (
|
||
<section className="pdm-section">
|
||
<h4>피드백 히스토리 ({pipeline.feedback.length})</h4>
|
||
<ul className="pdm-feedback">
|
||
{pipeline.feedback.map(f => (
|
||
<li key={f.id}>
|
||
<code>[{f.step}]</code> {f.feedback_text}
|
||
<small> {(f.received_at || '').replace('T', ' ')}</small>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</section>
|
||
)}
|
||
|
||
{pipeline.youtube_video_id && (
|
||
<a href={`https://youtu.be/${pipeline.youtube_video_id}`}
|
||
target="_blank" rel="noreferrer" className="pdm-youtube">
|
||
🎬 YouTube에서 보기
|
||
</a>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|