"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { apiFetch, type Job } from "@/lib/api"; import { GATE_META, type PlayreelJob } from "@/lib/playreel"; /* 무빙포스터 진입 — 두 갈래 (MOVING_POSTER_ENTRY_PLAN.md §1·§5) */ const ENTRIES = [ { href: "/poster", icon: "IMG", title: "이미지로 시작하기", desc: "포스터 한 장이 첫 프레임 그대로 살아나는 8~15초 무빙포스터", flow: ["포스터 원본이 첫 프레임", "불꽃·물결·조명 등 요소만 움직임", "제목·일시 나레이션 + 음악"], facts: [["필요한 것", "이미지 1장"], ["확인", "1회"], ["시간", "약 3분"], ["비용", "14크레딧"]], cta: "포스터 올리기", hot: false, badge: null, }, { href: "/playreel", icon: "URL", title: "공연상품페이지로 시작하기", desc: "상품페이지 주소 하나로 캐스팅·일정·줄거리까지 담긴 30초 예고편", flow: ["포스터 무빙 훅 8초", "상세페이지 스크롤 (줄거리·캐스트·캐스팅 스케줄·할인)", "예매 안내 밴드 + QR"], facts: [["필요한 것", "상품페이지 URL"], ["확인", "5회"], ["시간", "약 15분"], ["비용", "16크레딧"]], cta: "주소 넣기", hot: true, badge: "상세페이지까지", }, ] as const; type Recent = { id: string; name: string; href: string; kind: "이미지" | "상품페이지"; label: string; tone: string }; const F1_STATUS: Record = { queued: "대기 중", running: "만드는 중", awaiting_review: "검수 대기", failed: "실패", done: "완료", }; function tone(status: string) { return status === "failed" ? "#ff7a7a" : status === "done" ? "var(--color-mint)" : status === "awaiting_review" ? "#ffd27a" : "var(--color-text-gray-400)"; } export default function HomePage() { const [recent, setRecent] = useState([]); useEffect(() => { // 두 갈래의 최근 작업을 합쳐 최신순 6개. 한쪽 API가 없어도(playreel 미구현) 다른 쪽은 보인다. Promise.all([ apiFetch("/api/f1/jobs").catch(() => [] as Job[]), apiFetch("/api/playreel/jobs").catch(() => [] as PlayreelJob[]), ]).then(([f1, pr]) => { const a: (Recent & { t: number })[] = [ ...f1.map((j) => ({ id: j.id, name: j.name, href: `/poster/${j.id}`, kind: "이미지" as const, label: F1_STATUS[j.status] ?? j.status, tone: tone(j.status), t: j.created_at })), ...pr.map((j) => ({ id: j.id, name: j.name, href: `/playreel/${j.id}`, kind: "상품페이지" as const, label: j.status === "awaiting_review" && j.gate ? `${GATE_META[j.gate].name} 대기` : (F1_STATUS[j.status] ?? j.status), tone: tone(j.status), t: j.created_at })), ]; setRecent(a.sort((x, y) => y.t - x.t).slice(0, 6)); }); }, []); return (

어떻게 시작할까요?

가진 것에 따라 고르세요. 둘 다 9:16 세로 영상으로 완성됩니다.

{ENTRIES.map((e) => (
{e.badge && {e.badge}}
{e.icon}

{e.title}

{e.desc}

{e.flow.map((f, i) => ( {i > 0 && } {f} ))}
{e.facts.map(([k, v]) => (
{k}{v}
))}
{e.cta}
))}
{recent.length > 0 && (

최근 작업

{recent.map((j) => (
{j.kind}

{j.name}

{j.label}

))}
)}
); }