/* global React */ const _ULA = window.BOLDER_LOCAL_ASSETS || 'assets/'; const DEMO_IMGS = [ _ULA + 'upload-demo/img-1.jpg', _ULA + 'upload-demo/img-2.jpg', _ULA + 'upload-demo/img-3.jpg', _ULA + 'upload-demo/img-4.webp', ]; const OUTPUT_IMG = _ULA + 'upload-demo/output.png'; function UploadDemo() { // phase 0=idle 1=uploading 2=sent 3..6=steps lighting up 7=all done const [phase, setPhase] = React.useState(0); const [visible, setVisible] = React.useState([]); const [progress, setProgress] = React.useState(0); const [hiddenSteps, setHiddenSteps] = React.useState([]); // indices of collapsed steps const [showOutput, setShowOutput] = React.useState(false); const timerRef = React.useRef([]); function clearAll() { timerRef.current.forEach(clearTimeout); timerRef.current = []; } function runDemo() { clearAll(); setPhase(1); setVisible([]); setProgress(0); setHiddenSteps([]); setShowOutput(false); DEMO_IMGS.forEach((_, i) => { const t = setTimeout(() => { setVisible(v => [...v, i]); setProgress(Math.round(((i + 1) / DEMO_IMGS.length) * 100)); }, 300 + i * 450); timerRef.current.push(t); }); const B = 300 + DEMO_IMGS.length * 450; // base offset after uploads [ [B + 400, () => setPhase(2)], [B + 1000, () => setPhase(3)], [B + 1900, () => setPhase(4)], [B + 2800, () => setPhase(5)], [B + 3700, () => setPhase(6)], [B + 4400, () => setPhase(7)], // steps disappear one by one [B + 5000, () => setHiddenSteps([0])], [B + 5220, () => setHiddenSteps([0, 1])], [B + 5440, () => setHiddenSteps([0, 1, 2])], [B + 5660, () => setHiddenSteps([0, 1, 2, 3])], // output slides in [B + 5950, () => setShowOutput(true)], // reset [B + 10000, () => { setPhase(0); setVisible([]); setProgress(0); setHiddenSteps([]); setShowOutput(false); }], ].forEach(([delay, fn]) => { const t = setTimeout(fn, delay); timerRef.current.push(t); }); } React.useEffect(() => { const start = setTimeout(runDemo, 800); return () => { clearAll(); clearTimeout(start); }; }, []); // eslint-disable-line react-hooks/exhaustive-deps React.useEffect(() => { if (phase === 0) { const t = setTimeout(runDemo, 700); timerRef.current.push(t); } }, [phase]); // eslint-disable-line react-hooks/exhaustive-deps const steps = [ { n: "01", k: "Ticket criado", d: "Pedido entra na fila com prioridade e SLA definidos.", activeAt: 3 }, { n: "02", k: "Blueprint analisa", d: "O modelo proprietário interpreta o briefing e aplica o estilo da marca.", activeAt: 4 }, { n: "03", k: "Produção arranca", d: "IA gera, humano valida. Cada decisão é registada.", activeAt: 5 }, { n: "04", k: "Entrega + revisão", d: "Assets entregues na plataforma. 2 rondas de revisão incluídas.", activeAt: 6 }, ]; return (
{/* ── ESQUERDA ── */}
= 2 ? "#22c55e" : "var(--coral)" }} /> {phase >= 2 ? "Pedido enviado" : "Novo Pedido · Cliente"} BLDR-REQ-2026
= 2 ? "#22c55e" : "var(--rule-strong)"}`, borderRadius: 10, minHeight: 156, padding: 12, background: phase === 1 ? "var(--coral-soft)" : phase >= 2 ? "rgba(34,197,94,0.05)" : "transparent", transition: "all 400ms var(--easing)", display: "flex", flexWrap: "wrap", gap: 8, alignItems: "flex-start" }}> {visible.length === 0 && phase === 0 && (
Arrasta ou seleciona ficheiros
)} {DEMO_IMGS.map((src, i) => (
))}
= 2 ? 100 : progress}%`, background: phase >= 2 ? "#22c55e" : "var(--coral)", transition: "width 300ms ease, background 400ms ease" }} />
{phase === 0 ? "Aguarda ficheiros" : phase >= 2 ? "✓ Upload completo" : `A carregar… ${progress}%`}
Briefing Campanha Verão 2026 · editorial de produto
Tipo Editorial · 1 imagem
Tokens 1 / 25
{/* ── DIREITA — passos que somem + output ── */}
O que acontece a seguir {/* Steps — colapsam sequencialmente */}
{steps.map((s, i) => { const active = phase >= s.activeAt; const done = phase > s.activeAt; const hidden = hiddenSteps.includes(i); return ( ); })}
{/* Output image — aparece no mesmo espaço */}
Output · 1 token
Output
); } window.UploadDemo = UploadDemo;