Initial commit: Meshkee dashboards monorepo.
Includes business, customer, and super-admin apps with shared packages and production deploy scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { NavLink, useNavigate } from 'react-router-dom'
|
||||
import { Home, User, MapPin, ShoppingBag, ShoppingCart, Heart, HelpCircle, LogOut } from 'lucide-react'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
import { getActiveBusinessDomain } from '../lib/businessContext'
|
||||
import { isAbortError } from '../lib/api'
|
||||
import { getWebsiteBusinessInfo } from '../services/websiteService'
|
||||
import styles from './Sidebar.module.css'
|
||||
|
||||
const navItems = [
|
||||
{ icon: Home, label: 'Home', to: '/' },
|
||||
{ icon: ShoppingCart, label: 'Shopping Cart', to: '/checkout' },
|
||||
{ icon: User, label: 'My Profile', to: '/profile' },
|
||||
{ icon: MapPin, label: 'My Addresses', to: '/addresses' },
|
||||
{ icon: ShoppingBag, label: 'My Orders', to: '/orders' },
|
||||
{ icon: Heart, label: 'My Favorites', to: '/favorites' },
|
||||
]
|
||||
|
||||
export function Sidebar() {
|
||||
const navigate = useNavigate()
|
||||
const { user, logout } = useAuth()
|
||||
const [brandName, setBrandName] = useState('')
|
||||
const [logoUrl, setLogoUrl] = useState<string | null>(null)
|
||||
|
||||
const businessDomain = getActiveBusinessDomain()
|
||||
const fallbackBusinessName = user?.customerBusinesses[0]?.name ?? 'Store'
|
||||
const displayName = brandName || fallbackBusinessName
|
||||
const initial = displayName.trim().charAt(0) || businessDomain.charAt(0) || 'S'
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController()
|
||||
|
||||
async function loadBranding() {
|
||||
try {
|
||||
const info = await getWebsiteBusinessInfo(businessDomain, controller.signal)
|
||||
if (controller.signal.aborted) return
|
||||
setBrandName(info.nameFa?.trim() || info.name.trim() || fallbackBusinessName)
|
||||
setLogoUrl(info.logoUrl)
|
||||
} catch (err) {
|
||||
if (isAbortError(err)) return
|
||||
setBrandName(fallbackBusinessName)
|
||||
setLogoUrl(null)
|
||||
}
|
||||
}
|
||||
|
||||
void loadBranding()
|
||||
|
||||
return () => {
|
||||
controller.abort()
|
||||
}
|
||||
}, [businessDomain, fallbackBusinessName])
|
||||
|
||||
return (
|
||||
<aside className={styles.sidebar}>
|
||||
<div className={styles.brand}>
|
||||
{logoUrl ? (
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt={displayName}
|
||||
className={styles.brandLogo}
|
||||
/>
|
||||
) : (
|
||||
<span className={styles.brandFallback} aria-hidden>
|
||||
{initial.toUpperCase()}
|
||||
</span>
|
||||
)}
|
||||
<div className={styles.brandText}>
|
||||
<span className={styles.brandDomain}>{businessDomain}</span>
|
||||
<span className={styles.brandName}>{displayName}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className={styles.nav}>
|
||||
{navItems.map((item) => (
|
||||
<NavLink
|
||||
key={item.label}
|
||||
to={item.to}
|
||||
end={item.to === '/'}
|
||||
className={({ isActive }) =>
|
||||
`${styles.navItem} ${isActive ? styles.active : ''}`
|
||||
}
|
||||
>
|
||||
<item.icon size={20} />
|
||||
<span>{item.label}</span>
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className={styles.footer}>
|
||||
<button type="button" className={styles.navItem}>
|
||||
<HelpCircle size={20} />
|
||||
<span>Help Center</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.navItem}
|
||||
onClick={() => {
|
||||
logout()
|
||||
navigate('/login')
|
||||
}}
|
||||
>
|
||||
<LogOut size={20} />
|
||||
<span>Logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user