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:
Alireza Hassani
2026-07-22 13:48:53 +03:30
co-authored by Cursor
commit f566387c61
509 changed files with 62690 additions and 0 deletions
+281
View File
@@ -0,0 +1,281 @@
import { useEffect, useState } from 'react'
import { NavLink, useLocation, useNavigate } from 'react-router-dom'
import {
Home,
ShoppingBag,
Store,
Users,
Settings,
FileText,
Briefcase,
Globe,
HelpCircle,
LogOut,
ChevronDown,
Building2,
} from 'lucide-react'
import type { LucideIcon } from 'lucide-react'
import { useAuth } from '../context/AuthContext'
import {
BUSINESS_PROFILE_UPDATED_EVENT,
getActiveBusinessDomain,
} from '../lib/businessContext'
import { isAbortError } from '../lib/api'
import { getBusinessProfile } from '../services/businessProfileService'
import meshkeeLogo from '../assets/meshkee-logo.png'
import styles from './Sidebar.module.css'
interface NavChild {
label: string
to: string
}
interface NavGroup {
type: 'group'
icon: LucideIcon
label: string
basePath: string
children: NavChild[]
}
interface NavLinkItem {
type: 'link'
icon: LucideIcon
label: string
to: string
}
type NavItem = NavLinkItem | NavGroup
const navItems: NavItem[] = [
{ type: 'link', icon: Home, label: 'Home', to: '/' },
{ type: 'link', icon: Building2, label: 'Business Profile', to: '/business-profile' },
{
type: 'group',
icon: ShoppingBag,
label: 'Products',
basePath: '/products',
children: [
{ label: 'Overview', to: '/products' },
{ label: 'My Products', to: '/products/list' },
{ label: 'Add New Product', to: '/products/new' },
{ label: 'Categories', to: '/products/categories' },
{ label: 'Brands', to: '/products/brands' },
{ label: 'Settings', to: '/products/settings' },
],
},
{
type: 'group',
icon: Store,
label: 'Store',
basePath: '/store',
children: [
{ label: 'Overview', to: '/store' },
{ label: 'My Store Items', to: '/store/items' },
{ label: 'My Orders', to: '/store/orders' },
{ label: 'Shipping Fees', to: '/store/shipping' },
{ label: 'Shopping Cards', to: '/store/cards' },
{ label: 'Settings', to: '/store/settings' },
],
},
{ type: 'link', icon: Users, label: 'Customers', to: '/customers' },
{ type: 'link', icon: Settings, label: 'Settings', to: '/settings' },
{
type: 'group',
icon: FileText,
label: 'Blog',
basePath: '/blog',
children: [
{ label: 'Overview', to: '/blog' },
{ label: 'My Blogs', to: '/blog/list' },
{ label: 'Add New Blog', to: '/blog/new' },
{ label: 'Categories', to: '/blog/categories' },
{ label: 'Settings', to: '/blog/settings' },
],
},
{
type: 'group',
icon: Briefcase,
label: 'Portfolios',
basePath: '/portfolios',
children: [
{ label: 'Overview', to: '/portfolios' },
{ label: 'My Portfolios', to: '/portfolios/list' },
{ label: 'Add New Portfolio', to: '/portfolios/new' },
{ label: 'Categories', to: '/portfolios/categories' },
{ label: 'Settings', to: '/portfolios/settings' },
],
},
{
type: 'group',
icon: Globe,
label: 'Website',
basePath: '/website',
children: [
{ label: 'Overview', to: '/website' },
{ label: 'Sliders', to: '/website/sliders' },
{ label: 'Special Categories', to: '/website/special-categories' },
{ label: 'Special Brands', to: '/website/special-brands' },
{ label: 'Special Items', to: '/website/special-items' },
{ label: 'Contact Us Form', to: '/website/contact' },
{ label: 'Subscriptions', to: '/website/subscriptions' },
{ label: 'FAQ', to: '/website/faq' },
{ label: 'Badges', to: '/website/badges' },
{ label: 'E-Payment', to: '/website/e-payment' },
],
},
]
const footerItems = [
{ icon: HelpCircle, label: 'Help Center' },
{ icon: LogOut, label: 'Logout' },
]
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 [openGroups, setOpenGroups] = useState<Record<string, boolean>>({})
const [brandLogoUrl, setBrandLogoUrl] = useState<string | null>(null)
const [brandName, setBrandName] = useState('')
const businessDomain = getActiveBusinessDomain()
const fallbackBusinessName = user?.businesses[0]?.name ?? 'Business'
useEffect(() => {
const controller = new AbortController()
async function loadBranding() {
try {
const data = await getBusinessProfile(controller.signal)
setBrandLogoUrl(data.profile.logoUrl)
setBrandName(data.profile.nameEn.trim() || data.profile.nameFa.trim() || fallbackBusinessName)
} catch (err) {
if (isAbortError(err)) return
setBrandLogoUrl(null)
setBrandName(fallbackBusinessName)
}
}
void loadBranding()
function handleProfileUpdated() {
void loadBranding()
}
window.addEventListener(BUSINESS_PROFILE_UPDATED_EVENT, handleProfileUpdated)
return () => {
controller.abort()
window.removeEventListener(BUSINESS_PROFILE_UPDATED_EVENT, handleProfileUpdated)
}
}, [fallbackBusinessName])
useEffect(() => {
navItems.forEach((item) => {
if (item.type === 'group' && isGroupActive(item.basePath, pathname)) {
setOpenGroups((prev) => ({ ...prev, [item.label]: true }))
}
})
}, [pathname])
function toggleGroup(label: string) {
setOpenGroups((prev) => ({ ...prev, [label]: !prev[label] }))
}
return (
<aside className={styles.sidebar}>
<div className={styles.brand}>
<img
src={brandLogoUrl ?? meshkeeLogo}
alt={brandName || 'Business logo'}
className={`${styles.brandLogo} ${brandLogoUrl ? styles.brandLogoUploaded : ''}`}
/>
<div className={styles.brandText}>
<span className={styles.brandDomain}>{businessDomain}</span>
<span className={styles.brandName}>{brandName || fallbackBusinessName}</span>
</div>
</div>
<nav className={styles.nav}>
{navItems.map((item) => {
if (item.type === 'link') {
return (
<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>
)
}
const isOpen = openGroups[item.label] ?? false
const groupActive = isGroupActive(item.basePath, pathname)
return (
<div key={item.label} className={styles.navGroup}>
<button
type="button"
className={`${styles.navItem} ${styles.navGroupBtn} ${groupActive ? styles.active : ''}`}
onClick={() => toggleGroup(item.label)}
aria-expanded={isOpen}
>
<item.icon size={20} />
<span className={styles.navGroupLabel}>{item.label}</span>
<ChevronDown
size={16}
className={`${styles.chevron} ${isOpen ? styles.chevronOpen : ''}`}
/>
</button>
{isOpen && (
<div className={styles.subNav}>
{item.children.map((child) => (
<NavLink
key={child.to}
to={child.to}
end={child.to === item.basePath}
className={({ isActive }) =>
`${styles.subNavItem} ${isActive ? styles.subNavActive : ''}`
}
>
{child.label}
</NavLink>
))}
</div>
)}
</div>
)
})}
</nav>
<div className={styles.footer}>
{footerItems.map(({ icon: Icon, label }) => (
<button
key={label}
type="button"
className={styles.navItem}
onClick={() => {
if (label === 'Logout') {
logout()
navigate('/login')
}
}}
>
<Icon size={20} />
<span>{label}</span>
</button>
))}
</div>
</aside>
)
}