From 7fa8375bac618bc7c49349495c728d3090447d05 Mon Sep 17 00:00:00 2001 From: mawkone Date: Mon, 8 Jun 2026 14:17:10 -0700 Subject: [PATCH] feat(agency): refactor agency dashboard into a Next.js App Router layout with dynamic route-based views for individual clients --- .../agency/client/[clientId]/page.tsx | 186 +++++ app/[workspace]/agency/layout.tsx | 264 +++++++ app/[workspace]/agency/page.tsx | 653 +----------------- 3 files changed, 453 insertions(+), 650 deletions(-) create mode 100644 app/[workspace]/agency/client/[clientId]/page.tsx create mode 100644 app/[workspace]/agency/layout.tsx diff --git a/app/[workspace]/agency/client/[clientId]/page.tsx b/app/[workspace]/agency/client/[clientId]/page.tsx new file mode 100644 index 00000000..bbe92d0f --- /dev/null +++ b/app/[workspace]/agency/client/[clientId]/page.tsx @@ -0,0 +1,186 @@ +"use client"; + +import React from "react"; +import { useParams } from "next/navigation"; + +// The client list is currently mocked, but in the future this would be fetched from the DB +const CLIENTS = { + apex: { name: "Apex Plumbing" }, + barton: { name: "Barton Creek HVAC" }, +}; + +export default function ClientViewPage() { + const params = useParams(); + const clientId = params.clientId as keyof typeof CLIENTS; + const client = CLIENTS[clientId] || { name: "Unknown Client" }; + + return ( +
+ {/* Header */} +
+
+
+ Client +
+

+ {client.name} +

+

+ Manage projects, infrastructure, and secrets for this client. +

+
+ +
+ + {/* Tabs */} +
+ {["Projects", "Infrastructure", "Secrets Vault", "Contacts"].map( + (tab, i) => ( +
+ {tab} +
+ ), + )} +
+ + {/* Projects Grid */} +
+
+
+
+ {client.name.charAt(0)} +
+
+

+ Customer Portal +

