From 0dace905971deada11ae53078ca78741277f0b3d Mon Sep 17 00:00:00 2001 From: dwindown Date: Thu, 6 Nov 2025 23:11:59 +0700 Subject: [PATCH] refactor: Smart contextual headers - only when they add value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented intelligent header rules based on user feedback. Problem Analysis: 1. Dashboard submenu tabs already show page names (Overview, Revenue, Orders...) 2. Showing "Orders" header is ambiguous (Analytics or Management?) 3. Wasted vertical space for redundant information 4. FAB already handles actions on management pages Solution: Headers ONLY When They Add Value Rules Implemented: 1. Dashboard Pages: NO HEADERS - Submenu tabs are sufficient - Saves vertical space - No ambiguity Before: Dashboard → Overview = "Dashboard" header (redundant!) Dashboard → Orders = "Orders" header (confusing!) After: Dashboard → Overview = No header (tabs show "Overview") Dashboard → Orders = No header (tabs show "Orders") 2. Settings Pages: HEADERS ONLY WITH ACTIONS - Store Details + [Save] = Show header ✓ - Payments + [Refresh] = Show header ✓ - Pages without actions = No header (save space) Logic: If there is an action button, we need a place to put it → header If no action button, header is just wasting space → remove it 3. Management Pages: NO HEADERS - FAB handles actions ([+ Add Order]) - No need for redundant header with action button 4. Payments Exception: REMOVED - Treat Payments like any other settings page - Has action (Refresh) = show header - Consistent with other pages Implementation: Dashboard Pages (7 files): - Removed usePageHeader hook - Removed useEffect for setting header - Removed unused imports (useEffect, usePageHeader) - Result: Clean, no headers, tabs are enough PageHeader Component: - Removed Payments special case detection - Removed useLocation import - Simplified logic: hideOnDesktop prop only SettingsLayout Component: - Changed logic: Only set header when onSave OR action exists - If no action: clearPageHeader() instead of setPageHeader(title) - Result: Headers only appear when needed Benefits: ✅ Saves vertical space (no redundant headers) ✅ No ambiguity (Dashboard Orders vs Orders Management) ✅ Consistent logic (action = header, no action = no header) ✅ Cleaner UI (less visual clutter) ✅ FAB handles management page actions Files Modified: - Dashboard/index.tsx (Overview) - Dashboard/Revenue.tsx - Dashboard/Orders.tsx - Dashboard/Products.tsx - Dashboard/Customers.tsx - Dashboard/Coupons.tsx - Dashboard/Taxes.tsx - PageHeader.tsx - SettingsLayout.tsx Result: Smart headers that only appear when they add value! 🎯 --- admin-spa/src/components/PageHeader.tsx | 9 ++------- admin-spa/src/routes/Dashboard/Coupons.tsx | 9 +-------- admin-spa/src/routes/Dashboard/Customers.tsx | 11 ++--------- admin-spa/src/routes/Dashboard/Orders.tsx | 11 ++--------- admin-spa/src/routes/Dashboard/Products.tsx | 11 ++--------- admin-spa/src/routes/Dashboard/Revenue.tsx | 11 ++--------- admin-spa/src/routes/Dashboard/Taxes.tsx | 9 +-------- admin-spa/src/routes/Dashboard/index.tsx | 10 +--------- .../src/routes/Settings/components/SettingsLayout.tsx | 7 ++++--- 9 files changed, 17 insertions(+), 71 deletions(-) diff --git a/admin-spa/src/components/PageHeader.tsx b/admin-spa/src/components/PageHeader.tsx index a4b0cf1..d8d95df 100644 --- a/admin-spa/src/components/PageHeader.tsx +++ b/admin-spa/src/components/PageHeader.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { useLocation } from 'react-router-dom'; import { usePageHeader } from '@/contexts/PageHeaderContext'; interface PageHeaderProps { @@ -9,18 +8,14 @@ interface PageHeaderProps { export function PageHeader({ fullscreen = false, hideOnDesktop = false }: PageHeaderProps) { const { title, action } = usePageHeader(); - const location = useLocation(); if (!title) return null; - // Special case: Payments page should hide header on desktop (has its own layout) - const isPaymentsPage = location.pathname.startsWith('/settings/payments'); - const shouldHideOnDesktop = hideOnDesktop || isPaymentsPage; - // PageHeader is now ABOVE submenu in DOM order // z-20 ensures it stays on top when both are sticky + // Only hide on desktop if explicitly requested (for mobile-only headers) return ( -
+

{title}

diff --git a/admin-spa/src/routes/Dashboard/Coupons.tsx b/admin-spa/src/routes/Dashboard/Coupons.tsx index 69bf718..e27b3b4 100644 --- a/admin-spa/src/routes/Dashboard/Coupons.tsx +++ b/admin-spa/src/routes/Dashboard/Coupons.tsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useEffect } from 'react'; +import React, { useState, useMemo } from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'; import { Tag, DollarSign, TrendingUp, ShoppingCart } from 'lucide-react'; import { __ } from '@/lib/i18n'; @@ -12,18 +12,11 @@ import { ChartCard } from './components/ChartCard'; import { DataTable, Column } from './components/DataTable'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { DUMMY_COUPONS_DATA, CouponsData, CouponPerformance } from './data/dummyCoupons'; -import { usePageHeader } from '@/contexts/PageHeaderContext'; export default function CouponsReport() { - const { setPageHeader, clearPageHeader } = usePageHeader(); const { period } = useDashboardPeriod(); const store = getStoreCurrency(); - useEffect(() => { - setPageHeader(__('Coupons')); - return () => clearPageHeader(); - }, [setPageHeader, clearPageHeader]); - // Fetch real data or use dummy data based on toggle const { data, isLoading, error, refetch } = useCouponsAnalytics(DUMMY_COUPONS_DATA); diff --git a/admin-spa/src/routes/Dashboard/Customers.tsx b/admin-spa/src/routes/Dashboard/Customers.tsx index 1fa0c28..6395091 100644 --- a/admin-spa/src/routes/Dashboard/Customers.tsx +++ b/admin-spa/src/routes/Dashboard/Customers.tsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useEffect } from 'react'; +import React, { useState, useMemo } from 'react'; import { BarChart, Bar, LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'; import { Users, TrendingUp, DollarSign, ShoppingCart, UserPlus, UserCheck, Info } from 'lucide-react'; import { __ } from '@/lib/i18n'; @@ -12,18 +12,11 @@ import { ChartCard } from './components/ChartCard'; import { DataTable, Column } from './components/DataTable'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { DUMMY_CUSTOMERS_DATA, CustomersData, TopCustomer } from './data/dummyCustomers'; -import { usePageHeader } from '@/contexts/PageHeaderContext'; export default function CustomersAnalytics() { - const { setPageHeader, clearPageHeader } = usePageHeader(); const { period } = useDashboardPeriod(); const store = getStoreCurrency(); - useEffect(() => { - setPageHeader(__('Customers')); - return () => clearPageHeader(); - }, [setPageHeader, clearPageHeader]); - // Fetch real data or use dummy data based on toggle const { data, isLoading, error, refetch } = useCustomersAnalytics(DUMMY_CUSTOMERS_DATA); @@ -247,7 +240,7 @@ export default function CustomersAnalytics() { ]; return ( -
+
{/* Header */}

{__('Customers Analytics')}

diff --git a/admin-spa/src/routes/Dashboard/Orders.tsx b/admin-spa/src/routes/Dashboard/Orders.tsx index 6662519..57e51fe 100644 --- a/admin-spa/src/routes/Dashboard/Orders.tsx +++ b/admin-spa/src/routes/Dashboard/Orders.tsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useRef, useEffect } from 'react'; +import React, { useState, useMemo, useRef } from 'react'; import { BarChart, Bar, LineChart, Line, AreaChart, Area, ComposedChart, PieChart, Pie, Cell, Label, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'; import { ShoppingCart, TrendingUp, Package, XCircle, DollarSign, CheckCircle, Clock } from 'lucide-react'; import { __ } from '@/lib/i18n'; @@ -12,20 +12,13 @@ import { ChartCard } from './components/ChartCard'; import { DataTable, Column } from './components/DataTable'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { DUMMY_ORDERS_DATA, OrdersData } from './data/dummyOrders'; -import { usePageHeader } from '@/contexts/PageHeaderContext'; export default function OrdersAnalytics() { - const { setPageHeader, clearPageHeader } = usePageHeader(); const { period } = useDashboardPeriod(); const store = getStoreCurrency(); const [hoverIndex, setHoverIndex] = useState(undefined); const chartRef = useRef(null); - useEffect(() => { - setPageHeader(__('Orders')); - return () => clearPageHeader(); - }, [setPageHeader, clearPageHeader]); - // Fetch real data or use dummy data based on toggle const { data, isLoading, error, refetch } = useOrdersAnalytics(DUMMY_ORDERS_DATA); @@ -165,7 +158,7 @@ export default function OrdersAnalytics() { }; return ( -
+
{/* Header */}

{__('Orders Analytics')}

diff --git a/admin-spa/src/routes/Dashboard/Products.tsx b/admin-spa/src/routes/Dashboard/Products.tsx index cc6aa42..0729f6d 100644 --- a/admin-spa/src/routes/Dashboard/Products.tsx +++ b/admin-spa/src/routes/Dashboard/Products.tsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useEffect } from 'react'; +import React, { useState, useMemo } from 'react'; import { Package, TrendingUp, DollarSign, AlertTriangle, XCircle } from 'lucide-react'; import { __ } from '@/lib/i18n'; import { formatMoney, getStoreCurrency } from '@/lib/currency'; @@ -12,18 +12,11 @@ import { DataTable, Column } from './components/DataTable'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { DUMMY_PRODUCTS_DATA, ProductsData, TopProduct, ProductByCategory, StockAnalysisProduct } from './data/dummyProducts'; -import { usePageHeader } from '@/contexts/PageHeaderContext'; export default function ProductsPerformance() { - const { setPageHeader, clearPageHeader } = usePageHeader(); const { period } = useDashboardPeriod(); const store = getStoreCurrency(); - useEffect(() => { - setPageHeader(__('Products')); - return () => clearPageHeader(); - }, [setPageHeader, clearPageHeader]); - // Fetch real data or use dummy data based on toggle const { data, isLoading, error, refetch } = useProductsAnalytics(DUMMY_PRODUCTS_DATA); @@ -192,7 +185,7 @@ export default function ProductsPerformance() { ]; return ( -
+
{/* Header */}

{__('Products Performance')}

diff --git a/admin-spa/src/routes/Dashboard/Revenue.tsx b/admin-spa/src/routes/Dashboard/Revenue.tsx index 87db4e4..65ebacd 100644 --- a/admin-spa/src/routes/Dashboard/Revenue.tsx +++ b/admin-spa/src/routes/Dashboard/Revenue.tsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useEffect } from 'react'; +import React, { useState, useMemo } from 'react'; import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'; import { DollarSign, TrendingUp, TrendingDown, CreditCard, Truck, RefreshCw } from 'lucide-react'; import { __ } from '@/lib/i18n'; @@ -13,19 +13,12 @@ import { DataTable, Column } from './components/DataTable'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { DUMMY_REVENUE_DATA, RevenueData, RevenueByProduct, RevenueByCategory, RevenueByPaymentMethod, RevenueByShippingMethod } from './data/dummyRevenue'; -import { usePageHeader } from '@/contexts/PageHeaderContext'; export default function RevenueAnalytics() { - const { setPageHeader, clearPageHeader } = usePageHeader(); const { period } = useDashboardPeriod(); const [granularity, setGranularity] = useState<'day' | 'week' | 'month'>('day'); const store = getStoreCurrency(); - useEffect(() => { - setPageHeader(__('Revenue')); - return () => clearPageHeader(); - }, [setPageHeader, clearPageHeader]); - // Fetch real data or use dummy data based on toggle const { data, isLoading, error, refetch } = useRevenueAnalytics(DUMMY_REVENUE_DATA, granularity); @@ -343,7 +336,7 @@ export default function RevenueAnalytics() { ]; return ( -
+
{/* Header */}

{__('Revenue Analytics')}

diff --git a/admin-spa/src/routes/Dashboard/Taxes.tsx b/admin-spa/src/routes/Dashboard/Taxes.tsx index 96cac63..4c3c9b1 100644 --- a/admin-spa/src/routes/Dashboard/Taxes.tsx +++ b/admin-spa/src/routes/Dashboard/Taxes.tsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useEffect } from 'react'; +import React, { useState, useMemo } from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { DollarSign, FileText, ShoppingCart, TrendingUp } from 'lucide-react'; import { __ } from '@/lib/i18n'; @@ -12,18 +12,11 @@ import { ChartCard } from './components/ChartCard'; import { DataTable, Column } from './components/DataTable'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { DUMMY_TAXES_DATA, TaxesData, TaxByRate, TaxByLocation } from './data/dummyTaxes'; -import { usePageHeader } from '@/contexts/PageHeaderContext'; export default function TaxesReport() { - const { setPageHeader, clearPageHeader } = usePageHeader(); const { period } = useDashboardPeriod(); const store = getStoreCurrency(); - useEffect(() => { - setPageHeader(__('Taxes')); - return () => clearPageHeader(); - }, [setPageHeader, clearPageHeader]); - // Fetch real data or use dummy data based on toggle const { data, isLoading, error, refetch } = useTaxesAnalytics(DUMMY_TAXES_DATA); diff --git a/admin-spa/src/routes/Dashboard/index.tsx b/admin-spa/src/routes/Dashboard/index.tsx index 09d3865..203c438 100644 --- a/admin-spa/src/routes/Dashboard/index.tsx +++ b/admin-spa/src/routes/Dashboard/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useRef, useEffect } from 'react'; +import React, { useState, useMemo, useRef } from 'react'; import { Link } from 'react-router-dom'; import { AreaChart, Area, BarChart, Bar, LineChart, Line, ComposedChart, PieChart, Pie, Cell, Sector, Label, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'; import { TrendingUp, TrendingDown, ShoppingCart, DollarSign, Package, Users, AlertTriangle, ArrowUpRight } from 'lucide-react'; @@ -12,7 +12,6 @@ import { useOverviewAnalytics } from '@/hooks/useAnalytics'; import { ErrorCard } from '@/components/ErrorCard'; import { getPageLoadErrorMessage } from '@/lib/errorHandling'; import { useFABConfig } from '@/hooks/useFABConfig'; -import { usePageHeader } from '@/contexts/PageHeaderContext'; // Dummy data for visualization const DUMMY_DATA = { @@ -161,19 +160,12 @@ function MetricCard({ title, value, change, icon: Icon, format = 'number', perio export default function Dashboard() { useFABConfig('dashboard'); // Add FAB for quick actions - const { setPageHeader, clearPageHeader } = usePageHeader(); const { period } = useDashboardPeriod(); const store = getStoreCurrency(); const [hoverIndex, setHoverIndex] = useState(undefined); const [chartMetric, setChartMetric] = useState<'both' | 'revenue' | 'orders'>('both'); const chartRef = useRef(null); - // Set contextual header - useEffect(() => { - setPageHeader(__('Dashboard')); - return () => clearPageHeader(); - }, [setPageHeader, clearPageHeader]); - // Fetch real data or use dummy data based on toggle const { data, isLoading, error, refetch } = useOverviewAnalytics(DUMMY_DATA); diff --git a/admin-spa/src/routes/Settings/components/SettingsLayout.tsx b/admin-spa/src/routes/Settings/components/SettingsLayout.tsx index ab6e095..acf3cc1 100644 --- a/admin-spa/src/routes/Settings/components/SettingsLayout.tsx +++ b/admin-spa/src/routes/Settings/components/SettingsLayout.tsx @@ -35,7 +35,8 @@ export function SettingsLayout({ } }; - // Set page header when component mounts + // Set page header ONLY when there's an action (Save button or custom action) + // This saves vertical space on pages without actions useEffect(() => { if (onSave) { setPageHeader( @@ -59,8 +60,8 @@ export function SettingsLayout({ // If there's a custom action, use it setPageHeader(title, action); } else { - // Always set the title, even without action - setPageHeader(title); + // No action = no header (save vertical space) + clearPageHeader(); } return () => clearPageHeader();