187 lines
6.3 KiB
TypeScript
187 lines
6.3 KiB
TypeScript
import type { Page } from './Header';
|
|
import type { AuthUser } from '../../hooks/useAuth';
|
|
import { DownloadIcon } from './icons/DownloadIcon';
|
|
import { BookmarkIcon } from './icons/BookmarkIcon';
|
|
import { CheckIcon } from './icons/CheckIcon';
|
|
import { ClipboardIcon } from './icons/ClipboardIcon';
|
|
import { CloseIcon } from './icons/CloseIcon';
|
|
import { SunIcon } from './icons/SunIcon';
|
|
import { MoonIcon } from './icons/MoonIcon';
|
|
import { SpinnerIcon } from './icons/SpinnerIcon';
|
|
|
|
interface MobileMenuProps {
|
|
activePage: Page;
|
|
onPageChange: (page: Page) => void;
|
|
theme: 'light' | 'dark';
|
|
onToggleTheme: () => void;
|
|
onExport: (() => void) | null;
|
|
exporting: boolean;
|
|
onSaveSearch: (() => void) | null;
|
|
savingSearch: boolean;
|
|
user: AuthUser | null;
|
|
onLoginClick: () => void;
|
|
onRegisterClick: () => void;
|
|
onLogout: () => void;
|
|
onClose: () => void;
|
|
onShare: () => void;
|
|
copied: boolean;
|
|
}
|
|
|
|
export default function MobileMenu({
|
|
activePage,
|
|
onPageChange,
|
|
theme,
|
|
onToggleTheme,
|
|
onExport,
|
|
exporting,
|
|
onSaveSearch,
|
|
savingSearch,
|
|
user,
|
|
onLoginClick,
|
|
onRegisterClick,
|
|
onLogout,
|
|
onClose,
|
|
onShare,
|
|
copied,
|
|
}: MobileMenuProps) {
|
|
const mobileNavItem = (page: Page, label: string) => (
|
|
<button
|
|
key={page}
|
|
className={`w-full text-left px-4 py-3 text-base font-medium rounded ${
|
|
activePage === page
|
|
? 'bg-navy-700 text-white'
|
|
: 'text-warm-300 hover:bg-navy-800 hover:text-white'
|
|
}`}
|
|
onClick={() => {
|
|
onPageChange(page);
|
|
onClose();
|
|
}}
|
|
>
|
|
{label}
|
|
</button>
|
|
);
|
|
|
|
return (
|
|
<>
|
|
{/* Backdrop */}
|
|
<div className="fixed inset-0 bg-black/50 z-40" onClick={onClose} />
|
|
{/* Menu panel */}
|
|
<div className="fixed top-0 right-0 bottom-0 w-64 bg-navy-900 z-50 flex flex-col shadow-xl">
|
|
<div className="flex items-center justify-between px-4 h-12 border-b border-navy-700">
|
|
<span className="font-semibold">Menu</span>
|
|
<button
|
|
onClick={onClose}
|
|
className="flex items-center justify-center w-10 h-10 -mr-2 rounded hover:bg-navy-800 transition-colors"
|
|
aria-label="Close menu"
|
|
>
|
|
<CloseIcon className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
<nav className="flex-1 flex flex-col gap-1 p-3 overflow-y-auto">
|
|
{mobileNavItem('dashboard', 'Dashboard')}
|
|
{user && mobileNavItem('saved-searches', 'Saved')}
|
|
{mobileNavItem('data-sources', 'Data Sources')}
|
|
{mobileNavItem('faq', 'FAQ')}
|
|
{mobileNavItem('pricing', 'Pricing')}
|
|
|
|
{/* Dashboard actions */}
|
|
{activePage === 'dashboard' && (
|
|
<div className="mt-3 pt-3 border-t border-navy-700 flex flex-col gap-1">
|
|
{onSaveSearch && (
|
|
<button
|
|
onClick={() => {
|
|
onSaveSearch();
|
|
onClose();
|
|
}}
|
|
disabled={savingSearch}
|
|
className="w-full flex items-center gap-2 px-4 py-3 text-base text-warm-300 hover:bg-navy-800 hover:text-white rounded disabled:opacity-50"
|
|
>
|
|
{savingSearch ? (
|
|
<SpinnerIcon className="w-5 h-5 animate-spin" />
|
|
) : (
|
|
<BookmarkIcon className="w-5 h-5" />
|
|
)}
|
|
Save
|
|
</button>
|
|
)}
|
|
<button
|
|
onClick={() => {
|
|
onShare();
|
|
onClose();
|
|
}}
|
|
className="w-full flex items-center gap-2 px-4 py-3 text-base text-warm-300 hover:bg-navy-800 hover:text-white rounded"
|
|
>
|
|
{copied ? <CheckIcon className="w-5 h-5" /> : <ClipboardIcon className="w-5 h-5" />}
|
|
{copied ? 'Copied!' : 'Share'}
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
onExport?.();
|
|
onClose();
|
|
}}
|
|
disabled={!onExport || exporting}
|
|
className="w-full flex items-center gap-2 px-4 py-3 text-base text-warm-300 hover:bg-navy-800 hover:text-white rounded disabled:opacity-50"
|
|
>
|
|
<DownloadIcon className="w-5 h-5" />
|
|
{exporting ? 'Exporting...' : 'Export'}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</nav>
|
|
|
|
{/* Theme toggle + Auth section at bottom */}
|
|
<div className="p-3 border-t border-navy-700 flex flex-col gap-3">
|
|
{/* Theme toggle */}
|
|
<button
|
|
onClick={() => {
|
|
onToggleTheme();
|
|
}}
|
|
className="w-full flex items-center gap-3 px-4 py-3 text-base text-warm-300 hover:bg-navy-800 hover:text-white rounded transition-colors"
|
|
>
|
|
{theme === 'light' ? <SunIcon className="w-5 h-5" /> : <MoonIcon className="w-5 h-5" />}
|
|
<span>Theme: {theme === 'light' ? 'Light' : 'Dark'}</span>
|
|
</button>
|
|
|
|
{/* Auth buttons */}
|
|
<div>
|
|
{user ? (
|
|
<div className="flex items-center justify-between px-4 py-2">
|
|
<span className="text-sm text-warm-300 truncate">{user.email}</span>
|
|
<button
|
|
onClick={() => {
|
|
onLogout();
|
|
onClose();
|
|
}}
|
|
className="text-sm text-warm-400 hover:text-white"
|
|
>
|
|
Log out
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<div className="flex gap-2">
|
|
<button
|
|
onClick={() => {
|
|
onLoginClick();
|
|
onClose();
|
|
}}
|
|
className="flex-1 px-3 py-2.5 rounded bg-navy-800 hover:bg-navy-700 transition-colors text-sm text-center"
|
|
>
|
|
Log in
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
onRegisterClick();
|
|
onClose();
|
|
}}
|
|
className="flex-1 px-3 py-2.5 rounded bg-teal-600 hover:bg-teal-700 transition-colors text-sm font-medium text-center"
|
|
>
|
|
Register
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|