From 957376c770ff63b889efcabe3052324cb770be49 Mon Sep 17 00:00:00 2001 From: Chris Olson Date: Tue, 1 Sep 2026 08:56:06 -0400 Subject: [PATCH] Move page titles into the top header bar to reclaim vertical space PageHeader now sets title via context instead of rendering inline. TopBar displays the current page title and subtitle in the left area that was previously an empty spacer. Content area gains ~40px of vertical space on every page. Co-Authored-By: Claude Opus 4.6 --- src/components/layout/DashboardShell.tsx | 21 +++++++------ src/components/layout/TopBar.tsx | 13 ++++++-- src/components/ui/PageHeader.tsx | 17 +++++++---- src/lib/page-title-context.tsx | 39 ++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 src/lib/page-title-context.tsx diff --git a/src/components/layout/DashboardShell.tsx b/src/components/layout/DashboardShell.tsx index 7556801..06be722 100644 --- a/src/components/layout/DashboardShell.tsx +++ b/src/components/layout/DashboardShell.tsx @@ -2,6 +2,7 @@ import { ReactNode } from 'react'; import { DataProvider } from '@/lib/data-context'; +import { PageTitleProvider } from '@/lib/page-title-context'; import { Sidebar } from './Sidebar'; import { TopBar } from './TopBar'; import { MobileActivityFAB } from '@/components/ui/MobileActivityFAB'; @@ -10,16 +11,18 @@ import { DashboardData, DashboardConfig } from '@/types/data'; export function DashboardShell({ children, initialData, config }: { children: ReactNode; initialData: DashboardData; config: DashboardConfig }) { return ( -
- -
- -
- {children} -
+ +
+ +
+ +
+ {children} +
+
+
- -
+ ); } diff --git a/src/components/layout/TopBar.tsx b/src/components/layout/TopBar.tsx index bb88ca3..7768238 100644 --- a/src/components/layout/TopBar.tsx +++ b/src/components/layout/TopBar.tsx @@ -1,6 +1,7 @@ 'use client'; import { useData, DatePreset } from '@/lib/data-context'; +import { usePageTitle } from '@/lib/page-title-context'; import { format } from 'date-fns'; import { useState, useRef, useEffect } from 'react'; import { DISTRICTS } from '@/types/data'; @@ -15,6 +16,7 @@ const DATE_PRESETS: { value: DatePreset; label: string }[] = [ export function TopBar() { const { filters, setDistricts, setDatePreset, refresh, isLoading, lastRefreshed, clearAllFilters, clearCrossFilter } = useData(); + const { title, subtitle } = usePageTitle(); const [showDistrictDropdown, setShowDistrictDropdown] = useState(false); const dropdownRef = useRef(null); @@ -51,8 +53,15 @@ export function TopBar() { AgentMinder
- {/* Desktop spacer */} -
+ {/* Page title */} +
+ {title && ( + <> +

{title}

+ {subtitle && — {subtitle}} + + )} +
{/* Active filters indicator */} {filters.crossFilter && ( diff --git a/src/components/ui/PageHeader.tsx b/src/components/ui/PageHeader.tsx index d0f0292..425d458 100644 --- a/src/components/ui/PageHeader.tsx +++ b/src/components/ui/PageHeader.tsx @@ -1,15 +1,20 @@ 'use client'; +import { useEffect } from 'react'; +import { usePageTitle } from '@/lib/page-title-context'; + interface PageHeaderProps { title: string; subtitle: string; } export function PageHeader({ title, subtitle }: PageHeaderProps) { - return ( -
-

{title}

-

{subtitle}

-
- ); + const { setPageTitle } = usePageTitle(); + + useEffect(() => { + setPageTitle(title, subtitle); + return () => setPageTitle('', ''); + }, [title, subtitle, setPageTitle]); + + return null; } diff --git a/src/lib/page-title-context.tsx b/src/lib/page-title-context.tsx new file mode 100644 index 0000000..f7095b6 --- /dev/null +++ b/src/lib/page-title-context.tsx @@ -0,0 +1,39 @@ +'use client'; + +import { createContext, useContext, useState, useCallback, ReactNode } from 'react'; + +interface PageTitleState { + title: string; + subtitle: string; +} + +interface PageTitleContextValue extends PageTitleState { + setPageTitle: (title: string, subtitle: string) => void; +} + +const PageTitleContext = createContext({ + title: '', + subtitle: '', + setPageTitle: () => {}, +}); + +export function PageTitleProvider({ children }: { children: ReactNode }) { + const [state, setState] = useState({ title: '', subtitle: '' }); + + const setPageTitle = useCallback((title: string, subtitle: string) => { + setState(prev => { + if (prev.title === title && prev.subtitle === subtitle) return prev; + return { title, subtitle }; + }); + }, []); + + return ( + + {children} + + ); +} + +export function usePageTitle() { + return useContext(PageTitleContext); +}