"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { JM, JV } from "@/components/project-creation/modal-theme"; const STEPS = [ { l: "Creating Gitea repository", d: "Setting up version control for your project" }, { l: "Scaffolding the app", d: "Next.js · TypeScript · Tailwind CSS" }, { l: "Setting up your database", d: "PostgreSQL + schema based on your product plan" }, { l: "Building sign up & login", d: "Email + Google + GitHub OAuth" }, { l: "Wiring payments", d: "Stripe checkout, webhooks, billing portal" }, { l: "Generating app pages", d: "Dashboard, settings, onboarding, invite flow" }, { l: "Applying your design", d: "Theme applied across all pages" }, { l: "Building marketing website", d: "SEO-ready marketing surface" }, { l: "Setting up email", d: "Welcome, password reset, and marketing templates" }, { l: "Pushing to Gitea", d: "Full codebase committed and pushed" }, { l: "Deploying via Coolify", d: "Building Docker image, deploying to your servers" }, { l: "Running health checks", d: "Verifying pages, auth, and payments are live" }, ] as const; type PhaseRowProps = { done: boolean; active: boolean; label: string; sub?: string; onClick?: () => void; }; function PhaseRow({ done, active, label, sub, onClick }: PhaseRowProps) { return ( ); } function Confetti() { const colors = ["#6366F1", "#818CF8", "#4338CA", "#A5B4FC", "#C7D2FE", "#FCD34D", "#34D399", "#60A5FA"]; const pieces = Array.from({ length: 90 }, (_, i) => ({ i, color: colors[i % colors.length], left: Math.random() * 100, delay: Math.random() * 1.2, dur: Math.random() * 2.5 + 2, size: Math.random() * 9 + 4, xDrift: (Math.random() - 0.5) * 200, rot: Math.random() * 360, br: ["50%", "3px", "0"][Math.floor(Math.random() * 3)], })); return (
{pieces.map(p => (
))}
); } export interface BuildMvpJustineV2Props { workspace: string; projectId: string; projectName: string; giteaRepo?: string; /** First webapp surface label + theme if any */ designFeel?: string; designStructure?: string; accentLabel?: string; accentHex?: string; websiteVoice?: string; websiteStyle?: string; topicsLine?: string; pageColumns?: { title: string; pages: string[] }[]; onSwitchToPreview: () => void; } export function BuildMvpJustineV2({ workspace, projectId, projectName, giteaRepo, designFeel = "Friendly", designStructure = "Clean", accentLabel = "Indigo", accentHex = "#6366F1", websiteVoice = "Friendly · Balanced · Warm", websiteStyle = "Editorial", topicsLine = "The problem · Who it's for · Why now", pageColumns = [ { title: "Public", pages: ["Landing page", "Pricing", "About", "Blog"] }, { title: "Auth", pages: ["Sign up", "Log in", "Forgot password"] }, { title: "App", pages: ["Dashboard", "Onboarding", "Settings"] }, { title: "Payments", pages: ["Checkout", "Success", "Manage subscription"] }, ], onSwitchToPreview, }: BuildMvpJustineV2Props) { const router = useRouter(); const [uiPhase, setUiPhase] = useState<"review" | "progress" | "done">("review"); const [curStep, setCurStep] = useState(0); const [building, setBuilding] = useState(false); const [showConfetti, setShowConfetti] = useState(false); const intervalRef = useRef | null>(null); const giteaWebBase = process.env.NEXT_PUBLIC_GITEA_WEB_URL ?? "https://git.vibnai.com"; const giteaHref = giteaRepo ? `${giteaWebBase}/${giteaRepo}` : giteaWebBase; const clearBuildInterval = useCallback(() => { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } }, []); useEffect(() => () => clearBuildInterval(), [clearBuildInterval]); const startBuild = () => { if (building) return; setBuilding(true); setTimeout(() => { setUiPhase("progress"); setCurStep(0); intervalRef.current = setInterval(() => { setCurStep(c => { const next = c + 1; if (next >= STEPS.length) { clearBuildInterval(); setUiPhase("done"); setBuilding(false); setShowConfetti(true); setTimeout(() => setShowConfetti(false), 5000); return STEPS.length; } return next; }); }, 700); }, 400); }; const renderStepRows = () => STEPS.map((s, i) => { const done = i < curStep; const active = i === curStep && uiPhase === "progress"; return (
{done && } {active && ( )}
{s.l}
{(done || active) && (
{s.d}
)}
); }); return ( <> {showConfetti && }
V
vibn
{projectName}
MVP Setup
router.push(`/${workspace}/project/${projectId}/overview`)} /> router.push(`/${workspace}/project/${projectId}/tasks`)} /> router.push(`/${workspace}/project/${projectId}/design`)} /> router.push(`/${workspace}/project/${projectId}/growth`)} />
Save & go to dashboard
{uiPhase === "review" && (
Ready to build

Review everything below. Once you hit Build, AI codes your full product and deploys it.

What's being built
{( [ { icon: "⛓", k: "Sign up & login", v: "Email + social login", br: true, bb: true }, { icon: "$", k: "Payments", v: "Subscription billing", br: false, bb: true }, { icon: "✉", k: "Email", v: "Transactional + marketing", br: true, bb: true }, { icon: "◧", k: "Product style", v: "Clean & focused", br: false, bb: true }, { icon: "◉", k: "Website style", v: websiteStyle, br: true, bb: false }, { icon: "≡", k: "Campaign topics", v: topicsLine, br: false, bb: false }, ] as const ).map(cell => (
{cell.icon}
{cell.k}
{cell.v}
))}
Pages {pageColumns.reduce((n, c) => n + c.pages.length, 0)} pages total
{pageColumns.map((col, ci) => (
{col.title}
{col.pages.map(p => ( {p}
))}
))}
Your design
Feel
{designFeel}
Accent
{accentLabel}
Layout
{designStructure}
Your website
Voice
{websiteVoice}
Website style
{websiteStyle}
Topics
{topicsLine}

You're ready to build your product

Your app will be generated, your backend configured, and everything deployed to your infrastructure — fully automated, no code needed.

What happens next
{[ { icon: "✦", t: "Generate UI & all pages", est: "~30s" }, { icon: "⛁", t: "Set up database & backend", est: "~45s" }, { icon: "⛓", t: "Connect auth, payments & email", est: "~30s" }, { icon: "▲", t: "Deploy your app live", est: "~20s" }, ].map(row => (
{row.icon}
{row.t}
{row.est}
))}

Takes ~2–4 minutes · All steps run in parallel

No code needed · You can edit everything after

)} {(uiPhase === "progress" || uiPhase === "done") && (
{uiPhase === "done" ? ( <>
🚀
Your MVP is live
Deployed to Coolify · Pushed to Gitea · Ready to share
) : ( <>
Building your product…
Step {curStep} of {STEPS.length}
)}
{renderStepRows()}
{uiPhase === "done" && (
Your next 3 actions
{[ { n: "1", t: "Open your live app", d: "Share the URL with 5 real people today.", }, { n: "2", t: "Sign up as a user", d: "Go through your own onboarding. Fix anything confusing.", }, { n: "3", t: "Post your first topic", d: "AI has drafted your first content batch. Publish one today.", }, ].map((a, i, arr) => (
{a.n}
{a.t}
{a.d}
))}
View in Gitea ↗
)}
)}
); }