From a6054469eec62564bd215978679f588f27e0e6f6 Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Sat, 28 Mar 2026 19:12:45 +0100 Subject: [PATCH] feat: wrap home page in DashboardShell, remove stat cards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Home page now uses the same sidebar layout as dashboard pages. Sidebar shows simplified home mode (logo, app switcher, profile) without site-specific nav groups. Stat cards removed — plan info lives in settings, site count is self-evident from the list. --- app/layout-content.tsx | 5 +- app/page.tsx | 94 +++---------------------- components/dashboard/DashboardShell.tsx | 15 ++-- components/dashboard/Sidebar.tsx | 54 +++++++------- 4 files changed, 49 insertions(+), 119 deletions(-) diff --git a/app/layout-content.tsx b/app/layout-content.tsx index 745e0e9..c321a0c 100644 --- a/app/layout-content.tsx +++ b/app/layout-content.tsx @@ -95,6 +95,7 @@ function LayoutInner({ children }: { children: React.ReactNode }) { const showOfflineBar = Boolean(auth.user && !isOnline) // Site pages use DashboardShell with full sidebar — no Header needed const isSitePage = pathname.startsWith('/sites/') && pathname !== '/sites/new' + const isHomePage = pathname === '/' // Checkout page has its own minimal layout — no app header/footer const isCheckoutPage = pathname.startsWith('/checkout') @@ -103,13 +104,13 @@ function LayoutInner({ children }: { children: React.ReactNode }) { } // While auth is loading on a site or checkout page, render nothing to prevent flash of public header - if (auth.loading && (isSitePage || isCheckoutPage)) { + if (auth.loading && (isSitePage || isCheckoutPage || isHomePage)) { return null } // Authenticated site pages: full sidebar layout // DashboardShell inside children handles everything - if (isAuthenticated && isSitePage) { + if (isAuthenticated && (isSitePage || isHomePage)) { return ( <> {showOfflineBar && } diff --git a/app/page.tsx b/app/page.tsx index 22c4b7c..bd565ac 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -23,19 +23,16 @@ import PulseFAQ from '@/components/marketing/PulseFAQ' import { toast } from '@ciphera-net/ui' import { getAuthErrorMessage } from '@ciphera-net/ui' import { getSitesLimitForPlan } from '@/lib/plans' -import { formatDate } from '@/lib/utils/formatDate' -import { useUnifiedSettings } from '@/lib/unified-settings-context' +import DashboardShell from '@/components/dashboard/DashboardShell' type SiteStatsMap = Record export default function HomePage() { const { user, loading: authLoading } = useAuth() - const { openUnifiedSettings } = useUnifiedSettings() const [sites, setSites] = useState([]) const [sitesLoading, setSitesLoading] = useState(true) const [siteStats, setSiteStats] = useState({}) const [subscription, setSubscription] = useState(null) - const [subscriptionLoading, setSubscriptionLoading] = useState(false) const [showFinishSetupBanner, setShowFinishSetupBanner] = useState(true) const [deleteModalSite, setDeleteModalSite] = useState(null) const [deletedSites, setDeletedSites] = useState([]) @@ -116,13 +113,10 @@ export default function HomePage() { const loadSubscription = async () => { try { - setSubscriptionLoading(true) const sub = await getSubscription() setSubscription(sub) } catch { setSubscription(null) - } finally { - setSubscriptionLoading(false) } } @@ -236,7 +230,8 @@ export default function HomePage() { } return ( -
+ +
{showFinishSetupBanner && (

@@ -263,10 +258,10 @@ export default function HomePage() {

)} -
+
-

Your Sites

-

Manage your analytics sites and view insights.

+

Your Sites

+

Manage your analytics sites and view insights.

{(() => { const siteLimit = getSitesLimitForPlan(subscription?.plan_id) @@ -299,80 +294,6 @@ export default function HomePage() { )}
- {/* * Global Overview - min-h ensures no layout shift when Plan & usage loads */} -
-
-

Total Sites

-

{sites.length}

-
-
-

Total Visitors (24h)

-

- {sites.length === 0 || Object.keys(siteStats).length < sites.length - ? '--' - : Object.values(siteStats).reduce((sum, { stats }) => sum + (stats?.visitors ?? 0), 0).toLocaleString()} -

-
-
-

Plan & usage

- {subscriptionLoading ? ( -
-
-
-
-
-
- ) : subscription ? ( - <> -

- {(() => { - const raw = - subscription.plan_id?.startsWith('price_') - ? 'Pro' - : subscription.plan_id === 'free' || !subscription.plan_id - ? 'Free' - : subscription.plan_id - const label = raw === 'Free' || raw === 'Pro' ? raw : raw.charAt(0).toUpperCase() + raw.slice(1) - return `${label} Plan` - })()} -

- {(typeof subscription.sites_count === 'number' || (subscription.pageview_limit > 0 && typeof subscription.pageview_usage === 'number') || (!subscription.cancel_at_period_end && (subscription.subscription_status === 'active' || subscription.subscription_status === 'trialing'))) && ( -

- {typeof subscription.sites_count === 'number' && ( - Sites: {(() => { - const limit = getSitesLimitForPlan(subscription.plan_id) - return limit != null && typeof subscription.sites_count === 'number' ? `${subscription.sites_count}/${limit}` : subscription.sites_count - })()} - )} - {typeof subscription.sites_count === 'number' && (subscription.pageview_limit > 0 && typeof subscription.pageview_usage === 'number') && ' · '} - {subscription.pageview_limit > 0 && typeof subscription.pageview_usage === 'number' && ( - Pageviews: {subscription.pageview_usage.toLocaleString()}/{subscription.pageview_limit.toLocaleString()} - )} - {!subscription.cancel_at_period_end && (subscription.subscription_status === 'active' || subscription.subscription_status === 'trialing') && subscription.current_period_end && ( - - Renews {formatDate(new Date(subscription.current_period_end))} - - )} -

- )} -
- {subscription.has_payment_method ? ( - - ) : ( - - Upgrade - - )} -
- - ) : ( -

Free Plan

- )} -
-
- {!sitesLoading && sites.length === 0 && (
)} -
+
+ ) } diff --git a/components/dashboard/DashboardShell.tsx b/components/dashboard/DashboardShell.tsx index 20b36db..579a15a 100644 --- a/components/dashboard/DashboardShell.tsx +++ b/components/dashboard/DashboardShell.tsx @@ -41,15 +41,15 @@ const Sidebar = dynamic(() => import('./Sidebar'), { ), }) -function GlassTopBar({ siteId }: { siteId: string }) { +function GlassTopBar({ siteId }: { siteId: string | null }) { const { collapsed, toggle } = useSidebar() - const { data: realtime } = useRealtime(siteId) + const { data: realtime } = useRealtime(siteId ?? '') const lastUpdatedRef = useRef(null) const [, setTick] = useState(0) useEffect(() => { - if (realtime) lastUpdatedRef.current = Date.now() - }, [realtime]) + if (siteId && realtime) lastUpdatedRef.current = Date.now() + }, [siteId, realtime]) useEffect(() => { if (lastUpdatedRef.current == null) return @@ -57,7 +57,8 @@ function GlassTopBar({ siteId }: { siteId: string }) { return () => clearInterval(timer) }, [realtime]) - const pageTitle = usePageTitle() + const dashboardTitle = usePageTitle() + const pageTitle = siteId ? dashboardTitle : 'Your Sites' return (
@@ -74,7 +75,7 @@ function GlassTopBar({ siteId }: { siteId: string }) {
{/* Realtime indicator */} - {lastUpdatedRef.current != null && ( + {siteId && lastUpdatedRef.current != null && (
@@ -91,7 +92,7 @@ export default function DashboardShell({ siteId, children, }: { - siteId: string + siteId: string | null children: React.ReactNode }) { const [mobileOpen, setMobileOpen] = useState(false) diff --git a/components/dashboard/Sidebar.tsx b/components/dashboard/Sidebar.tsx index 52153e4..cdd057d 100644 --- a/components/dashboard/Sidebar.tsx +++ b/components/dashboard/Sidebar.tsx @@ -364,7 +364,7 @@ function SettingsButton({ interface SidebarContentProps { isMobile: boolean collapsed: boolean - siteId: string + siteId: string | null sites: Site[] canEdit: boolean pendingHref: string | null @@ -414,32 +414,38 @@ function SidebarContent({ {/* Site Picker */} - + {siteId && ( + + )} {/* Nav Groups */} - + ) : ( +
+ )} {/* Bottom — utility items */}
@@ -488,7 +494,7 @@ function SidebarContent({ export default function Sidebar({ siteId, mobileOpen, onMobileClose, onMobileOpen, }: { - siteId: string; mobileOpen: boolean; onMobileClose: () => void; onMobileOpen: () => void + siteId: string | null; mobileOpen: boolean; onMobileClose: () => void; onMobileOpen: () => void }) { const auth = useAuth() const { user } = auth