import { useEffect, useMemo, useState, type Dispatch, type ReactNode, type SetStateAction } from 'react'; import type React from 'react'; import { getConfig } from '../config/env'; import { SHELL_HEADER_MIN_CLASS } from '../constants/shellChrome'; import { __, sprintf } from '../lib/i18n'; import { NavIcon } from './NavIcon'; import type { NavItem, NavItemBadge } from '../types'; function NavBadge({ badge }: { badge: NavItemBadge }) { const isOff = badge === 'off'; return ( {isOff ? __('Off', 'sikshya') : __('Pro', 'sikshya')} ); } /** Map builder / nested routes to a Course submenu id so the Course group stays highlighted. */ function effectiveNavPage(currentPage: string): string { if (currentPage === 'add-course') { return 'courses'; } if (currentPage === 'add-lesson') { return 'lessons'; } if (currentPage === 'email-template-edit' || currentPage === 'email-templates') { return 'email-hub'; } // Commerce → Sales hub: standalone lists + detail URLs use other view ids than `sales`. if (currentPage === 'sales' || currentPage === 'orders' || currentPage === 'order' || currentPage === 'payments' || currentPage === 'payment') { return 'sales'; } return currentPage; } function branchActive(item: NavItem, currentPage: string): boolean { const navPage = effectiveNavPage(currentPage); if (item.id === navPage) { return true; } return item.children?.some((c) => branchActive(c, currentPage)) ?? false; } /** Fixed-width column so every row’s icon and label line up. */ function IconSlot({ children, size = 'md' }: { children: ReactNode; size?: 'md' | 'sm' }) { const box = size === 'sm' ? 'h-4 w-4' : 'h-5 w-5'; return ( {children} ); } function ChildLink({ item, currentPage, brandedChrome, }: { item: NavItem; currentPage: string; brandedChrome: boolean; }) { if (!item.href) { return null; } const active = item.id === effectiveNavPage(currentPage); return ( {item.label} {item.badge ? : null} ); } function NavBlock({ item, currentPage, open, setOpen, brandedChrome, }: { item: NavItem; currentPage: string; open: Record; setOpen: Dispatch>>; brandedChrome: boolean; }) { const children = item.children; if (children?.length) { const childBranch = branchActive(item, currentPage); const expanded = open[item.id] ?? childBranch; return (
{expanded ? (
{children.map((c) => ( ))}
) : null}
); } if (!item.href) { return null; } const active = item.id === effectiveNavPage(currentPage); return ( {item.label} {item.badge ? : null} ); } type Props = { items: NavItem[]; currentPage: string; /** Free / main Sikshya plugin version. */ version: string; /** Installed Pro add-on semver (when the Pro plugin is loaded). */ proPluginVersion?: string; /** True when the Pro licence is active on this site. */ proLicensed?: boolean; adminUrl: string; /** Plugin root URL; used for the default mark when no white-label logo is set. */ pluginUrl?: string; branding?: { pluginName?: string; logoUrl?: string; sidebarBg?: string; sidebarText?: string; }; }; export function Sidebar({ items, currentPage, version, proPluginVersion, proLicensed, adminUrl, pluginUrl, branding, }: Props) { // Tools lives in the top header; omit from the sidebar to reduce duplication. const visibleItems = useMemo(() => items.filter((item) => item.id !== 'tools'), [items]); const [open, setOpen] = useState>({}); useEffect(() => { setOpen((prev) => { const next = { ...prev }; visibleItems.forEach((item) => { if (item.children?.length && branchActive(item, currentPage)) { next[item.id] = true; } }); return next; }); }, [currentPage, visibleItems]); const wpHome = `${adminUrl.replace(/\/?$/, '/')}index.php`; const title = branding?.pluginName?.trim() ? branding.pluginName.trim() : 'Sikshya'; const pluginBase = (pluginUrl?.trim() || getConfig().pluginUrl || '').replace(/\/+$/, ''); const whiteLabelLogo = branding?.logoUrl?.trim() || ''; const logoSrc = whiteLabelLogo || (pluginBase ? `${pluginBase}/assets/images/logo-white.png` : ''); const brandedChrome = Boolean(branding?.sidebarBg || branding?.sidebarText); const sidebarStyle = useMemo(() => { if (!branding?.sidebarBg && !branding?.sidebarText) return undefined; return { backgroundColor: branding?.sidebarBg || undefined, color: branding?.sidebarText || undefined, }; }, [branding?.sidebarBg, branding?.sidebarText]); return ( ); }