+
+ + Live on {client.name.toLowerCase().replace(/[^a-z0-9]/g, "")}.vibn.app +
+
+
+ +
+
+
+ ); +} diff --git a/app/[workspace]/agency/layout.tsx b/app/[workspace]/agency/layout.tsx new file mode 100644 index 00000000..d0edb039 --- /dev/null +++ b/app/[workspace]/agency/layout.tsx @@ -0,0 +1,264 @@ +"use client"; + +import React from "react"; +import Link from "next/link"; +import { useParams, usePathname } from "next/navigation"; + +// Minimal SVG Icons +const Icons = { + Search: () => , + Target: () => , + MessageCircle: () => , + Kanban: () => , + FileText: () => , + Users: () => , + Folder: () => , + CreditCard: () => , + TrendingDown: () => , + Settings: () => , +}; + +function NavItem({ + icon: Icon, + label, + active, + badge, + href, +}: { + icon?: React.ElementType; + label: string; + active?: boolean; + badge?: string; + href: string; +}) { + return ( + { + if (!active) e.currentTarget.style.background = "#f6f4f0"; + }} + onMouseLeave={(e) => { + if (!active) e.currentTarget.style.background = "transparent"; + }} + > +
+ {Icon && ( + + + + )} + {label} +
+ {badge && ( + + {badge} + + )} + + ); +} + +function SectionHeader({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ); +} + +export default function AgencyLayout({ + children, +}: { + children: React.ReactNode; +}) { + const params = useParams(); + const pathname = usePathname(); + const workspace = params.workspace as string; + + // The client list is currently mocked, but in the future this would be fetched from the DB + const clients = [ + { id: "apex", name: "Apex Plumbing" }, + { id: "barton", name: "Barton Creek HVAC" }, + ]; + + return ( +
+ {/* ── LEFT SIDEBAR (Cadence Style) ── */} +
+ {/* Workspace Switcher */} +
+
+
+ {typeof workspace === "string" ? workspace.charAt(0) : "A"} +
+
+ + {typeof workspace === "string" + ? workspace.replace(/-/g, " ") + : "Atlas Agency"} + + + Pro · 2 members + +
+
+
+ + {/* Global Search */} +
+
+ + Search or jump to... + + ⌘K + +
+
+ + {/* Navigation */} +
+ Growth + + + + + + + Clients + + {clients.map((client) => { + const clientPath = `/${workspace}/agency/client/${client.id}`; + const isActive = pathname.startsWith(clientPath); + return ( + + ); + })} + + Finance & Ops + + + + +
+
+ + {/* ── MAIN CONTENT AREA ── */} +
+ {children} +
+
+ ); +} diff --git a/app/[workspace]/agency/page.tsx b/app/[workspace]/agency/page.tsx index d29ea850..3a71e249 100644 --- a/app/[workspace]/agency/page.tsx +++ b/app/[workspace]/agency/page.tsx @@ -1,90 +1,9 @@ "use client"; -import React, { useState } from "react"; -import { useParams } from "next/navigation"; +import React from "react"; // Minimal SVG Icons const Icons = { - Search: () => ( - - - - - ), - Target: () => ( - - - - - - ), - MessageCircle: () => ( - - - - ), - Kanban: () => ( - - - - - - - ), - FileText: () => ( - - - - - - - - ), Users: () => ( ), - Folder: () => ( - - - - ), - Box: () => ( - - - - - - ), - Server: () => ( - - - - - - - ), - Repeat: () => ( - - - - - - - ), - CreditCard: () => ( - - - - - ), - TrendingDown: () => ( - - - - - ), - Key: () => ( - - - - ), - Settings: () => ( - - - - - ), - ChevronDown: () => ( - - - - ), - ChevronRight: () => ( - - - - ), }; -function NavItem({ - icon: Icon, - label, - active, - badge, - inset = false, -}: { - icon?: React.ElementType; - label: string; - active?: boolean; - badge?: string; - inset?: boolean; -}) { - return ( - - ); -} - -function SectionHeader({ children }: { children: React.ReactNode }) { - return ( -
- {children} -
- ); -} - -export default function AgencyDashboard() { - const { workspace } = useParams(); - const [activeTab, setActiveTab] = useState("find_clients"); - - return ( -
- {/* ── LEFT SIDEBAR (Cadence Style) ── */} -
- {/* Workspace Switcher */} -
-
-
- {typeof workspace === "string" ? workspace.charAt(0) : "A"} -
-
- - {typeof workspace === "string" - ? workspace.replace(/-/g, " ") - : "Atlas Agency"} - - - Pro · 2 members - -
-
-
- - {/* Global Search */} -
-
- - Search or jump to... - - ⌘K - -
-
- - {/* Navigation */} -
- Growth -
setActiveTab("find_clients")}> - -
- - - - - - Clients - -
setActiveTab("client_apex")}> - -
- -
setActiveTab("client_barton")}> - -
- - Finance & Ops - - - - -
-
- - {/* ── MAIN CONTENT AREA ── */} -
- {activeTab === "find_clients" && } - {activeTab === "client_apex" && } - {activeTab === "client_barton" && ( - - )} -
-
- ); -} - -// ── CLIENT VIEW (Projects, Infra, Secrets) ── -function ClientView({ name }: { name: string }) { - return ( -
- {/* Header */} -
-
-
- Client -
-

- {name} -

-

- Manage projects, infrastructure, and secrets for this client. -

-
- -
- - {/* Tabs */} -
- {["Projects", "Infrastructure", "Secrets Vault", "Contacts"].map( - (tab, i) => ( -
- {tab} -
- ), - )} -
- - {/* Projects Grid */} -
-
-
-
- {name.charAt(0)} -
-
-

- Customer Portal -

-
- - Live on {name.toLowerCase().replace(/[^a-z0-9]/g, "")}.vibn.app -
-
-
- -
-
-
- ); -} - -// ── FIND CLIENTS VIEW (The Lead Gen Engine) ── -function FindClientsView() { +export default function FindClientsView() { return (
{/* Header */} @@ -743,7 +96,7 @@ function FindClientsView() { Ideal Customer Profile
- "Plumbers and HVAC" + "Plumbers and HVAC"