diff --git a/admin-spa/src/App.tsx b/admin-spa/src/App.tsx index d0484c6..ba02008 100644 --- a/admin-spa/src/App.tsx +++ b/admin-spa/src/App.tsx @@ -30,7 +30,10 @@ import SubmenuBar from './components/nav/SubmenuBar'; import DashboardSubmenuBar from './components/nav/DashboardSubmenuBar'; import { DashboardProvider } from '@/contexts/DashboardContext'; import { PageHeaderProvider } from '@/contexts/PageHeaderContext'; +import { FABProvider } from '@/contexts/FABContext'; import { PageHeader } from '@/components/PageHeader'; +import { BottomNav } from '@/components/nav/BottomNav'; +import { FAB } from '@/components/FAB'; import { useActiveSection } from '@/hooks/useActiveSection'; import { NAV_TREE_VERSION } from '@/nav/tree'; import { __ } from '@/lib/i18n'; @@ -192,6 +195,7 @@ import SettingsIndex from '@/routes/Settings'; import SettingsStore from '@/routes/Settings/Store'; import SettingsPayments from '@/routes/Settings/Payments'; import SettingsShipping from '@/routes/Settings/Shipping'; +import MorePage from '@/routes/More'; // Addon Route Component - Dynamically loads addon components function AddonRoute({ config }: { config: any }) { @@ -369,6 +373,9 @@ function AppRoutes() { {/* Customers */} } /> + {/* More */} + } /> + {/* Settings */} } /> } /> @@ -436,12 +443,14 @@ function Shell() { ) : ( )} -
+
+ + ) ) : ( @@ -510,11 +519,13 @@ function AuthWrapper() { } return ( - - - - - + + + + + + + ); } diff --git a/admin-spa/src/components/FAB.tsx b/admin-spa/src/components/FAB.tsx new file mode 100644 index 0000000..3b0a6c0 --- /dev/null +++ b/admin-spa/src/components/FAB.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Plus } from 'lucide-react'; +import { useFAB } from '@/contexts/FABContext'; +import { Button } from '@/components/ui/button'; + +export function FAB() { + const { config } = useFAB(); + + if (!config || config.visible === false) { + return null; + } + + return ( + + ); +} diff --git a/admin-spa/src/components/nav/BottomNav.tsx b/admin-spa/src/components/nav/BottomNav.tsx new file mode 100644 index 0000000..ebf31bd --- /dev/null +++ b/admin-spa/src/components/nav/BottomNav.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { NavLink, useLocation } from 'react-router-dom'; +import { LayoutDashboard, ReceiptText, Package, Users, MoreHorizontal } from 'lucide-react'; +import { __ } from '@/lib/i18n'; + +interface BottomNavItem { + to: string; + icon: React.ReactNode; + label: string; + startsWith?: string; +} + +const navItems: BottomNavItem[] = [ + { + to: '/dashboard', + icon: , + label: __('Dashboard'), + startsWith: '/dashboard' + }, + { + to: '/orders', + icon: , + label: __('Orders'), + startsWith: '/orders' + }, + { + to: '/products', + icon: , + label: __('Products'), + startsWith: '/products' + }, + { + to: '/customers', + icon: , + label: __('Customers'), + startsWith: '/customers' + }, + { + to: '/more', + icon: , + label: __('More'), + startsWith: '/more' + } +]; + +export function BottomNav() { + const location = useLocation(); + + const isActive = (item: BottomNavItem) => { + if (item.startsWith) { + return location.pathname.startsWith(item.startsWith); + } + return location.pathname === item.to; + }; + + return ( + + ); +} diff --git a/admin-spa/src/contexts/FABContext.tsx b/admin-spa/src/contexts/FABContext.tsx new file mode 100644 index 0000000..ab7be87 --- /dev/null +++ b/admin-spa/src/contexts/FABContext.tsx @@ -0,0 +1,43 @@ +import React, { createContext, useContext, useState, ReactNode } from 'react'; + +export interface FABConfig { + icon?: ReactNode; + label: string; + onClick: () => void; + visible?: boolean; + variant?: 'primary' | 'secondary'; +} + +interface FABContextType { + config: FABConfig | null; + setFAB: (config: FABConfig | null) => void; + clearFAB: () => void; +} + +const FABContext = createContext(undefined); + +export function FABProvider({ children }: { children: ReactNode }) { + const [config, setConfig] = useState(null); + + const setFAB = (newConfig: FABConfig | null) => { + setConfig(newConfig); + }; + + const clearFAB = () => { + setConfig(null); + }; + + return ( + + {children} + + ); +} + +export function useFAB() { + const context = useContext(FABContext); + if (!context) { + throw new Error('useFAB must be used within FABProvider'); + } + return context; +} diff --git a/admin-spa/src/hooks/useScrollDirection.ts b/admin-spa/src/hooks/useScrollDirection.ts new file mode 100644 index 0000000..537b8a1 --- /dev/null +++ b/admin-spa/src/hooks/useScrollDirection.ts @@ -0,0 +1,30 @@ +import { useState, useEffect } from 'react'; + +export function useScrollDirection() { + const [scrollDirection, setScrollDirection] = useState<'up' | 'down'>('up'); + const [lastScrollY, setLastScrollY] = useState(0); + + useEffect(() => { + const handleScroll = () => { + const currentScrollY = window.scrollY; + + if (currentScrollY > lastScrollY && currentScrollY > 50) { + // Scrolling down + setScrollDirection('down'); + } else if (currentScrollY < lastScrollY) { + // Scrolling up + setScrollDirection('up'); + } + + setLastScrollY(currentScrollY); + }; + + window.addEventListener('scroll', handleScroll, { passive: true }); + + return () => { + window.removeEventListener('scroll', handleScroll); + }; + }, [lastScrollY]); + + return scrollDirection; +} diff --git a/admin-spa/src/routes/More/index.tsx b/admin-spa/src/routes/More/index.tsx new file mode 100644 index 0000000..28cdbd2 --- /dev/null +++ b/admin-spa/src/routes/More/index.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Tag, Settings as SettingsIcon, ChevronRight } from 'lucide-react'; +import { __ } from '@/lib/i18n'; + +interface MenuItem { + icon: React.ReactNode; + label: string; + description: string; + to: string; +} + +const menuItems: MenuItem[] = [ + { + icon: , + label: __('Coupons'), + description: __('Manage discount codes and promotions'), + to: '/coupons' + }, + { + icon: , + label: __('Settings'), + description: __('Configure your store settings'), + to: '/settings' + } +]; + +export default function MorePage() { + const navigate = useNavigate(); + + return ( +
+ {/* Header */} +
+
+

{__('More')}

+

+ {__('Additional features and settings')} +

+
+
+ + {/* Menu Items */} +
+ {menuItems.map((item) => ( + + ))} +
+
+ ); +}