This implements the mobile optimization agent (P3 - Enhancement) with: PWA Configuration: - Add next-pwa integration with offline caching strategies - Create web app manifest for installability - Add service worker with background sync support - Create offline fallback page Mobile Components: - BottomNav: Touch-friendly bottom navigation bar - MobileHeader: Responsive header with back navigation - InstallPrompt: Smart PWA install prompt (iOS & Android) - SwipeableCard: Gesture-based swipeable cards - PullToRefresh: Native-like pull to refresh - QRScanner: Camera-based QR code scanning Mobile Library: - camera.ts: Camera access and photo capture utilities - offline.ts: IndexedDB-based offline storage and sync - gestures.ts: Touch gesture detection (swipe, pinch, tap) - pwa.ts: PWA status, install prompts, service worker management Mobile Pages: - /m: Mobile dashboard with quick actions and stats - /m/scan: QR code scanner for plant lookup - /m/quick-add: Streamlined plant registration form - /m/profile: User profile with offline status Dependencies added: next-pwa, idb
101 lines
3.5 KiB
TypeScript
101 lines
3.5 KiB
TypeScript
import * as React from 'react';
|
|
import Link from 'next/link';
|
|
import { useRouter } from 'next/router';
|
|
|
|
interface MobileHeaderProps {
|
|
title?: string;
|
|
showBack?: boolean;
|
|
rightAction?: React.ReactNode;
|
|
}
|
|
|
|
export function MobileHeader({ title, showBack = false, rightAction }: MobileHeaderProps) {
|
|
const router = useRouter();
|
|
|
|
const handleBack = () => {
|
|
if (window.history.length > 1) {
|
|
router.back();
|
|
} else {
|
|
router.push('/m');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<header className="fixed top-0 left-0 right-0 z-50 bg-white border-b border-gray-200 pt-safe md:hidden">
|
|
<div className="flex items-center justify-between h-14 px-4">
|
|
{/* Left side */}
|
|
<div className="flex items-center w-20">
|
|
{showBack ? (
|
|
<button
|
|
onClick={handleBack}
|
|
className="flex items-center justify-center w-10 h-10 -ml-2 rounded-full hover:bg-gray-100 active:bg-gray-200"
|
|
aria-label="Go back"
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
className="h-6 w-6 text-gray-700"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
|
</svg>
|
|
</button>
|
|
) : (
|
|
<Link href="/m">
|
|
<a className="flex items-center space-x-2">
|
|
<div className="w-8 h-8 bg-green-600 rounded-lg flex items-center justify-center">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
className="h-5 w-5 text-white"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
</a>
|
|
</Link>
|
|
)}
|
|
</div>
|
|
|
|
{/* Center - Title */}
|
|
<div className="flex-1 text-center">
|
|
<h1 className="text-lg font-semibold text-gray-900 truncate">{title || 'LocalGreenChain'}</h1>
|
|
</div>
|
|
|
|
{/* Right side */}
|
|
<div className="flex items-center justify-end w-20">
|
|
{rightAction || (
|
|
<button
|
|
className="flex items-center justify-center w-10 h-10 -mr-2 rounded-full hover:bg-gray-100 active:bg-gray-200"
|
|
aria-label="Notifications"
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
className="h-6 w-6 text-gray-700"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|
|
|
|
export default MobileHeader;
|