first commit
This commit is contained in:
193
components/layout/MegaMenu.tsx
Normal file
193
components/layout/MegaMenu.tsx
Normal file
@@ -0,0 +1,193 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { Wheat, Leaf, ShoppingBag, Building2, Settings, LucideIcon } from 'lucide-react'
|
||||
|
||||
interface MegaMenuItem {
|
||||
name: string
|
||||
href: string
|
||||
description: string
|
||||
}
|
||||
|
||||
interface MegaMenuCategory {
|
||||
name: string
|
||||
id?: string
|
||||
icon: LucideIcon
|
||||
items: MegaMenuItem[]
|
||||
}
|
||||
|
||||
interface MegaMenuData {
|
||||
title: string
|
||||
type: 'categories'
|
||||
categories: MegaMenuCategory[]
|
||||
}
|
||||
|
||||
interface MegaMenuProps {
|
||||
menuKey: string
|
||||
menu: MegaMenuData
|
||||
isActive: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export default function MegaMenu({ menuKey, menu, isActive, onClose }: MegaMenuProps) {
|
||||
const [latestProducts, setLatestProducts] = useState<MegaMenuItem[]>([])
|
||||
const [isLoadingProducts, setIsLoadingProducts] = useState(true)
|
||||
|
||||
// Fetch 4 latest products for Products menu on component mount
|
||||
useEffect(() => {
|
||||
if (menuKey === 'products') {
|
||||
const fetchLatestProducts = async () => {
|
||||
try {
|
||||
setIsLoadingProducts(true)
|
||||
|
||||
// Fetch latest 4 products directly
|
||||
const productsResponse = await fetch('/api/products?limit=4&sort=latest')
|
||||
const productsData = await productsResponse.json()
|
||||
|
||||
if (productsData && productsData.products) {
|
||||
const products = productsData.products.map((product: any) => ({
|
||||
name: product.name,
|
||||
href: `/products/${product.slug}`,
|
||||
description: product.description?.substring(0, 50) + '...' || 'Premium quality product'
|
||||
}))
|
||||
|
||||
setLatestProducts(products)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching latest products:', error)
|
||||
// Fallback to static data if API fails
|
||||
setLatestProducts([
|
||||
{ name: 'Premium Basmati Rice', href: '/products', description: 'Aromatic long-grain rice' },
|
||||
{ name: 'Kashmina Rice', href: '/products', description: 'Premium quality rice' },
|
||||
{ name: 'Non-Basmati Rice', href: '/products', description: 'Traditional varieties' },
|
||||
{ name: 'Multigrain Flour', href: '/products', description: 'Nutritious blend' },
|
||||
])
|
||||
} finally {
|
||||
setIsLoadingProducts(false)
|
||||
}
|
||||
}
|
||||
|
||||
fetchLatestProducts()
|
||||
} else {
|
||||
setIsLoadingProducts(false)
|
||||
}
|
||||
}, [menuKey]) // Only depend on menuKey, not isActive
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isActive && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 10 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="absolute top-full mt-2 bg-white rounded-xl shadow-2xl border border-slate-200/50 backdrop-blur-lg overflow-hidden z-50 right-0"
|
||||
style={{
|
||||
width: 'auto',
|
||||
minWidth: menuKey === 'products' ? '320px' : '280px',
|
||||
maxWidth: '90vw'
|
||||
}}
|
||||
>
|
||||
<div className="p-3 sm:p-4">
|
||||
{menuKey === 'products' ? (
|
||||
// Latest Products - Simple List
|
||||
isLoadingProducts ? (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2 pb-1.5 border-b border-slate-100">
|
||||
<div className="w-4 h-4 bg-slate-200 rounded animate-pulse"></div>
|
||||
<div className="h-4 bg-slate-200 rounded w-32 animate-pulse"></div>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{[1, 2, 3, 4].map((item) => (
|
||||
<div key={item} className="p-2">
|
||||
<div className="h-3 bg-slate-200 rounded w-full mb-1 animate-pulse"></div>
|
||||
<div className="h-2 bg-slate-100 rounded w-3/4 animate-pulse"></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2 pb-1.5 border-b border-slate-100">
|
||||
<ShoppingBag className="w-4 h-4 text-emerald-600 flex-shrink-0" />
|
||||
<h3 className="font-semibold text-slate-800 text-sm">Latest Products</h3>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{latestProducts.map((product, idx) => (
|
||||
<Link
|
||||
key={idx}
|
||||
href={product.href}
|
||||
className="block p-2 rounded-lg hover:bg-slate-50 transition-colors duration-200 group"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div className="font-medium text-slate-800 group-hover:text-emerald-600 transition-colors duration-200 text-sm truncate">
|
||||
{product.name}
|
||||
</div>
|
||||
<div className="text-xs text-slate-500 truncate">
|
||||
{product.description}
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
// Other menu types - Categories
|
||||
menu.type === 'categories' && (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
|
||||
{menu.categories?.map((category, idx) => {
|
||||
const IconComponent = category.icon
|
||||
return (
|
||||
<div key={idx} className="space-y-2 sm:space-y-3">
|
||||
<div className="flex items-center space-x-2 pb-1.5 border-b border-slate-100">
|
||||
<IconComponent className="w-4 h-4 text-emerald-600 flex-shrink-0" />
|
||||
<h3 className="font-semibold text-slate-800 text-sm truncate">{category.name}</h3>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{category.items?.map((item, itemIdx) => (
|
||||
<Link
|
||||
key={itemIdx}
|
||||
href={item.href}
|
||||
className="block p-2 rounded-lg hover:bg-slate-50 transition-colors duration-200 group"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div className="font-medium text-slate-800 group-hover:text-emerald-600 transition-colors duration-200 text-sm truncate">
|
||||
{item.name}
|
||||
</div>
|
||||
<div className="text-xs text-slate-500 truncate">
|
||||
{item.description}
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
{/* View All Products Button - Only for Products Menu */}
|
||||
{menuKey === 'products' && !isLoadingProducts && (
|
||||
<div className="mt-4 pt-3 border-t border-slate-100">
|
||||
<Link
|
||||
href="/products"
|
||||
className="block w-full p-3 rounded-lg bg-gradient-to-r from-emerald-600 to-blue-600 text-white hover:from-emerald-700 hover:to-blue-700 transition-all duration-200 group text-center font-medium"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div className="flex items-center justify-center space-x-2">
|
||||
<ShoppingBag className="w-4 h-4" />
|
||||
<span>View All Products</span>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
191
components/layout/MobileTabBar.tsx
Normal file
191
components/layout/MobileTabBar.tsx
Normal file
@@ -0,0 +1,191 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import {
|
||||
Home,
|
||||
ShoppingBag,
|
||||
ShoppingCart,
|
||||
User,
|
||||
MoreHorizontal
|
||||
} from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import CartSidebar from '@/components/shop/CartSidebar'
|
||||
import { cartManager } from '@/lib/cart'
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: 'home',
|
||||
label: 'Home',
|
||||
icon: Home,
|
||||
href: '/',
|
||||
activeRoutes: ['/']
|
||||
},
|
||||
{
|
||||
id: 'products',
|
||||
label: 'Products',
|
||||
icon: ShoppingBag,
|
||||
href: '/products',
|
||||
activeRoutes: ['/products', '/products/[slug]']
|
||||
},
|
||||
{
|
||||
id: 'cart',
|
||||
label: 'Cart',
|
||||
icon: ShoppingCart,
|
||||
href: '/cart',
|
||||
activeRoutes: ['/cart', '/checkout']
|
||||
},
|
||||
{
|
||||
id: 'account',
|
||||
label: 'Account',
|
||||
icon: User,
|
||||
href: '/dashboard',
|
||||
activeRoutes: ['/dashboard']
|
||||
},
|
||||
// {
|
||||
// id: 'more',
|
||||
// label: 'More',
|
||||
// icon: MoreHorizontal,
|
||||
// href: '/more',
|
||||
// activeRoutes: ['/more', '/about', '/contact', '/partnership', '/bulk-supply']
|
||||
// }
|
||||
]
|
||||
|
||||
export default function MobileTabBar() {
|
||||
const pathname = usePathname()
|
||||
const [isVisible, setIsVisible] = useState(true)
|
||||
const [lastScrollY, setLastScrollY] = useState(0)
|
||||
const [cartItemCount, setCartItemCount] = useState(0)
|
||||
|
||||
// Load cart count
|
||||
const loadCartCount = () => {
|
||||
const cart = cartManager.getCart()
|
||||
const count = cart.reduce((sum, item) => sum + item.quantity, 0)
|
||||
setCartItemCount(count)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadCartCount()
|
||||
|
||||
const handleCartUpdate = () => loadCartCount()
|
||||
window.addEventListener('cartUpdated', handleCartUpdate)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('cartUpdated', handleCartUpdate)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Hide/show tab bar on scroll
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const currentScrollY = window.scrollY
|
||||
|
||||
if (currentScrollY > lastScrollY && currentScrollY > 100) {
|
||||
setIsVisible(false) // Hide when scrolling down
|
||||
} else {
|
||||
setIsVisible(true) // Show when scrolling up
|
||||
}
|
||||
|
||||
setLastScrollY(currentScrollY)
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', handleScroll, { passive: true })
|
||||
return () => window.removeEventListener('scroll', handleScroll)
|
||||
}, [lastScrollY])
|
||||
|
||||
const isActiveTab = (tab: typeof tabs[0]) => {
|
||||
if (tab.activeRoutes.includes(pathname)) return true
|
||||
return tab.activeRoutes.some(route => {
|
||||
if (route.includes('[slug]')) {
|
||||
const baseRoute = route.replace('/[slug]', '')
|
||||
return pathname.startsWith(baseRoute) && pathname !== baseRoute
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"fixed bottom-0 left-0 right-0 z-50 bg-white border-t border-gray-200 shadow-lg transition-transform duration-300 md:hidden",
|
||||
isVisible ? "translate-y-0" : "translate-y-full"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center justify-around px-2 py-2 safe-area-bottom">
|
||||
{tabs.map((tab) => {
|
||||
const Icon = tab.icon
|
||||
const isActive = isActiveTab(tab)
|
||||
|
||||
// Special handling for cart tab
|
||||
if (tab.id === 'cart') {
|
||||
return (
|
||||
<CartSidebar key={tab.id}>
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col items-center justify-center py-2 px-3 rounded-lg transition-all duration-200 min-w-0 flex-1 relative cursor-pointer",
|
||||
"text-gray-600 hover:text-emerald-600 hover:bg-gray-50"
|
||||
)}
|
||||
>
|
||||
<div className="relative">
|
||||
<Icon
|
||||
className="h-5 w-5 mb-1 transition-transform duration-200"
|
||||
/>
|
||||
{cartItemCount > 0 && (
|
||||
<Badge
|
||||
className="absolute -top-2 -right-2 h-4 w-4 flex items-center justify-center p-0 bg-red-500 text-white !text-xs"
|
||||
>
|
||||
{cartItemCount > 99 ? '99+' : cartItemCount}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
className="text-xs font-medium truncate transition-colors duration-200 text-gray-600"
|
||||
>
|
||||
{tab.label}
|
||||
</span>
|
||||
</div>
|
||||
</CartSidebar>
|
||||
)
|
||||
}
|
||||
|
||||
// Regular tabs
|
||||
return (
|
||||
<Link
|
||||
key={tab.id}
|
||||
href={tab.href}
|
||||
className={cn(
|
||||
"flex flex-col items-center justify-center py-2 px-3 rounded-lg transition-all duration-200 min-w-0 flex-1",
|
||||
isActive
|
||||
? "text-emerald-600 bg-emerald-50"
|
||||
: "text-gray-600 hover:text-emerald-600 hover:bg-gray-50"
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
className={cn(
|
||||
"h-5 w-5 mb-1 transition-transform duration-200",
|
||||
isActive ? "scale-110" : ""
|
||||
)}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs font-medium truncate transition-colors duration-200",
|
||||
isActive ? "text-emerald-700" : "text-gray-600"
|
||||
)}
|
||||
>
|
||||
{tab.label}
|
||||
</span>
|
||||
{isActive && (
|
||||
<div className="absolute -top-0.5 left-1/2 transform -translate-x-1/2 w-8 h-0.5 bg-emerald-600 rounded-full" />
|
||||
)}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Safe area padding for devices with home indicator */}
|
||||
<div className="h-safe-area-inset-bottom bg-white" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
145
components/layout/footer.tsx
Normal file
145
components/layout/footer.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import Link from 'next/link'
|
||||
import { Linkedin, Facebook, Instagram, Mail, Phone, MapPin, Award, Truck, Shield } from 'lucide-react'
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="bg-gray-900 text-white">
|
||||
{/* Main Footer Content */}
|
||||
<div className="max-w-7xl mx-auto px-3 sm:px-6 lg:px-8 py-8 sm:py-12 lg:py-16">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-5 gap-6 sm:gap-8">
|
||||
|
||||
{/* Company Info - Full width on mobile */}
|
||||
<div className="lg:col-span-2 mb-6 lg:mb-0">
|
||||
<div className="mb-4 sm:mb-6">
|
||||
<h3 className="text-xl sm:text-2xl font-bold text-orange-400 mb-2">Padmaaja</h3>
|
||||
<p className="text-gray-400 text-sm sm:text-base leading-relaxed">
|
||||
India's leading manufacturer and exporter of premium Basmati rice and multigrain flour.
|
||||
Committed to delivering authentic quality and taste that has been trusted for generations.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Contact Info */}
|
||||
<div className="space-y-2 sm:space-y-3">
|
||||
<div className="flex items-start space-x-3">
|
||||
<MapPin className="h-4 w-4 sm:h-5 sm:w-5 text-orange-400 mt-1 flex-shrink-0" />
|
||||
<div className="text-xs sm:text-sm">
|
||||
<p className="font-medium">Corporate Office</p>
|
||||
<p className="text-gray-400">11B/79, Vrindavan Colony, Lucknow - 226029</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<Phone className="h-4 w-4 sm:h-5 sm:w-5 text-orange-400" />
|
||||
<div className="text-xs sm:text-sm">
|
||||
<a href="tel:+91-9475758817" className="hover:text-orange-400 transition-colors">
|
||||
+91-9475758817
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<Mail className="h-4 w-4 sm:h-5 sm:w-5 text-orange-400" />
|
||||
<div className="text-xs sm:text-sm">
|
||||
<a href="mailto:info@padmajarice.com" className="hover:text-orange-400 transition-colors">
|
||||
info@padmajarice.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Menu sections - Responsive layout */}
|
||||
<div className="lg:col-span-3">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6 lg:gap-8">
|
||||
|
||||
{/* Quick Links */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-base sm:text-lg mb-3 sm:mb-4 text-white">Quick Links</h4>
|
||||
<ul className="space-y-1.5 sm:space-y-2 text-xs sm:text-sm">
|
||||
<li><Link href="/about" className="text-gray-400 hover:text-orange-400 transition-colors">About Us</Link></li>
|
||||
<li><Link href="/products" className="text-gray-400 hover:text-orange-400 transition-colors">Our Products</Link></li>
|
||||
<li><Link href="/partnership" className="text-gray-400 hover:text-orange-400 transition-colors">Partnership</Link></li>
|
||||
<li><Link href="/wholesaler" className="text-gray-400 hover:text-orange-400 transition-colors">Become Wholesaler</Link></li>
|
||||
<li><Link href="/contact" className="text-gray-400 hover:text-orange-400 transition-colors">Contact Us</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Business Solutions */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-base sm:text-lg mb-3 sm:mb-4 text-white">Business</h4>
|
||||
<ul className="space-y-1.5 sm:space-y-2 text-xs sm:text-sm">
|
||||
<li><Link href="/wholesaler" className="text-gray-400 hover:text-orange-400 transition-colors">Distributor Program</Link></li>
|
||||
<li><Link href="/partnership" className="text-gray-400 hover:text-orange-400 transition-colors">Business Partnership</Link></li>
|
||||
<li><Link href="/contact" className="text-gray-400 hover:text-orange-400 transition-colors">Private Label</Link></li>
|
||||
<li><Link href="/contact" className="text-gray-400 hover:text-orange-400 transition-colors">Export Inquiry</Link></li>
|
||||
<li><Link href="/contact" className="text-gray-400 hover:text-orange-400 transition-colors">Franchise</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Legal & Support - Responsive column spanning */}
|
||||
<div className="col-span-2 lg:col-span-1">
|
||||
<h4 className="font-semibold text-base sm:text-lg mb-3 sm:mb-4 text-white">Support</h4>
|
||||
<ul className="space-y-1.5 sm:space-y-2 text-xs sm:text-sm grid grid-cols-2 lg:grid-cols-1 gap-x-4 lg:gap-x-0">
|
||||
<li><Link href="/orders" className="text-gray-400 hover:text-orange-400 transition-colors">Track Order</Link></li>
|
||||
<li><Link href="/profile" className="text-gray-400 hover:text-orange-400 transition-colors">My Account</Link></li>
|
||||
<li><Link href="/terms-of-service" className="text-gray-400 hover:text-orange-400 transition-colors">Terms of Service</Link></li>
|
||||
<li><Link href="/privacy-policy" className="text-gray-400 hover:text-orange-400 transition-colors">Privacy Policy</Link></li>
|
||||
<li><Link href="/refund-policy" className="text-gray-400 hover:text-orange-400 transition-colors">Refund Policy</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Social Media & Newsletter */}
|
||||
{/* <div className="mt-6 sm:mt-8 pt-6 sm:pt-8 border-t border-gray-800">
|
||||
<div className="flex flex-col space-y-4 sm:space-y-6 lg:space-y-0 lg:flex-row lg:justify-between lg:items-center"> */}
|
||||
|
||||
{/* Social Media */}
|
||||
{/* <div className="flex flex-col sm:flex-row sm:items-center space-y-2 sm:space-y-0 sm:space-x-4 lg:space-x-6">
|
||||
<span className="text-sm font-medium text-gray-400">Follow Us:</span>
|
||||
<div className="flex space-x-4">
|
||||
<Link href="#" className="text-gray-400 hover:text-orange-400 transition-colors">
|
||||
<Linkedin className="h-5 w-5 sm:h-6 sm:w-6" />
|
||||
</Link>
|
||||
<Link href="#" className="text-gray-400 hover:text-orange-400 transition-colors">
|
||||
<Facebook className="h-5 w-5 sm:h-6 sm:w-6" />
|
||||
</Link>
|
||||
<Link href="#" className="text-gray-400 hover:text-orange-400 transition-colors">
|
||||
<Instagram className="h-5 w-5 sm:h-6 sm:w-6" />
|
||||
</Link>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* Newsletter Signup */}
|
||||
{/* <div className="flex flex-col sm:flex-row sm:items-center space-y-2 sm:space-y-0 sm:space-x-3">
|
||||
<span className="text-sm font-medium text-gray-400">Stay Updated:</span>
|
||||
<div className="flex w-full sm:w-auto">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Enter your email"
|
||||
className="flex-1 sm:flex-none px-3 py-2 bg-gray-800 border border-gray-700 rounded-l-md text-sm focus:outline-none focus:border-orange-400 text-white placeholder-gray-500 w-full sm:w-48"
|
||||
/>
|
||||
<button className="px-3 sm:px-4 py-2 bg-orange-600 hover:bg-orange-700 text-white text-sm rounded-r-md transition-colors">
|
||||
Subscribe
|
||||
</button>
|
||||
</div>
|
||||
</div> */}
|
||||
{/* </div>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
{/* Bottom Bar */}
|
||||
<div className="bg-gray-950">
|
||||
<div className="max-w-7xl mx-auto px-3 sm:px-6 lg:px-8 py-3 sm:py-4">
|
||||
<div className="flex flex-col sm:flex-row justify-between items-center text-xs sm:text-sm text-gray-400 space-y-2 sm:space-y-0">
|
||||
<div>
|
||||
<p>© 2024 Padmaaja. All rights reserved.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Crafted with ❤️ by <Link href='https://web.jabin.org' target='_blank'>Jabin Web</Link></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
366
components/layout/header.tsx
Normal file
366
components/layout/header.tsx
Normal file
@@ -0,0 +1,366 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { ChevronDown, Menu, X, Wheat, Leaf, Package, Building2, Settings, ShoppingBag, LogOut, LucideIcon } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
|
||||
import { useSession, signOut } from 'next-auth/react'
|
||||
import CartSidebar from '@/components/shop/CartSidebar'
|
||||
import MegaMenu from '@/components/layout/MegaMenu'
|
||||
import { isFeatureEnabled } from '@/lib/business-config'
|
||||
|
||||
interface MenuItem {
|
||||
name: string
|
||||
href: string
|
||||
description: string
|
||||
}
|
||||
|
||||
interface MenuCategory {
|
||||
name: string
|
||||
icon: LucideIcon
|
||||
items: MenuItem[]
|
||||
}
|
||||
|
||||
interface MegaMenuConfig {
|
||||
title: string
|
||||
type: 'categories'
|
||||
categories: MenuCategory[]
|
||||
}
|
||||
|
||||
export function Header() {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||
const [activeDropdown, setActiveDropdown] = useState<string | null>(null)
|
||||
const [isScrolled, setIsScrolled] = useState(false)
|
||||
|
||||
const navItems = [
|
||||
// { name: 'Home', href: '/' },
|
||||
{ name: 'Recipes', href: '/recipes' },
|
||||
// { name: 'About', href: '/about' },
|
||||
{ name: 'Contact', href: '/contact' },
|
||||
]
|
||||
|
||||
const megaMenus: Record<string, MegaMenuConfig> = {
|
||||
products: {
|
||||
title: 'Products',
|
||||
type: 'categories' as const,
|
||||
categories: [] // Will be populated dynamically by MegaMenu component
|
||||
},
|
||||
company: {
|
||||
title: 'Company',
|
||||
type: 'categories' as const,
|
||||
categories: [
|
||||
{
|
||||
name: 'About Us',
|
||||
icon: Building2,
|
||||
items: [
|
||||
{ name: 'Our Story', href: '/about', description: 'Company history and mission' },
|
||||
{ name: 'Our Founder', href: '/about/founder', description: 'Meet our visionary leader' },
|
||||
{ name: 'Certifications', href: '/about/certifications', description: 'Industry recognition' },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Business',
|
||||
icon: Settings,
|
||||
items: [
|
||||
{ name: 'Partnership', href: '/partnership', description: 'Join our network' },
|
||||
{ name: 'Wholesaler', href: '/wholesaler', description: 'Wholesaler program' },
|
||||
{ name: 'Sustainability', href: '/sustainability', description: 'Environmental commitment' },
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const { data: session } = useSession()
|
||||
|
||||
const adminNavigation = [
|
||||
{ name: 'Admin Dashboard', href: '/admin', icon: Settings },
|
||||
{ name: 'Manage Products', href: '/admin/products', icon: Package },
|
||||
{ name: 'Manage Orders', href: '/admin/orders', icon: ShoppingBag },
|
||||
]
|
||||
|
||||
const memberNavigation = [
|
||||
{ name: 'Dashboard', href: '/dashboard', icon: Settings },
|
||||
{ name: 'My Orders', href: '/dashboard/orders', icon: ShoppingBag },
|
||||
{ name: 'Profile', href: '/dashboard/profile', icon: Settings },
|
||||
]
|
||||
|
||||
// Scroll detection for sticky header
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const scrollTop = window.scrollY
|
||||
const scrolled = scrollTop > 0
|
||||
setIsScrolled(scrolled)
|
||||
|
||||
// Add/remove class to body for content padding
|
||||
if (scrolled) {
|
||||
document.body.classList.add('header-sticky')
|
||||
} else {
|
||||
document.body.classList.remove('header-sticky')
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
document.body.classList.remove('header-sticky')
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (mobileMenuOpen) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
} else {
|
||||
document.body.style.overflow = 'unset'
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.body.style.overflow = 'unset'
|
||||
}
|
||||
}, [mobileMenuOpen])
|
||||
|
||||
const handleMouseEnter = (dropdown: string) => {
|
||||
setActiveDropdown(dropdown)
|
||||
}
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setActiveDropdown(null)
|
||||
}
|
||||
|
||||
const handleMobileMenuClick = () => {
|
||||
setMobileMenuOpen(!mobileMenuOpen)
|
||||
}
|
||||
|
||||
const handleMobileLinkClick = () => {
|
||||
setMobileMenuOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className={`transition-all duration-300 ${
|
||||
isScrolled
|
||||
? 'fixed top-0 left-0 right-0 z-50 bg-white/95 backdrop-blur-md border-b border-slate-200 shadow-lg'
|
||||
: 'relative bg-white border-b border-slate-200'
|
||||
}`}>
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className={`flex justify-between items-center transition-all duration-300 ${
|
||||
isScrolled ? 'h-20' : 'h-24'
|
||||
}`}>
|
||||
{/* Logo */}
|
||||
<Link href="/" className="flex-shrink-0 flex items-center z-10">
|
||||
<div className="flex items-center space-x-3">
|
||||
<Image
|
||||
src="/kashmina-logo.png"
|
||||
alt="PADMAAJA RASOOI PVT. LTD."
|
||||
width={150}
|
||||
height={150}
|
||||
className="object-contain"
|
||||
/>
|
||||
{/* <div className="flex flex-col">
|
||||
<span className="text-lg font-bold text-slate-800 transition-colors duration-300 leading-tight">
|
||||
KASHMINA RICE
|
||||
</span>
|
||||
<span className="text-xs uppercase text-slate-600 transition-colors duration-300 leading-tight">
|
||||
"Premium Rice"
|
||||
</span>
|
||||
</div> */}
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden lg:flex items-center space-x-8">
|
||||
{/* Regular Nav Items */}
|
||||
{navItems.map(item => (
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className="text-base font-medium text-slate-700 hover:text-emerald-600 transition-colors duration-200"
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{/* Mega Menu Items */}
|
||||
{Object.entries(megaMenus).map(([key, menu]) => (
|
||||
<div
|
||||
key={key}
|
||||
className="relative"
|
||||
onMouseEnter={() => handleMouseEnter(key)}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<button
|
||||
className={`flex items-center space-x-1 text-base font-medium text-slate-700 hover:text-emerald-600 transition-colors duration-200 ${
|
||||
activeDropdown === key ? 'text-emerald-600' : ''
|
||||
}`}
|
||||
>
|
||||
<span>{menu.title}</span>
|
||||
<ChevronDown className={`w-4 h-4 transition-transform duration-200 ${
|
||||
activeDropdown === key ? 'rotate-180' : ''
|
||||
}`} />
|
||||
</button>
|
||||
|
||||
{/* Mega Menu Dropdown */}
|
||||
<MegaMenu
|
||||
menuKey={key}
|
||||
menu={menu}
|
||||
isActive={activeDropdown === key}
|
||||
onClose={() => setActiveDropdown(null)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
{/* B2C Feature - Cart Sidebar (Disabled for B2B mode) */}
|
||||
{isFeatureEnabled('cart') && <CartSidebar />}
|
||||
|
||||
{session ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="relative h-10 w-10 rounded-full">
|
||||
<Avatar className="h-10 w-10">
|
||||
<AvatarImage src={session.user.image || ''} alt={session.user.name || ''} />
|
||||
<AvatarFallback>
|
||||
{session.user.name?.[0] || session.user.email?.[0] || 'U'}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56" align="end" forceMount>
|
||||
<DropdownMenuLabel className="font-normal">
|
||||
<div className="flex flex-col space-y-1">
|
||||
<p className="text-sm font-medium leading-none">{session.user.name}</p>
|
||||
<p className="text-xs leading-none text-muted-foreground">
|
||||
{session.user.email}
|
||||
</p>
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{session.user.role === 'ADMIN' && adminNavigation.map((item) => (
|
||||
<DropdownMenuItem key={item.name} asChild>
|
||||
<Link href={item.href} className="flex items-center">
|
||||
<item.icon className="mr-2 h-4 w-4" />
|
||||
{item.name}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
|
||||
{(session.user.role === 'MEMBER' || session.user.role === 'ADMIN') &&
|
||||
memberNavigation.map((item) => (
|
||||
<DropdownMenuItem key={item.name} asChild>
|
||||
<Link href={item.href} className="flex items-center">
|
||||
<item.icon className="mr-2 h-4 w-4" />
|
||||
{item.name}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => signOut()}>
|
||||
<LogOut className="mr-2 h-4 w-4" />
|
||||
Log out
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button variant="ghost" className="h-10 px-4 py-2 text-base font-medium" asChild>
|
||||
<Link href="/auth/signin">Sign In</Link>
|
||||
</Button>
|
||||
<Button className="h-10 px-4 py-2 text-base font-medium" asChild>
|
||||
<Link href="/auth/signup">Sign Up</Link>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<div className="lg:hidden">
|
||||
<button
|
||||
onClick={handleMobileMenuClick}
|
||||
className="p-4 rounded-lg text-slate-700 hover:bg-slate-100 transition-colors duration-200 touch-manipulation min-w-[48px] min-h-[48px] flex items-center justify-center"
|
||||
aria-label="Toggle mobile menu"
|
||||
>
|
||||
{mobileMenuOpen ? <X className="w-7 h-7" /> : <Menu className="w-7 h-7" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
<AnimatePresence>
|
||||
{mobileMenuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="lg:hidden border-t bg-white border-slate-200 shadow-lg relative z-50"
|
||||
>
|
||||
<div className="px-4 py-6 space-y-6 max-h-[80vh] overflow-y-auto">
|
||||
{/* Regular Nav Items */}
|
||||
{navItems.map(item => (
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className="mobile-nav-item block text-lg font-medium text-slate-700 hover:text-emerald-600 transition-colors duration-200 py-3 px-2 rounded-lg hover:bg-slate-50 touch-manipulation"
|
||||
onClick={handleMobileLinkClick}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{/* Mobile Mega Menu Items */}
|
||||
{Object.entries(megaMenus).map(([key, menu]) => (
|
||||
<div key={key} className="space-y-3">
|
||||
<h3 className="font-semibold text-slate-800 text-lg sm:text-xl">{menu.title}</h3>
|
||||
|
||||
{menu.type === 'categories' ? (
|
||||
// Products with Categories - Mobile Responsive
|
||||
<div className="space-y-4">
|
||||
{menu.categories?.map((category: MenuCategory, idx: number) => {
|
||||
const IconComponent = category.icon
|
||||
return (
|
||||
<div key={idx} className="space-y-2">
|
||||
<div className="flex items-center space-x-2 text-emerald-600 font-medium text-base sm:text-lg">
|
||||
<IconComponent className="w-4 h-4 flex-shrink-0" />
|
||||
<span>{category.name}</span>
|
||||
</div>
|
||||
<div className="pl-6 space-y-1">
|
||||
{category.items.map((item: MenuItem, itemIdx: number) => (
|
||||
<Link
|
||||
key={itemIdx}
|
||||
href={item.href}
|
||||
className="mobile-nav-item block text-base text-slate-600 hover:text-emerald-600 hover:bg-slate-50 transition-colors duration-200 py-2 px-2 rounded-md touch-manipulation"
|
||||
onClick={handleMobileLinkClick}
|
||||
>
|
||||
<div className="font-medium">{item.name}</div>
|
||||
<div className="text-xs text-slate-500 mt-0.5">{item.description}</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Mobile CTA */}
|
||||
<Link href="/contact#quote" onClick={handleMobileLinkClick}>
|
||||
<Button className="w-full bg-emerald-600 text-white hover:bg-emerald-700 rounded-xl px-6 py-4 mt-6 font-medium text-lg touch-manipulation">
|
||||
Get a Quote
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user