/** * BoostMedia AI Content Generator Admin - Sidebar Component * * @package BoostMedia_AI * @license GPL-2.0-or-later */ import { useMemo, useState, useEffect, useRef } from 'react' import { NavLink } from 'react-router-dom' import { LayoutDashboard, FileText, Sparkles, Briefcase, List, Link2, Newspaper, ClipboardList, Coins, Download, Settings, Activity, } from 'lucide-react' import type { ReactNode } from 'react' import { Badge } from '../common' import { useUpdateCheck } from '../../hooks/useUpdateCheck' import { useOnboarding } from '../../hooks/useOnboarding' import { PingDot } from '../onboarding/PingDot' import { t } from '../../lib/i18n' import { endpoints } from '../../api/client' import appIconUrl from '../../assets/app-icon.png' interface MenuItem { id: string label: string icon: ReactNode path: string badge?: number disabled?: boolean } export function Sidebar() { const { updateAvailable } = useUpdateCheck() const { needsSetup, isSetupComplete } = useOnboarding() const [activeJobCount, setActiveJobCount] = useState(0) const mountedRef = useRef(true) const pingMap: Record = { '/usage': needsSetup.usage, '/post-types': needsSetup.contentTypes, '/links': needsSetup.links, '/reporters': needsSetup.reporters, '/settings': needsSetup.settings, } useEffect(() => { mountedRef.current = true const check = async () => { try { const result = await endpoints.listJobs({ status: 'active', limit: 1 }) if (mountedRef.current) setActiveJobCount(result?.data?.summary?.active_count || 0) } catch { /* ignore */ } } check() const interval = setInterval(check, 30000) return () => { mountedRef.current = false; clearInterval(interval) } }, []) const debugMode = Boolean((window as any).bmaiSettings?.debugMode) const menuItems: MenuItem[] = useMemo(() => { const items: MenuItem[] = [ { id: 'dashboard', label: t('Dashboard'), icon: , path: '/' }, { id: 'usage', label: t('Usage & Costs'), icon: , path: '/usage' }, { id: 'post-types', label: t('Content Types'), icon: , path: '/post-types' }, { id: 'links', label: t('Links'), icon: , path: '/links' }, { id: 'reporters', label: t('Reporters'), icon: , path: '/reporters' }, { id: 'plans', label: t('Content Plans'), icon: , path: '/plans' }, { id: 'generate', label: t('Create Content'), icon: , path: '/generate' }, { id: 'jobs', label: t('Jobs'), icon: , path: '/jobs', badge: activeJobCount > 0 ? activeJobCount : undefined }, { id: 'generated', label: t('Generated Content'), icon: , path: '/generated' }, { id: 'updates', label: t('Updates'), icon: , path: '/updates' }, { id: 'settings', label: t('Settings'), icon: , path: '/settings' }, ] if (debugMode) { items.push({ id: 'logs', label: t('Logs'), icon: , path: '/logs' }) } return items }, [activeJobCount, debugMode]) return ( ) }