From 2984545dde87d7c55eecd882fcff082cd1391da7 Mon Sep 17 00:00:00 2001 From: saridsa2 Date: Mon, 16 Mar 2026 14:41:59 +0530 Subject: [PATCH] feat: add app shell with sidebar navigation, routing, and placeholder pages Co-Authored-By: Claude Sonnet 4.6 --- src/components/layout/app-shell.tsx | 18 ++++ src/components/layout/sidebar.tsx | 126 ++++++++++++++++++++++++++++ src/components/layout/top-bar.tsx | 30 +++++++ src/main.tsx | 27 +++++- src/pages/all-leads.tsx | 12 +++ src/pages/campaign-detail.tsx | 12 +++ src/pages/campaigns.tsx | 12 +++ src/pages/lead-workspace.tsx | 12 +++ src/pages/login.tsx | 3 + src/pages/outreach.tsx | 12 +++ 10 files changed, 260 insertions(+), 4 deletions(-) create mode 100644 src/components/layout/app-shell.tsx create mode 100644 src/components/layout/sidebar.tsx create mode 100644 src/components/layout/top-bar.tsx create mode 100644 src/pages/all-leads.tsx create mode 100644 src/pages/campaign-detail.tsx create mode 100644 src/pages/campaigns.tsx create mode 100644 src/pages/lead-workspace.tsx create mode 100644 src/pages/login.tsx create mode 100644 src/pages/outreach.tsx diff --git a/src/components/layout/app-shell.tsx b/src/components/layout/app-shell.tsx new file mode 100644 index 0000000..b18f0f4 --- /dev/null +++ b/src/components/layout/app-shell.tsx @@ -0,0 +1,18 @@ +import type { ReactNode } from "react"; +import { useLocation } from "react-router"; +import { Sidebar } from "./sidebar"; + +interface AppShellProps { + children: ReactNode; +} + +export const AppShell = ({ children }: AppShellProps) => { + const { pathname } = useLocation(); + + return ( +
+ +
{children}
+
+ ); +}; diff --git a/src/components/layout/sidebar.tsx b/src/components/layout/sidebar.tsx new file mode 100644 index 0000000..a8cd367 --- /dev/null +++ b/src/components/layout/sidebar.tsx @@ -0,0 +1,126 @@ +import type { FC, HTMLAttributes } from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faBullhorn, + faChartMixed, + faCommentDots, + faGear, + faGrid2, + faPlug, +} from "@fortawesome/pro-regular-svg-icons"; +import { MobileNavigationHeader } from "@/components/application/app-navigation/base-components/mobile-header"; +import { NavAccountCard } from "@/components/application/app-navigation/base-components/nav-account-card"; +import { NavItemBase } from "@/components/application/app-navigation/base-components/nav-item"; +import type { NavItemType } from "@/components/application/app-navigation/config"; + +// TODO: Wire to useAuth() once auth-provider.tsx is implemented +const isAdmin = false; + +const MAIN_SIDEBAR_WIDTH = 292; + +// FontAwesome icon wrappers that satisfy FC> +const IconGrid2: FC> = ({ className }) => ( + +); +const IconBullhorn: FC> = ({ className }) => ( + +); +const IconCommentDots: FC> = ({ className }) => ( + +); +const IconChartMixed: FC> = ({ className }) => ( + +); +const IconPlug: FC> = ({ className }) => ( + +); +const IconGear: FC> = ({ className }) => ( + +); + +const mainItems: NavItemType[] = [ + { label: "Lead Workspace", href: "/", icon: IconGrid2 }, + { label: "Campaigns", href: "/campaigns", icon: IconBullhorn }, + { label: "Outreach", href: "/outreach", icon: IconCommentDots }, +]; + +const insightsItems: NavItemType[] = [ + { label: "Analytics", href: "/analytics", icon: IconChartMixed }, +]; + +const adminItems: NavItemType[] = [ + { label: "Integrations", href: "/integrations", icon: IconPlug }, + { label: "Settings", href: "/settings", icon: IconGear }, +]; + +interface SidebarProps { + activeUrl?: string; +} + +export const Sidebar = ({ activeUrl = "/" }: SidebarProps) => { + const navSections = [ + { label: "Main", items: mainItems }, + { label: "Insights", items: insightsItems }, + ...(isAdmin ? [{ label: "Admin", items: adminItems }] : []), + ]; + + const content = ( + + ); + + return ( + <> + {/* Mobile header navigation */} + {content} + + {/* Desktop sidebar navigation */} +
{content}
+ + {/* Placeholder to take up physical space because the real sidebar has `fixed` position. */} +
+ + ); +}; diff --git a/src/components/layout/top-bar.tsx b/src/components/layout/top-bar.tsx new file mode 100644 index 0000000..c833e36 --- /dev/null +++ b/src/components/layout/top-bar.tsx @@ -0,0 +1,30 @@ +import { SearchLg } from "@untitledui/icons"; +import { Avatar } from "@/components/base/avatar/avatar"; +import { Input } from "@/components/base/input/input"; + +interface TopBarProps { + title: string; + subtitle?: string; +} + +export const TopBar = ({ title, subtitle }: TopBarProps) => { + return ( +
+
+

{title}

+ {subtitle &&

{subtitle}

} +
+ +
+
+ +
+ +
+
+ ); +}; diff --git a/src/main.tsx b/src/main.tsx index 947dae7..7cf1b00 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,8 +1,14 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; -import { BrowserRouter, Route, Routes } from "react-router"; -import { HomeScreen } from "@/pages/home-screen"; +import { BrowserRouter, Outlet, Route, Routes } from "react-router"; +import { AppShell } from "@/components/layout/app-shell"; import { NotFound } from "@/pages/not-found"; +import { AllLeadsPage } from "@/pages/all-leads"; +import { CampaignDetailPage } from "@/pages/campaign-detail"; +import { CampaignsPage } from "@/pages/campaigns"; +import { LeadWorkspacePage } from "@/pages/lead-workspace"; +import { LoginPage } from "@/pages/login"; +import { OutreachPage } from "@/pages/outreach"; import { RouteProvider } from "@/providers/router-provider"; import { ThemeProvider } from "@/providers/theme-provider"; import "@/styles/globals.css"; @@ -13,8 +19,21 @@ createRoot(document.getElementById("root")!).render( - } /> - } /> + } /> + + + + } + > + } /> + } /> + } /> + } /> + } /> + } /> + diff --git a/src/pages/all-leads.tsx b/src/pages/all-leads.tsx new file mode 100644 index 0000000..018d4f0 --- /dev/null +++ b/src/pages/all-leads.tsx @@ -0,0 +1,12 @@ +import { TopBar } from "@/components/layout/top-bar"; + +export const AllLeadsPage = () => { + return ( +
+ +
+

All Leads — coming soon

+
+
+ ); +}; diff --git a/src/pages/campaign-detail.tsx b/src/pages/campaign-detail.tsx new file mode 100644 index 0000000..f27804b --- /dev/null +++ b/src/pages/campaign-detail.tsx @@ -0,0 +1,12 @@ +import { TopBar } from "@/components/layout/top-bar"; + +export const CampaignDetailPage = () => { + return ( +
+ +
+

Campaign Detail — coming soon

+
+
+ ); +}; diff --git a/src/pages/campaigns.tsx b/src/pages/campaigns.tsx new file mode 100644 index 0000000..25c713f --- /dev/null +++ b/src/pages/campaigns.tsx @@ -0,0 +1,12 @@ +import { TopBar } from "@/components/layout/top-bar"; + +export const CampaignsPage = () => { + return ( +
+ +
+

Campaigns — coming soon

+
+
+ ); +}; diff --git a/src/pages/lead-workspace.tsx b/src/pages/lead-workspace.tsx new file mode 100644 index 0000000..cddaca5 --- /dev/null +++ b/src/pages/lead-workspace.tsx @@ -0,0 +1,12 @@ +import { TopBar } from "@/components/layout/top-bar"; + +export const LeadWorkspacePage = () => { + return ( +
+ +
+

Lead Workspace — coming soon

+
+
+ ); +}; diff --git a/src/pages/login.tsx b/src/pages/login.tsx new file mode 100644 index 0000000..1222273 --- /dev/null +++ b/src/pages/login.tsx @@ -0,0 +1,3 @@ +export const LoginPage = () => { + return
Login placeholder
; +}; diff --git a/src/pages/outreach.tsx b/src/pages/outreach.tsx new file mode 100644 index 0000000..f52321f --- /dev/null +++ b/src/pages/outreach.tsx @@ -0,0 +1,12 @@ +import { TopBar } from "@/components/layout/top-bar"; + +export const OutreachPage = () => { + return ( +
+ +
+

Outreach — coming soon

+
+
+ ); +};