diff --git a/app/api/projects/create/route.ts b/app/api/projects/create/route.ts index 97c2e907..bc0e288e 100644 --- a/app/api/projects/create/route.ts +++ b/app/api/projects/create/route.ts @@ -78,6 +78,8 @@ export async function POST(request: Request) { githubRepoUrl, githubDefaultBranch, githubToken, + creationMode, + sourceData, } = body; // Check slug uniqueness @@ -239,6 +241,16 @@ export async function POST(request: Request) { // Coolify project — one per VIBN project, scopes all app services + DBs. // Apps are deployed on-demand via apps_create (no auto-scaffold). coolifyProjectUuid, + // How this project was created — drives the AI's first-chat seed. + // Shape: { mode: "build"|"oss"|"import", sourceData: {...} } where + // sourceData is the path-specific payload from the wizard. + creationMode: creationMode ?? null, + kickoff: creationMode ? { + mode: creationMode, + sourceData: sourceData ?? null, + vision: vision || null, + createdAt: now, + } : null, // Import metadata isImport: !!githubRepoUrl, importAnalysisStatus: githubRepoUrl ? 'pending' : null, diff --git a/components/project-association-prompt.tsx b/components/project-association-prompt.tsx index eff1a577..b5e581c7 100644 --- a/components/project-association-prompt.tsx +++ b/components/project-association-prompt.tsx @@ -298,7 +298,6 @@ export function ProjectAssociationPrompt({ workspace }: { workspace: string }) { setUnassociatedWorkspace(null); } }} - initialWorkspacePath={unassociatedWorkspace?.workspacePath} workspace={workspace} /> diff --git a/components/project-creation/BuildSetup.tsx b/components/project-creation/BuildSetup.tsx new file mode 100644 index 00000000..b2a38ea3 --- /dev/null +++ b/components/project-creation/BuildSetup.tsx @@ -0,0 +1,132 @@ +"use client"; + +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { toast } from "sonner"; +import { JM } from "./modal-theme"; +import { + SetupHeader, FieldLabel, TextInput, TextArea, ForWhomSelector, + PrimaryButton, SecondaryButton, StepDots, type SetupProps, +} from "./setup-shared"; + +/** + * "Build your own idea" — two-step setup. + * Step 1: project name + audience. + * Step 2: describe the idea (free text). Becomes the seed message + * for the first AI conversation in the project. + */ +export function BuildSetup({ workspace, onClose, onBack }: SetupProps) { + const router = useRouter(); + const [step, setStep] = useState<0 | 1>(0); + const [name, setName] = useState(""); + const [forWhom, setForWhom] = useState<"personal" | "client">("personal"); + const [idea, setIdea] = useState(""); + const [loading, setLoading] = useState(false); + + const canContinue = name.trim().length > 0; + const canCreate = idea.trim().length > 4; + + const handleCreate = async () => { + if (!canCreate) return; + setLoading(true); + try { + const res = await fetch("/api/projects/create", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + projectName: name.trim(), + projectType: "web-app", + slug: name.toLowerCase().replace(/[^a-z0-9]+/g, "-"), + vision: idea.trim(), + product: { name: name.trim(), isForClient: forWhom === "client" }, + creationMode: "build", + sourceData: { idea: idea.trim() }, + }), + }); + if (!res.ok) { + const err = await res.json(); + toast.error(err.error || "Failed to create project"); + return; + } + const data = await res.json(); + onClose(); + router.push(`/${workspace}/project/${data.projectId}/overview`); + } catch { + toast.error("Something went wrong"); + } finally { + setLoading(false); + } + }; + + return ( +
+ setStep(0)} onClose={onClose} + /> + + {step === 0 && ( + <> + Project name + { if (e.key === "Enter" && canContinue) setStep(1); }} + autoFocus + /> + + + + setStep(1)} disabled={!canContinue}> + Next → + + } /> + + )} + + {step === 1 && ( + <> + What do you want to build? +