| import { useEffect, useState } from 'react'; |
| import { motion } from 'framer-motion'; |
| import { checkHealth, type HealthStatus } from '@/api'; |
| import { ShowcaseFlow } from '@/components/ShowcaseFlow'; |
|
|
| function StatusDot({ ok }: { ok: boolean }) { |
| return ( |
| <span |
| className={`h-1.5 w-1.5 shrink-0 rounded-full ${ok ? 'bg-emerald-400 shadow-[0_0_8px_rgba(52,211,153,0.6)]' : 'bg-red-400'}`} |
| aria-hidden |
| /> |
| ); |
| } |
|
|
| export default function App() { |
| const [health, setHealth] = useState<HealthStatus | null>(null); |
| const [healthOk, setHealthOk] = useState(false); |
|
|
| useEffect(() => { |
| checkHealth() |
| .then((h) => { |
| setHealth(h); |
| setHealthOk(h.status === 'healthy'); |
| }) |
| .catch(() => { |
| setHealth(null); |
| setHealthOk(false); |
| }); |
| }, []); |
|
|
| return ( |
| <div className="studio-page-bg"> |
| <header className="studio-header"> |
| <div className="studio-shell py-4 sm:py-5"> |
| <div className="studio-hero-panel"> |
| <div className="max-w-2xl space-y-3"> |
| <span className="studio-kicker">AI launch suite</span> |
| <motion.h1 |
| initial={{ opacity: 0, y: 8 }} |
| animate={{ opacity: 1, y: 0 }} |
| transition={{ duration: 0.35 }} |
| className="font-display text-3xl font-semibold leading-tight tracking-tight text-slate-900 sm:text-4xl" |
| > |
| Product Showcase Studio |
| </motion.h1> |
| <p className="max-w-xl text-sm leading-relaxed text-slate-600"> |
| Transform a product URL into premium ad videos with AI concept ideation, shot planning, keyframe |
| guidance, and one-click master cuts. |
| </p> |
| <div className="flex flex-wrap gap-2"> |
| <span className="studio-pill">AI planning</span> |
| <span className="studio-pill">Concept engine</span> |
| <span className="studio-pill">Fast render stack</span> |
| </div> |
| </div> |
| <div className="studio-health-grid"> |
| {health && ( |
| <> |
| <span className={`studio-status-chip ${healthOk ? 'studio-status-chip-ok' : 'studio-status-chip-error'}`}> |
| <StatusDot ok={healthOk} /> |
| API {healthOk ? 'live' : 'unreachable'} |
| </span> |
| <span className={`studio-status-chip ${health.kie_configured ? '' : 'studio-status-chip-warn'}`}> |
| <StatusDot ok={health.kie_configured} /> |
| KIE |
| </span> |
| {health.replicate_configured && ( |
| <span className="studio-status-chip"> |
| <StatusDot ok={true} /> |
| Replicate |
| </span> |
| )} |
| <span className={`studio-status-chip ${health.openai_configured ? '' : 'studio-status-chip-muted'}`}> |
| <StatusDot ok={health.openai_configured} /> |
| OpenAI |
| </span> |
| {health.ffmpeg_available === false && ( |
| <span className="studio-status-chip studio-status-chip-warn"> |
| FFmpeg missing |
| </span> |
| )} |
| </> |
| )} |
| </div> |
| </div> |
| </div> |
| </header> |
| <main className="studio-shell py-8 sm:py-12"> |
| <ShowcaseFlow health={health} /> |
| </main> |
| <footer className="border-t border-slate-200/80 py-7 text-center text-[0.72rem] text-slate-500"> |
| Product Showcase Studio — local pipeline |
| </footer> |
| </div> |
| ); |
| } |
|
|