import { useEffect, useMemo, useState } from 'react' import { NavLink, useLocation, useNavigate } from 'react-router-dom' import { Home, ShoppingBag, Store, Users, Settings, Briefcase, Globe, Layers, HelpCircle, LogOut, ChevronDown, Building2, Pencil, Package, Wallet, } from 'lucide-react' import type { LucideIcon } from 'lucide-react' import { useLocale } from '@meshkee/dashboard-ui' import { useAuth } from '../context/AuthContext' import { useT } from '../i18n/useT' import type { BusinessMessageKey } from '../i18n/messages' import { BUSINESS_PROFILE_UPDATED_EVENT, getActiveBusinessDomain, } from '../lib/businessContext' import { isAbortError } from '../lib/api' import { getBusinessProfile } from '../services/businessProfileService' import { useTenantBranding } from '../context/TenantBrandingContext' import { hasBusinessModule, hasAnyBusinessModule, type BusinessModuleId, } from '../utils/businessModules' import { Tooltip } from './Tooltip' import styles from './Sidebar.module.css' interface NavChild { labelKey: BusinessMessageKey to: string } interface NavGroup { type: 'group' id: string icon: LucideIcon labelKey: BusinessMessageKey basePath: string children: NavChild[] moduleId?: BusinessModuleId } interface NavLinkItem { type: 'link' id: string icon: LucideIcon labelKey: BusinessMessageKey to: string moduleId?: BusinessModuleId } type NavItem = NavLinkItem | NavGroup function isGroupActive(group: NavGroup, pathname: string) { if (pathname === group.basePath || pathname.startsWith(`${group.basePath}/`)) { return true } if (group.id === 'content') { if ( pathname.startsWith('/blog') || pathname.startsWith('/videos') || pathname.startsWith('/instructions') || pathname.startsWith('/workshops') ) { return true } } return group.children.some( (child) => pathname === child.to || pathname.startsWith(`${child.to}/`), ) } interface SidebarProps { open?: boolean } export function Sidebar({ open = false }: SidebarProps) { const { pathname } = useLocation() const navigate = useNavigate() const { user, logout } = useAuth() const { locale } = useLocale() const { enabledModules } = useTenantBranding() const t = useT() const [openGroups, setOpenGroups] = useState>({}) const [brandLogoUrl, setBrandLogoUrl] = useState(null) const [nameEn, setNameEn] = useState('') const [nameFa, setNameFa] = useState('') const businessDomain = getActiveBusinessDomain() const fallbackBusinessName = user?.businesses[0]?.name ?? t('app.storeFallback') const brandName = useMemo(() => { if (locale === 'fa') { return nameFa.trim() || nameEn.trim() || fallbackBusinessName } return nameEn.trim() || nameFa.trim() || fallbackBusinessName }, [locale, nameEn, nameFa, fallbackBusinessName]) const navItems = useMemo(() => { const items: NavItem[] = [ { type: 'link', id: 'home', icon: Home, labelKey: 'nav.home', to: '/' }, { type: 'link', id: 'business-profile', icon: Building2, labelKey: 'nav.businessProfile', to: '/business-profile', }, { type: 'group', id: 'products', icon: ShoppingBag, labelKey: 'nav.products', basePath: '/products', moduleId: 'products', children: [ { labelKey: 'nav.products.overview', to: '/products' }, { labelKey: 'nav.products.list', to: '/products/list' }, { labelKey: 'nav.products.new', to: '/products/new' }, { labelKey: 'nav.products.categories', to: '/products/categories' }, { labelKey: 'nav.products.brands', to: '/products/brands' }, { labelKey: 'nav.products.settings', to: '/products/settings' }, ], }, { type: 'group', id: 'store', icon: Store, labelKey: 'nav.store', basePath: '/store', moduleId: 'store', children: [ { labelKey: 'nav.store.overview', to: '/store' }, { labelKey: 'nav.store.items', to: '/store/items' }, { labelKey: 'nav.store.orders', to: '/store/orders' }, { labelKey: 'nav.store.shipping', to: '/store/shipping' }, { labelKey: 'nav.store.cards', to: '/store/cards' }, { labelKey: 'nav.store.settings', to: '/store/settings' }, ], }, { type: 'link', id: 'customers', icon: Users, labelKey: 'nav.customers', to: '/customers' }, { type: 'group', id: 'finance', icon: Wallet, labelKey: 'nav.finance', basePath: '/finance', moduleId: 'finance', children: [ { labelKey: 'nav.finance.overview', to: '/finance' }, { labelKey: 'nav.finance.invoices', to: '/invoices' }, { labelKey: 'nav.finance.transactions', to: '/transactions' }, ], }, { type: 'link', id: 'customer-products', icon: Package, labelKey: 'nav.customerProducts', to: '/customer-products', moduleId: 'customer_products', }, { type: 'group', id: 'content', icon: Layers, labelKey: 'nav.content', basePath: '/content', children: [ ...(hasBusinessModule(enabledModules, 'blog') ? [{ labelKey: 'nav.content.blog' as BusinessMessageKey, to: '/blog' }] : []), ...(hasBusinessModule(enabledModules, 'videos') ? [{ labelKey: 'nav.content.videos' as BusinessMessageKey, to: '/videos' }] : []), ...(hasBusinessModule(enabledModules, 'instructions') ? [{ labelKey: 'nav.content.instructions' as BusinessMessageKey, to: '/instructions' }] : []), ...(hasBusinessModule(enabledModules, 'workshops') ? [{ labelKey: 'nav.workshops' as BusinessMessageKey, to: '/workshops' }] : []), ], }, { type: 'group', id: 'portfolios', icon: Briefcase, labelKey: 'nav.portfolios', basePath: '/portfolios', moduleId: 'portfolio', children: [ { labelKey: 'nav.portfolios.overview', to: '/portfolios' }, { labelKey: 'nav.portfolios.list', to: '/portfolios/list' }, { labelKey: 'nav.portfolios.new', to: '/portfolios/new' }, { labelKey: 'nav.portfolios.categories', to: '/portfolios/categories' }, { labelKey: 'nav.portfolios.settings', to: '/portfolios/settings' }, ], }, { type: 'group', id: 'website', icon: Globe, labelKey: 'nav.website', basePath: '/website', children: [ { labelKey: 'nav.website.overview', to: '/website' }, { labelKey: 'nav.website.staticImages', to: '/website/static-images' }, { labelKey: 'nav.website.specialItems', to: '/website/special-items' }, { labelKey: 'nav.website.contact', to: '/website/contact' }, { labelKey: 'nav.website.subscriptions', to: '/website/subscriptions' }, { labelKey: 'nav.website.faq', to: '/website/faq' }, { labelKey: 'nav.website.badges', to: '/website/badges' }, { labelKey: 'nav.website.settings', to: '/website/settings' }, ], }, { type: 'link', id: 'settings', icon: Settings, labelKey: 'nav.settings', to: '/settings' }, ] return items.filter((item) => { if (item.type === 'group' && item.id === 'content') { return hasAnyBusinessModule(enabledModules, ['blog', 'videos', 'instructions', 'workshops']) } return !item.moduleId || hasBusinessModule(enabledModules, item.moduleId) }) }, [enabledModules]) useEffect(() => { const controller = new AbortController() async function loadBranding() { try { const data = await getBusinessProfile(controller.signal) setBrandLogoUrl(data.profile.logoUrl) setNameEn(data.profile.nameEn.trim()) setNameFa(data.profile.nameFa.trim()) } catch (err) { if (isAbortError(err)) return setBrandLogoUrl(null) setNameEn('') setNameFa('') } } void loadBranding() function handleProfileUpdated() { void loadBranding() } window.addEventListener(BUSINESS_PROFILE_UPDATED_EVENT, handleProfileUpdated) return () => { controller.abort() window.removeEventListener(BUSINESS_PROFILE_UPDATED_EVENT, handleProfileUpdated) } }, []) useEffect(() => { const activeGroup = navItems.find( (item) => item.type === 'group' && isGroupActive(item, pathname), ) if (!activeGroup || activeGroup.type !== 'group') return setOpenGroups({ [activeGroup.id]: true }) }, [pathname, navItems]) function toggleGroup(id: string) { setOpenGroups((prev) => { const willOpen = !prev[id] return willOpen ? { [id]: true } : {} }) } return ( ) }