import { useEffect, useMemo, useState } from 'react' import { NavLink, useLocation, useNavigate } from 'react-router-dom' import { Home, ShoppingBag, Store, Users, Settings, FileText, Briefcase, Globe, HelpCircle, LogOut, ChevronDown, Building2, Pencil, Package, } 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, 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(basePath: string, pathname: string) { return pathname === basePath || pathname.startsWith(`${basePath}/`) } export function Sidebar() { 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: 'link', id: 'customer-products', icon: Package, labelKey: 'nav.customerProducts', to: '/customer-products', moduleId: 'customer_products', }, { type: 'group', id: 'blog', icon: FileText, labelKey: 'nav.blog', basePath: '/blog', moduleId: 'blog', children: [ { labelKey: 'nav.blog.overview', to: '/blog' }, { labelKey: 'nav.blog.list', to: '/blog/list' }, { labelKey: 'nav.blog.new', to: '/blog/new' }, { labelKey: 'nav.blog.categories', to: '/blog/categories' }, { labelKey: 'nav.blog.settings', to: '/blog/settings' }, ], }, { 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.sliders', to: '/website/sliders' }, { labelKey: 'nav.website.specialCategories', to: '/website/special-categories' }, { labelKey: 'nav.website.specialBrands', to: '/website/special-brands' }, { 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.ePayment', to: '/website/e-payment' }, ], }, { type: 'link', id: 'settings', icon: Settings, labelKey: 'nav.settings', to: '/settings' }, ] return items.filter( (item) => !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.basePath, 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 ( ) }