From 63b16e76bb831cffbdd0a49181cf8145ffc4b34b Mon Sep 17 00:00:00 2001 From: mawkone Date: Mon, 8 Jun 2026 13:13:51 -0700 Subject: [PATCH] feat(agency): implement dynamic day-0 routing to seamlessly send agency users to the Command Center instead of the Maker projects grid --- .../app/[workspace]/projects/page.tsx | 423 ++++++++++++++---- 1 file changed, 332 insertions(+), 91 deletions(-) diff --git a/vibn-frontend/app/[workspace]/projects/page.tsx b/vibn-frontend/app/[workspace]/projects/page.tsx index cf4bbc40..9a606515 100644 --- a/vibn-frontend/app/[workspace]/projects/page.tsx +++ b/vibn-frontend/app/[workspace]/projects/page.tsx @@ -17,6 +17,7 @@ import { } from "@/components/ui/alert-dialog"; import { Loader2, Trash2 } from "lucide-react"; import { toast } from "sonner"; +import AgencyDashboard from "../agency/page"; interface ProjectWithStats { id: string; @@ -43,24 +44,64 @@ function timeAgo(dateStr?: string | null): string { } function StatusDot({ status }: { status?: string }) { - const color = status === "live" ? "#2e7d32" : status === "building" ? "#3d5afe" : "#d4a04a"; - const anim = status === "building" ? "vibn-breathe 2.5s ease infinite" : "none"; + const color = + status === "live" + ? "#2e7d32" + : status === "building" + ? "#3d5afe" + : "#d4a04a"; + const anim = + status === "building" ? "vibn-breathe 2.5s ease infinite" : "none"; return ( - + ); } function StatusTag({ status }: { status?: string }) { - const label = status === "live" ? "Live" : status === "building" ? "Building" : "Defining"; - const color = status === "live" ? "#2e7d32" : status === "building" ? "#3d5afe" : "#9a7b3a"; - const bg = status === "live" ? "#2e7d3210" : status === "building" ? "#3d5afe10" : "#d4a04a12"; + const label = + status === "live" + ? "Live" + : status === "building" + ? "Building" + : "Defining"; + const color = + status === "live" + ? "#2e7d32" + : status === "building" + ? "#3d5afe" + : "#9a7b3a"; + const bg = + status === "live" + ? "#2e7d3210" + : status === "building" + ? "#3d5afe10" + : "#d4a04a12"; return ( - + {label} ); @@ -72,10 +113,12 @@ export default function ProjectsPage() { const { data: session, status } = useSession(); const [projects, setProjects] = useState([]); + const [isAgency, setIsAgency] = useState(null); const [loading, setLoading] = useState(true); const [loadError, setLoadError] = useState(null); const [showNew, setShowNew] = useState(false); - const [projectToDelete, setProjectToDelete] = useState(null); + const [projectToDelete, setProjectToDelete] = + useState(null); const [isDeleting, setIsDeleting] = useState(false); const [hoveredId, setHoveredId] = useState(null); @@ -83,19 +126,41 @@ export default function ProjectsPage() { try { setLoading(true); setLoadError(null); + + // Fetch Workspace metadata first to determine routing + const wsRes = await fetch(`/api/workspaces/${workspace}`, { + credentials: "include", + }); + if (wsRes.ok) { + const wsData = await wsRes.json(); + setIsAgency(wsData.isAgency); + if (wsData.isAgency) { + setLoading(false); + return; // Stop fetching projects if it's an agency, let AgencyDashboard handle its own data + } + } + const res = await fetch("/api/projects", { credentials: "include" }); if (res.status === 401) { throw new Error("Your session expired — please log in again."); } if (!res.ok) { let body: { error?: string; details?: string } = {}; - try { body = await res.json(); } catch { /* keep {} */ } - throw new Error(body.error || `HTTP ${res.status} ${res.statusText}`.trim()); + try { + body = await res.json(); + } catch { + /* keep {} */ + } + throw new Error( + body.error || `HTTP ${res.status} ${res.statusText}`.trim(), + ); } const data = await res.json(); setProjects(data.projects ?? []); } catch (err) { - setLoadError(err instanceof Error ? err.message : "Failed to load projects"); + setLoadError( + err instanceof Error ? err.message : "Failed to load projects", + ); } finally { setLoading(false); } @@ -133,7 +198,9 @@ export default function ProjectsPage() { const statusSummary = () => { const live = projects.filter((p) => p.status === "live").length; const building = projects.filter((p) => p.status === "building").length; - const defining = projects.filter((p) => !p.status || p.status === "defining").length; + const defining = projects.filter( + (p) => !p.status || p.status === "defining", + ).length; const parts = []; if (defining) parts.push(`${defining} defining`); if (building) parts.push(`${building} building`); @@ -141,63 +208,112 @@ export default function ProjectsPage() { return `${projects.length} total · ${parts.join(" · ")}`; }; + if (isAgency === true) { + return ; + } + return (
{/* Header */} -
+
-

+

Projects

{!loading && ( -

{statusSummary()}

+

+ {statusSummary()} +

)}
{/* Loading */} {loading && ( -
- +
+
)} {/* Error */} {!loading && loadError && ( -
+
{loadError} @@ -327,19 +536,39 @@ export default function ProjectsPage() { {/* Empty state */} {!loading && projects.length === 0 && (
-

+

No projects yet

-

+

Tell Vibn what you want to build and it will figure out the rest.