- Add Google Fonts (Newsreader/Outfit/IBM Plex Mono) + warm beige CSS palette - New VIBNSidebar: Stackless-style 220px sidebar with project list + user footer - New ProjectShell: project header with name/status/progress% + tab bar - Tabs: Atlas → PRD → Design → Build → Deploy → Settings - New /prd page: section-by-section progress view - New /build page: locked until PRD complete - Projects list page: Stackless-style row layout - Simplify overview page to just render AtlasChat Made-with: Cursor
177 lines
5.5 KiB
TypeScript
177 lines
5.5 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { useParams } from "next/navigation";
|
|
import Link from "next/link";
|
|
|
|
interface Project {
|
|
id: string;
|
|
status?: string;
|
|
prd?: string;
|
|
giteaRepoUrl?: string;
|
|
}
|
|
|
|
const BUILD_FEATURES = [
|
|
"Authentication system",
|
|
"Database schema",
|
|
"API endpoints",
|
|
"Core UI",
|
|
"Business logic",
|
|
"Tests",
|
|
];
|
|
|
|
export default function BuildPage() {
|
|
const params = useParams();
|
|
const projectId = params.projectId as string;
|
|
const workspace = params.workspace as string;
|
|
const [project, setProject] = useState<Project | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
fetch(`/api/projects/${projectId}`)
|
|
.then((r) => r.json())
|
|
.then((d) => {
|
|
setProject(d.project);
|
|
setLoading(false);
|
|
})
|
|
.catch(() => setLoading(false));
|
|
}, [projectId]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100%", fontFamily: "Outfit, sans-serif", color: "#a09a90" }}>
|
|
Loading…
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const hasRepo = Boolean(project?.giteaRepoUrl);
|
|
const hasPRD = Boolean(project?.prd);
|
|
|
|
if (!hasPRD) {
|
|
return (
|
|
<div
|
|
className="vibn-enter"
|
|
style={{
|
|
flex: 1, display: "flex", alignItems: "center", justifyContent: "center",
|
|
padding: 40, fontFamily: "Outfit, sans-serif",
|
|
}}
|
|
>
|
|
<div style={{ textAlign: "center", maxWidth: 360 }}>
|
|
<div style={{
|
|
width: 56, height: 56, borderRadius: 14,
|
|
background: "#fff", border: "1px solid #e8e4dc",
|
|
display: "flex", alignItems: "center", justifyContent: "center",
|
|
fontSize: "1.4rem", margin: "0 auto 18px",
|
|
boxShadow: "0 2px 8px #1a1a1a08",
|
|
}}>
|
|
🔒
|
|
</div>
|
|
<h3 style={{
|
|
fontFamily: "Newsreader, serif", fontSize: "1.3rem",
|
|
fontWeight: 400, color: "#1a1a1a", marginBottom: 8,
|
|
}}>
|
|
Complete your PRD first
|
|
</h3>
|
|
<p style={{ fontSize: "0.82rem", color: "#a09a90", lineHeight: 1.6, marginBottom: 20 }}>
|
|
Finish your discovery with Atlas, then the builder unlocks automatically.
|
|
</p>
|
|
<Link
|
|
href={`/${workspace}/project/${projectId}/overview`}
|
|
style={{
|
|
display: "inline-block",
|
|
padding: "9px 20px", borderRadius: 7,
|
|
background: "#1a1a1a", color: "#fff",
|
|
fontSize: "0.78rem", fontWeight: 600,
|
|
fontFamily: "Outfit, sans-serif",
|
|
textDecoration: "none",
|
|
}}
|
|
>
|
|
Continue with Atlas →
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!hasRepo) {
|
|
return (
|
|
<div
|
|
className="vibn-enter"
|
|
style={{
|
|
flex: 1, display: "flex", alignItems: "center", justifyContent: "center",
|
|
padding: 40, fontFamily: "Outfit, sans-serif",
|
|
}}
|
|
>
|
|
<div style={{ textAlign: "center", maxWidth: 360 }}>
|
|
<div style={{
|
|
width: 56, height: 56, borderRadius: 14,
|
|
background: "#fff", border: "1px solid #e8e4dc",
|
|
display: "flex", alignItems: "center", justifyContent: "center",
|
|
fontSize: "1.4rem", margin: "0 auto 18px",
|
|
boxShadow: "0 2px 8px #1a1a1a08",
|
|
}}>
|
|
⚡
|
|
</div>
|
|
<h3 style={{
|
|
fontFamily: "Newsreader, serif", fontSize: "1.3rem",
|
|
fontWeight: 400, color: "#1a1a1a", marginBottom: 8,
|
|
}}>
|
|
PRD ready — build coming soon
|
|
</h3>
|
|
<p style={{ fontSize: "0.82rem", color: "#a09a90", lineHeight: 1.6 }}>
|
|
The Architect agent will generate your project structure and kick off the build pipeline.
|
|
This feature is in active development.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className="vibn-enter"
|
|
style={{
|
|
flex: 1, display: "flex", alignItems: "center", justifyContent: "center",
|
|
padding: 40, fontFamily: "Outfit, sans-serif",
|
|
}}
|
|
>
|
|
<div style={{ width: "100%", maxWidth: 500 }}>
|
|
<h3 style={{
|
|
fontFamily: "Newsreader, serif", fontSize: "1.2rem",
|
|
fontWeight: 400, color: "#1a1a1a", marginBottom: 18,
|
|
}}>
|
|
Build progress
|
|
</h3>
|
|
{BUILD_FEATURES.map((f, i) => (
|
|
<div
|
|
key={i}
|
|
className="vibn-enter"
|
|
style={{
|
|
display: "flex", alignItems: "center", gap: 12,
|
|
padding: "12px 16px", marginBottom: 4, borderRadius: 8,
|
|
background: "#fff", border: "1px solid #e8e4dc",
|
|
animationDelay: `${i * 0.05}s`,
|
|
}}
|
|
>
|
|
<span style={{
|
|
width: 7, height: 7, borderRadius: "50%",
|
|
background: "#d4a04a", display: "inline-block", flexShrink: 0,
|
|
}} />
|
|
<span style={{ flex: 1, fontSize: "0.84rem", color: "#1a1a1a" }}>{f}</span>
|
|
<div style={{ width: 80, height: 3, borderRadius: 2, background: "#eae6de" }}>
|
|
<div style={{ height: "100%", width: "0%", borderRadius: 2, background: "#3d5afe" }} />
|
|
</div>
|
|
<span style={{
|
|
fontFamily: "IBM Plex Mono, monospace",
|
|
fontSize: "0.7rem", color: "#a09a90", minWidth: 28, textAlign: "right",
|
|
}}>
|
|
0%
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|