Files
vibn-frontend/components/project-main/FreshIdeaMain.tsx
Mark Henderson 99c1a83b9f feat: load Atlas discovery history into CooChat sidebar
Eliminates the two-chat experience on the overview page:

- CooChat now pre-loads Atlas conversation history on mount, showing
  the full discovery conversation in the left panel. Atlas messages
  render with a blue "A" avatar; COO messages use the dark "◈" icon.
  A "Discovery · COO" divider separates historical from new messages.
- FreshIdeaMain detects when a PRD already exists and replaces the
  duplicate AtlasChat with a clean completion view ("Discovery complete")
  that links to the PRD and Build pages. Atlas chat only shows when
  discovery is still in progress.

Made-with: Cursor
2026-03-10 16:28:44 -07:00

198 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useEffect, useState } from "react";
import { AtlasChat } from "@/components/AtlasChat";
import { useRouter, useParams } from "next/navigation";
import Link from "next/link";
const DISCOVERY_PHASES = [
"big_picture",
"users_personas",
"features_scope",
"business_model",
"screens_data",
"risks_questions",
];
interface FreshIdeaMainProps {
projectId: string;
projectName: string;
}
export function FreshIdeaMain({ projectId, projectName }: FreshIdeaMainProps) {
const router = useRouter();
const params = useParams();
const workspace = params?.workspace as string;
const [savedPhaseIds, setSavedPhaseIds] = useState<Set<string>>(new Set());
const [allDone, setAllDone] = useState(false);
const [prdLoading, setPrdLoading] = useState(false);
const [dismissed, setDismissed] = useState(false);
const [hasPrd, setHasPrd] = useState(false);
useEffect(() => {
// Check if PRD already exists on the project
fetch(`/api/projects/${projectId}`)
.then(r => r.json())
.then(d => { if (d.project?.prd) setHasPrd(true); })
.catch(() => {});
const poll = () => {
fetch(`/api/projects/${projectId}/save-phase`)
.then(r => r.json())
.then(d => {
const ids = new Set<string>((d.phases ?? []).map((p: { phase: string }) => p.phase));
setSavedPhaseIds(ids);
const done = DISCOVERY_PHASES.every(id => ids.has(id));
setAllDone(done);
})
.catch(() => {});
};
poll();
const interval = setInterval(poll, 8_000);
return () => clearInterval(interval);
}, [projectId]);
const handleGeneratePRD = async () => {
if (prdLoading) return;
setPrdLoading(true);
try {
router.push(`/${workspace}/project/${projectId}/prd`);
} finally {
setPrdLoading(false);
}
};
const handleMVP = () => {
router.push(`/${workspace}/project/${projectId}/build`);
};
// Once the PRD exists, show a clean "done" state in the main panel.
// The Atlas conversation history lives in the left CooChat sidebar.
if (hasPrd) {
return (
<div style={{
height: "100%", display: "flex", flexDirection: "column",
alignItems: "center", justifyContent: "center",
fontFamily: "Outfit, sans-serif", padding: "32px",
}}>
<div style={{
maxWidth: 420, textAlign: "center",
display: "flex", flexDirection: "column", alignItems: "center", gap: 20,
}}>
<div style={{
width: 48, height: 48, borderRadius: 14, background: "#1a1a1a",
display: "flex", alignItems: "center", justifyContent: "center",
fontSize: "1.2rem", color: "#fff",
}}></div>
<div>
<div style={{ fontSize: "1.05rem", fontWeight: 700, color: "#1a1a1a", marginBottom: 6 }}>
Discovery complete
</div>
<div style={{ fontSize: "0.82rem", color: "#6b6560", lineHeight: 1.65 }}>
Your PRD is saved. The full discovery conversation is in the left panel talk to your COO to plan what to build next.
</div>
</div>
<div style={{ display: "flex", gap: 10 }}>
<Link
href={`/${workspace}/project/${projectId}/prd`}
style={{
padding: "10px 20px", borderRadius: 8, border: "none",
background: "#1a1a1a", color: "#fff",
fontSize: "0.82rem", fontWeight: 600,
textDecoration: "none", display: "inline-block",
}}
>
View PRD
</Link>
<Link
href={`/${workspace}/project/${projectId}/build`}
style={{
padding: "10px 20px", borderRadius: 8,
border: "1px solid #e8e4dc",
background: "#fff", color: "#1a1a1a",
fontSize: "0.82rem", fontWeight: 500,
textDecoration: "none", display: "inline-block",
}}
>
Go to Build
</Link>
</div>
</div>
</div>
);
}
return (
<div style={{ height: "100%", display: "flex", flexDirection: "column", position: "relative" }}>
{/* Decision banner — shown when all 6 phases are saved */}
{allDone && !dismissed && (
<div style={{
background: "linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%)",
padding: "18px 28px",
display: "flex", alignItems: "center", justifyContent: "space-between",
gap: 16, flexShrink: 0, flexWrap: "wrap",
borderBottom: "1px solid #333",
}}>
<div>
<div style={{ fontSize: "0.88rem", fontWeight: 700, color: "#fff", fontFamily: "Outfit, sans-serif", marginBottom: 3 }}>
Discovery complete what's next?
</div>
<div style={{ fontSize: "0.75rem", color: "#a09a90", fontFamily: "Outfit, sans-serif" }}>
Atlas has captured all 6 discovery phases. Choose your next step.
</div>
</div>
<div style={{ display: "flex", gap: 10, flexShrink: 0 }}>
<button
onClick={handleGeneratePRD}
disabled={prdLoading}
style={{
padding: "10px 20px", borderRadius: 8, border: "none",
background: "#fff", color: "#1a1a1a",
fontSize: "0.84rem", fontWeight: 700,
fontFamily: "Outfit, sans-serif", cursor: "pointer",
transition: "opacity 0.12s",
}}
onMouseEnter={e => (e.currentTarget.style.opacity = "0.88")}
onMouseLeave={e => (e.currentTarget.style.opacity = "1")}
>
{prdLoading ? "Navigating…" : "Generate PRD →"}
</button>
<button
onClick={handleMVP}
style={{
padding: "10px 20px", borderRadius: 8,
border: "1px solid rgba(255,255,255,0.2)",
background: "transparent", color: "#fff",
fontSize: "0.84rem", fontWeight: 600,
fontFamily: "Outfit, sans-serif", cursor: "pointer",
transition: "background 0.12s",
}}
onMouseEnter={e => (e.currentTarget.style.background = "rgba(255,255,255,0.08)")}
onMouseLeave={e => (e.currentTarget.style.background = "transparent")}
>
Plan MVP Test
</button>
<button
onClick={() => setDismissed(true)}
style={{
background: "none", border: "none", cursor: "pointer",
color: "#666", fontSize: "1rem", padding: "4px 6px",
fontFamily: "Outfit, sans-serif",
}}
title="Dismiss"
>
×
</button>
</div>
</div>
)}
<AtlasChat
projectId={projectId}
projectName={projectName}
/>
</div>
);
}