From c36c1b069689080d1ab92a538d246853cd5ed1be Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Sat, 28 Mar 2026 19:35:23 +0100 Subject: [PATCH] feat: wrap all authenticated pages in DashboardShell, fix site card actions - Move DashboardShell wrapping to layout-content.tsx for all dashboard pages (home, integrations, pricing) instead of per-page - GlassTopBar derives page title from pathname (Integrations, Pricing) - Site card: gear icon now opens site settings, separate trash icon for delete --- app/layout-content.tsx | 22 +++++++++++++++++----- app/page.tsx | 3 --- components/dashboard/DashboardShell.tsx | 15 ++++++++++++++- components/sites/SiteList.tsx | 11 +++++++++-- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/app/layout-content.tsx b/app/layout-content.tsx index c321a0c..18a988b 100644 --- a/app/layout-content.tsx +++ b/app/layout-content.tsx @@ -17,6 +17,7 @@ import { LoadingOverlay } from '@ciphera-net/ui' import { useRouter } from 'next/navigation' import { UnifiedSettingsProvider, useUnifiedSettings } from '@/lib/unified-settings-context' import UnifiedSettingsModal from '@/components/settings/unified/UnifiedSettingsModal' +import DashboardShell from '@/components/dashboard/DashboardShell' const ORG_SWITCH_KEY = 'pulse_switching_org' @@ -95,7 +96,8 @@ 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 === '/' + // Pages that use DashboardShell with home sidebar (no site context) + const isDashboardPage = pathname === '/' || pathname.startsWith('/integrations') || pathname === '/pricing' // Checkout page has its own minimal layout — no app header/footer const isCheckoutPage = pathname.startsWith('/checkout') @@ -104,13 +106,12 @@ 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 || isHomePage)) { + if (auth.loading && (isSitePage || isCheckoutPage || isDashboardPage)) { return null } - // Authenticated site pages: full sidebar layout - // DashboardShell inside children handles everything - if (isAuthenticated && (isSitePage || isHomePage)) { + // Authenticated site pages: DashboardShell provided by sites layout + if (isAuthenticated && isSitePage) { return ( <> {showOfflineBar && } @@ -120,6 +121,17 @@ function LayoutInner({ children }: { children: React.ReactNode }) { ) } + // Authenticated dashboard pages (home, integrations, pricing): wrap in DashboardShell + if (isAuthenticated && isDashboardPage) { + return ( + <> + {showOfflineBar && } + {children} + + + ) + } + // Checkout page: render children only (has its own layout) if (isAuthenticated && isCheckoutPage) { return <>{children} diff --git a/app/page.tsx b/app/page.tsx index c5c0e8e..c506644 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -23,7 +23,6 @@ import PulseFAQ from '@/components/marketing/PulseFAQ' import { toast } from '@ciphera-net/ui' import { getAuthErrorMessage } from '@ciphera-net/ui' import { getSitesLimitForPlan } from '@/lib/plans' -import DashboardShell from '@/components/dashboard/DashboardShell' type SiteStatsMap = Record @@ -230,7 +229,6 @@ export default function HomePage() { } return ( -
{showFinishSetupBanner && (
@@ -374,6 +372,5 @@ export default function HomePage() {
)}
-
) } diff --git a/components/dashboard/DashboardShell.tsx b/components/dashboard/DashboardShell.tsx index 579a15a..c2c40b3 100644 --- a/components/dashboard/DashboardShell.tsx +++ b/components/dashboard/DashboardShell.tsx @@ -28,6 +28,18 @@ function usePageTitle() { return PAGE_TITLES[segment] ?? (segment ? segment.charAt(0).toUpperCase() + segment.slice(1) : 'Dashboard') } +const HOME_PAGE_TITLES: Record = { + '': 'Your Sites', + integrations: 'Integrations', + pricing: 'Pricing', +} + +function useHomePageTitle() { + const pathname = usePathname() + const segment = pathname.split('/').filter(Boolean)[0] ?? '' + return HOME_PAGE_TITLES[segment] ?? (segment ? segment.charAt(0).toUpperCase() + segment.slice(1) : 'Your Sites') +} + // Load sidebar only on the client — prevents SSR flash const Sidebar = dynamic(() => import('./Sidebar'), { ssr: false, @@ -58,7 +70,8 @@ function GlassTopBar({ siteId }: { siteId: string | null }) { }, [realtime]) const dashboardTitle = usePageTitle() - const pageTitle = siteId ? dashboardTitle : 'Your Sites' + const homeTitle = useHomePageTitle() + const pageTitle = siteId ? dashboardTitle : homeTitle return (
diff --git a/components/sites/SiteList.tsx b/components/sites/SiteList.tsx index a0d2d70..8065672 100644 --- a/components/sites/SiteList.tsx +++ b/components/sites/SiteList.tsx @@ -5,7 +5,7 @@ import Image from 'next/image' import { Site } from '@/lib/api/sites' import type { Stats } from '@/lib/api/stats' import { formatNumber } from '@ciphera-net/ui' -import { BarChartIcon, SettingsIcon, BookOpenIcon, ExternalLinkIcon, Button } from '@ciphera-net/ui' +import { BarChartIcon, SettingsIcon, TrashIcon, BookOpenIcon, ExternalLinkIcon, Button } from '@ciphera-net/ui' import { useAuth } from '@/lib/auth/context' import { FAVICON_SERVICE_URL } from '@/lib/utils/favicon' @@ -104,6 +104,13 @@ function SiteCard({ site, stats, statsLoading, onDelete, canDelete }: SiteCardPr View Dashboard + + + {canDelete && ( )}