64 lines
2.3 KiB
TypeScript
64 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import type { Job } from "@/lib/api";
|
|
|
|
/* 프로덕트 wizard-stepper 패턴: 완료=민트 체크, 현재=민트 링+글로우, 대기=회색 번호 */
|
|
|
|
const STAGE_LABELS: Record<string, string> = {
|
|
detect: "분석",
|
|
narration_text: "나레이션",
|
|
tts: "음성",
|
|
bgm: "음악",
|
|
motion: "모션 선정",
|
|
i2v: "애니메이션",
|
|
render: "렌더",
|
|
transfer: "변환",
|
|
};
|
|
|
|
const Check = () => (
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M20 6 9 17l-5-5" />
|
|
</svg>
|
|
);
|
|
|
|
export default function StageStepper({ job, labels }: { job: Pick<Job, "stages" | "status">; labels?: Record<string, string> }) {
|
|
const L = labels ?? STAGE_LABELS;
|
|
const entries = Object.entries(job.stages);
|
|
// 검수 대기 중에는 "아직 끝나지 않은 첫 스테이지"가 현재다.
|
|
// 스테이지명을 박아두면 게이트가 늘 때마다 여기가 조용히 틀어진다.
|
|
const firstPending = entries.find(([, st]) => st.status !== "done")?.[0];
|
|
|
|
return (
|
|
<div className="wizard-stepper">
|
|
{entries.map(([key, st], i) => {
|
|
const cls =
|
|
st.status === "done" ? "done" :
|
|
st.status === "failed" ? "failed" :
|
|
st.status === "running" || (job.status === "awaiting_review" && key === firstPending) ? "current" :
|
|
"pending";
|
|
return (
|
|
<div key={key} style={{ display: "contents" }}>
|
|
{i > 0 && (
|
|
<div className={entries[i - 1][1].status === "done" ? "wizard-step-line done" : "wizard-step-line"} />
|
|
)}
|
|
<div className={`wizard-step ${cls}`}>
|
|
<div className="wizard-stepper-node">
|
|
{st.status === "done" ? <Check /> : i + 1}
|
|
</div>
|
|
<div className="wizard-stepper-label">{L[key] ?? key}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/** 스테이지 진행률 (%) — 선형 진행바용 */
|
|
export function stageProgress(job: Pick<Job, "stages">): number {
|
|
const sts = Object.values(job.stages);
|
|
const done = sts.filter((s) => s.status === "done").length;
|
|
const running = sts.some((s) => s.status === "running") ? 0.5 : 0;
|
|
return Math.round(((done + running) / sts.length) * 100);
|
|
}
|