feat: Mobile-only contextual headers + consistent button sizing

Implemented 3 key improvements based on user feedback:

1.  PageHeader Mobile-Only
   Problem: Contextual header showing on desktop was redundant
   Solution: Added md:hidden to PageHeader component

   Before:
   Desktop: Shows "Store Details" header (redundant with nav)
   Mobile: Shows "Store Details" header (good!)

   After:
   Desktop: No contextual header (clean!)
   Mobile: Shows "Store Details" header (perfect!)

   Result: Cleaner desktop UI, mobile gets contextual clarity

2.  Contextual Headers on All Pages
   Problem: Dashboard and Payments pages missing contextual headers
   Solution:
   - Added usePageHeader to Dashboard
   - Fixed SettingsLayout to always set header (not just when onSave exists)

   Before:
   - Dashboard: No header (confusing)
   - Payments: No header (confusing)
   - Store Details: Has header (only one working)

   After:
   - Dashboard: "Dashboard" header ✓
   - Payments: "Payments" header ✓
   - Store Details: "Store Details" header ✓
   - All settings pages: Contextual headers ✓

   Result: Consistent UX across all pages!

3.  Re-added .ui-ctrl to Button
   Problem: Removed .ui-ctrl earlier, but it's needed for mobile sizing
   Solution: Added .ui-ctrl back to Button component

   Why .ui-ctrl is Good:
   - Mobile: 44px height (good touch target)
   - Desktop: 36px height (compact, efficient)
   - Responsive by default
   - Follows UI/UX best practices

   Result: Buttons properly sized for touch on mobile!

Mobile Layout (Final):
┌─────────────────────────────────┐
│ Dashboard                       │ ← Contextual header!
├─────────────────────────────────┤
│ Overview | Revenue | Orders ... │ ← Submenu
├─────────────────────────────────┤
│ Last 7 days          [Refresh]  │
├─────────────────────────────────┤
│ Revenue                         │
│ Rp64.500                        │
│ 99.9% vs previous 7 days        │
│                          ( + )  │ ← FAB
├─────────────────────────────────┤
│ Bottom Nav                      │
└─────────────────────────────────┘

Desktop Layout (Final):
┌─────────────────────────────────┐
│ Header                          │
├─────────────────────────────────┤
│ Dashboard | Orders | Products   │ ← Top Nav
├─────────────────────────────────┤
│ Overview | Revenue | Orders ... │ ← Submenu
├─────────────────────────────────┤
│ (No contextual header)          │ ← Clean!
├─────────────────────────────────┤
│ Revenue                         │
│ Rp64.500                        │
└─────────────────────────────────┘

Files Modified:
- PageHeader.tsx: Added md:hidden for mobile-only
- Dashboard/index.tsx: Added contextual header
- SettingsLayout.tsx: Always set header (not just with onSave)
- button.tsx: Re-added .ui-ctrl class

Result:
 Mobile: Contextual headers on all pages
 Desktop: Clean, no redundant headers
 Buttons: Proper touch targets (44px mobile, 36px desktop)
 Consistent UX across all pages! 🎉
This commit is contained in:
dwindown
2025-11-06 22:45:47 +07:00
parent a779f9a226
commit 97288a41dc
4 changed files with 17 additions and 4 deletions

View File

@@ -12,8 +12,9 @@ export function PageHeader({ fullscreen = false }: PageHeaderProps) {
// PageHeader is now ABOVE submenu in DOM order // PageHeader is now ABOVE submenu in DOM order
// z-20 ensures it stays on top when both are sticky // z-20 ensures it stays on top when both are sticky
// Mobile-only: hidden on desktop (md:hidden)
return ( return (
<div className="sticky top-0 z-20 border-b bg-background"> <div className="sticky top-0 z-20 border-b bg-background md:hidden">
<div className="w-full max-w-5xl mx-auto px-4 py-3 flex items-center justify-between min-w-0"> <div className="w-full max-w-5xl mx-auto px-4 py-3 flex items-center justify-between min-w-0">
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<h1 className="text-lg font-semibold truncate">{title}</h1> <h1 className="text-lg font-semibold truncate">{title}</h1>

View File

@@ -45,7 +45,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
const Comp = asChild ? Slot : "button" const Comp = asChild ? Slot : "button"
return ( return (
<Comp <Comp
className={cn(buttonVariants({ variant, size, className }))} className={cn('ui-ctrl', buttonVariants({ variant, size, className }))}
ref={ref} ref={ref}
{...props} {...props}
/> />

View File

@@ -12,6 +12,7 @@ import { useOverviewAnalytics } from '@/hooks/useAnalytics';
import { ErrorCard } from '@/components/ErrorCard'; import { ErrorCard } from '@/components/ErrorCard';
import { getPageLoadErrorMessage } from '@/lib/errorHandling'; import { getPageLoadErrorMessage } from '@/lib/errorHandling';
import { useFABConfig } from '@/hooks/useFABConfig'; import { useFABConfig } from '@/hooks/useFABConfig';
import { usePageHeader } from '@/contexts/PageHeaderContext';
// Dummy data for visualization // Dummy data for visualization
const DUMMY_DATA = { const DUMMY_DATA = {
@@ -160,12 +161,19 @@ function MetricCard({ title, value, change, icon: Icon, format = 'number', perio
export default function Dashboard() { export default function Dashboard() {
useFABConfig('dashboard'); // Add FAB for quick actions useFABConfig('dashboard'); // Add FAB for quick actions
const { setPageHeader, clearPageHeader } = usePageHeader();
const { period } = useDashboardPeriod(); const { period } = useDashboardPeriod();
const store = getStoreCurrency(); const store = getStoreCurrency();
const [hoverIndex, setHoverIndex] = useState<number | undefined>(undefined); const [hoverIndex, setHoverIndex] = useState<number | undefined>(undefined);
const [chartMetric, setChartMetric] = useState<'both' | 'revenue' | 'orders'>('both'); const [chartMetric, setChartMetric] = useState<'both' | 'revenue' | 'orders'>('both');
const chartRef = useRef<any>(null); const chartRef = useRef<any>(null);
// Set contextual header
useEffect(() => {
setPageHeader(__('Dashboard'));
return () => clearPageHeader();
}, [setPageHeader, clearPageHeader]);
// Fetch real data or use dummy data based on toggle // Fetch real data or use dummy data based on toggle
const { data, isLoading, error, refetch } = useOverviewAnalytics(DUMMY_DATA); const { data, isLoading, error, refetch } = useOverviewAnalytics(DUMMY_DATA);

View File

@@ -55,12 +55,16 @@ export function SettingsLayout({
)} )}
</Button> </Button>
); );
} else if (action) {
// If there's a custom action, use it
setPageHeader(title, action);
} else { } else {
clearPageHeader(); // Always set the title, even without action
setPageHeader(title);
} }
return () => clearPageHeader(); return () => clearPageHeader();
}, [title, onSave, isSaving, isLoading, saveLabel]); }, [title, onSave, isSaving, isLoading, saveLabel, action, setPageHeader, clearPageHeader]);
return ( return (
<div className="space-y-6 min-w-0"> <div className="space-y-6 min-w-0">