feat: implement 4 project type flows with unique AI experiences
- New multi-step CreateProjectFlow replaces 2-step modal with TypeSelector and 4 setup components (Fresh Idea, Chat Import, Code Import, Migrate) - overview/page.tsx routes to unique main component per creationMode - FreshIdeaMain: wraps AtlasChat with post-discovery decision banner (Generate PRD vs Plan MVP Test) - ChatImportMain: 3-stage flow (intake → extracting → review) with editable insight buckets (decisions, ideas, questions, architecture, users) - CodeImportMain: 4-stage flow (input → cloning → mapping → surfaces) with architecture map and surface selection - MigrateMain: 5-stage flow with audit, review, planning, and migration plan doc with checkbox-tracked tasks and non-destructive warning banner - New API routes: analyze-chats, analyze-repo, analysis-status, generate-migration-plan (all using Gemini) - ProjectShell: accepts creationMode prop, filters/renames tabs per type (code-import hides PRD, migration hides PRD/Grow/Insights, renames Atlas tab) - Right panel adapts content based on creationMode Made-with: Cursor
This commit is contained in:
@@ -19,9 +19,10 @@ interface ProjectShellProps {
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
featureCount?: number;
|
||||
creationMode?: "fresh" | "chat-import" | "code-import" | "migration";
|
||||
}
|
||||
|
||||
const TABS = [
|
||||
const ALL_TABS = [
|
||||
{ id: "overview", label: "Atlas", path: "overview" },
|
||||
{ id: "prd", label: "PRD", path: "prd" },
|
||||
{ id: "design", label: "Design", path: "design" },
|
||||
@@ -32,6 +33,23 @@ const TABS = [
|
||||
{ id: "settings", label: "Settings", path: "settings" },
|
||||
];
|
||||
|
||||
function getTabsForMode(
|
||||
mode: "fresh" | "chat-import" | "code-import" | "migration" = "fresh"
|
||||
) {
|
||||
switch (mode) {
|
||||
case "code-import":
|
||||
// Hide PRD — this project already has code; goal is go-to-market surfaces
|
||||
return ALL_TABS.filter(t => t.id !== "prd");
|
||||
case "migration":
|
||||
// Hide PRD, rename overview, hide Grow and Insights (less relevant)
|
||||
return ALL_TABS
|
||||
.filter(t => !["prd", "grow", "insights"].includes(t.id))
|
||||
.map(t => t.id === "overview" ? { ...t, label: "Migration Plan" } : t);
|
||||
default:
|
||||
return ALL_TABS;
|
||||
}
|
||||
}
|
||||
|
||||
const DISCOVERY_PHASES = [
|
||||
{ id: "big_picture", label: "Big Picture" },
|
||||
{ id: "users_personas", label: "Users & Personas" },
|
||||
@@ -101,8 +119,10 @@ export function ProjectShell({
|
||||
createdAt,
|
||||
updatedAt,
|
||||
featureCount = 0,
|
||||
creationMode,
|
||||
}: ProjectShellProps) {
|
||||
const pathname = usePathname();
|
||||
const TABS = getTabsForMode(creationMode);
|
||||
const activeTab = TABS.find((t) => pathname?.includes(`/${t.path}`))?.id ?? "overview";
|
||||
const progress = projectProgress ?? 0;
|
||||
|
||||
@@ -250,68 +270,84 @@ export function ProjectShell({
|
||||
fontFamily: "Outfit, sans-serif",
|
||||
display: activeTab === "design" ? "none" : undefined,
|
||||
}}>
|
||||
{/* Discovery phases */}
|
||||
<SectionLabel>Discovery</SectionLabel>
|
||||
{DISCOVERY_PHASES.map((phase, i) => {
|
||||
const isDone = savedPhaseIds.has(phase.id);
|
||||
const isActive = !isDone && i === firstUnsavedIdx;
|
||||
return (
|
||||
<div
|
||||
key={phase.id}
|
||||
style={{
|
||||
display: "flex", alignItems: "center", gap: 10,
|
||||
padding: "9px 0",
|
||||
borderBottom: i < DISCOVERY_PHASES.length - 1 ? "1px solid #f0ece4" : "none",
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
width: 20, height: 20, borderRadius: 5, flexShrink: 0,
|
||||
background: isDone ? "#2e7d3210" : isActive ? "#d4a04a12" : "#f6f4f0",
|
||||
display: "flex", alignItems: "center", justifyContent: "center",
|
||||
fontSize: "0.58rem", fontWeight: 700,
|
||||
color: isDone ? "#2e7d32" : isActive ? "#9a7b3a" : "#c5c0b8",
|
||||
}}>
|
||||
{isDone ? "✓" : isActive ? "→" : i + 1}
|
||||
</div>
|
||||
<span style={{
|
||||
fontSize: "0.78rem",
|
||||
fontWeight: isActive ? 600 : 400,
|
||||
color: isDone ? "#6b6560" : isActive ? "#1a1a1a" : "#b5b0a6",
|
||||
}}>
|
||||
{phase.label}
|
||||
</span>
|
||||
{/* Right panel content — varies by creation mode */}
|
||||
{(creationMode === "code-import" || creationMode === "migration") ? (
|
||||
<>
|
||||
<SectionLabel>
|
||||
{creationMode === "migration" ? "Migration" : "Import"}
|
||||
</SectionLabel>
|
||||
<div style={{ fontSize: "0.78rem", color: "#a09a90", lineHeight: 1.5, marginBottom: 16 }}>
|
||||
{creationMode === "migration"
|
||||
? "Atlas will audit your existing product and generate a safe, phased migration plan."
|
||||
: "Atlas will clone your repository and map the architecture, then suggest surfaces to build."}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<div style={{ height: 1, background: "#f0ece4", margin: "16px 0" }} />
|
||||
|
||||
{/* Captured data — summaries from saved phases */}
|
||||
<SectionLabel>Captured</SectionLabel>
|
||||
{savedPhases.length > 0 ? (
|
||||
savedPhases.map((p) => (
|
||||
<div key={p.phase} style={{ marginBottom: 14 }}>
|
||||
<div style={{
|
||||
fontSize: "0.62rem", color: "#2e7d32",
|
||||
textTransform: "uppercase", letterSpacing: "0.05em",
|
||||
marginBottom: 3, fontWeight: 600, display: "flex", alignItems: "center", gap: 4,
|
||||
}}>
|
||||
<span>✓</span><span>{p.title}</span>
|
||||
</div>
|
||||
<div style={{ fontSize: "0.75rem", color: "#4a4640", lineHeight: 1.45 }}>
|
||||
{p.summary}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
</>
|
||||
) : (
|
||||
<p style={{ fontSize: "0.78rem", color: "#c5c0b8", lineHeight: 1.5, margin: 0 }}>
|
||||
Atlas will capture key details here as you chat.
|
||||
</p>
|
||||
<>
|
||||
{/* Discovery phases */}
|
||||
<SectionLabel>Discovery</SectionLabel>
|
||||
{DISCOVERY_PHASES.map((phase, i) => {
|
||||
const isDone = savedPhaseIds.has(phase.id);
|
||||
const isActive = !isDone && i === firstUnsavedIdx;
|
||||
return (
|
||||
<div
|
||||
key={phase.id}
|
||||
style={{
|
||||
display: "flex", alignItems: "center", gap: 10,
|
||||
padding: "9px 0",
|
||||
borderBottom: i < DISCOVERY_PHASES.length - 1 ? "1px solid #f0ece4" : "none",
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
width: 20, height: 20, borderRadius: 5, flexShrink: 0,
|
||||
background: isDone ? "#2e7d3210" : isActive ? "#d4a04a12" : "#f6f4f0",
|
||||
display: "flex", alignItems: "center", justifyContent: "center",
|
||||
fontSize: "0.58rem", fontWeight: 700,
|
||||
color: isDone ? "#2e7d32" : isActive ? "#9a7b3a" : "#c5c0b8",
|
||||
}}>
|
||||
{isDone ? "✓" : isActive ? "→" : i + 1}
|
||||
</div>
|
||||
<span style={{
|
||||
fontSize: "0.78rem",
|
||||
fontWeight: isActive ? 600 : 400,
|
||||
color: isDone ? "#6b6560" : isActive ? "#1a1a1a" : "#b5b0a6",
|
||||
}}>
|
||||
{phase.label}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<div style={{ height: 1, background: "#f0ece4", margin: "16px 0" }} />
|
||||
|
||||
{/* Captured data — summaries from saved phases */}
|
||||
<SectionLabel>Captured</SectionLabel>
|
||||
{savedPhases.length > 0 ? (
|
||||
savedPhases.map((p) => (
|
||||
<div key={p.phase} style={{ marginBottom: 14 }}>
|
||||
<div style={{
|
||||
fontSize: "0.62rem", color: "#2e7d32",
|
||||
textTransform: "uppercase", letterSpacing: "0.05em",
|
||||
marginBottom: 3, fontWeight: 600, display: "flex", alignItems: "center", gap: 4,
|
||||
}}>
|
||||
<span>✓</span><span>{p.title}</span>
|
||||
</div>
|
||||
<div style={{ fontSize: "0.75rem", color: "#4a4640", lineHeight: 1.45 }}>
|
||||
{p.summary}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<p style={{ fontSize: "0.78rem", color: "#c5c0b8", lineHeight: 1.5, margin: 0 }}>
|
||||
Atlas will capture key details here as you chat.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<div style={{ height: 1, background: "#f0ece4", margin: "16px 0" }} />
|
||||
|
||||
{/* Project info */}
|
||||
{/* Project info — always shown */}
|
||||
<SectionLabel>Project Info</SectionLabel>
|
||||
{[
|
||||
{ k: "Created", v: timeAgo(createdAt) },
|
||||
|
||||
Reference in New Issue
Block a user