feat(web-ui): PipelineDetailModal + 카드 mini 미리보기
This commit is contained in:
117
src/pages/music/components/PipelineDetailModal.jsx
Normal file
117
src/pages/music/components/PipelineDetailModal.jsx
Normal file
@@ -0,0 +1,117 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user