import { useStore } from '@nanostores/react'; import * as RadixDialog from '@radix-ui/react-dialog'; import classNames from 'classnames'; import { AnimatePresence, motion, type Variants } from 'framer-motion'; import { useEffect, useMemo, useState } from 'react'; import { TabTile } from '~/components/@settings/core/TabTile'; import DebugTab from '~/components/@settings/tabs/debug/DebugTab'; import { EventLogsTab } from '~/components/@settings/tabs/event-logs/EventLogsTab'; import NotificationsTab from '~/components/@settings/tabs/notifications/NotificationsTab'; import SettingsTab from '~/components/@settings/tabs/settings/SettingsTab'; import TaskManagerTab from '~/components/@settings/tabs/task-manager/TaskManagerTab'; import BackgroundRays from '~/components/ui/BackgroundRays'; import { useDebugStatus } from '~/lib/hooks/useDebugStatus'; import { useNotifications } from '~/lib/hooks/useNotifications'; import { profileStore } from '~/lib/stores/profile'; import { resetTabConfiguration, tabConfigurationStore } from '~/lib/stores/settings'; import { logger } from '~/utils/logger'; import { AvatarDropdown } from './AvatarDropdown'; import { DEFAULT_TAB_CONFIG, TAB_DESCRIPTIONS } from './constants'; import type { Profile, TabType, TabVisibilityConfig } from './types'; interface ControlPanelProps { open: boolean; onClose: () => void; } interface TabWithDevType extends TabVisibilityConfig { isExtraDevTab?: boolean; } interface ExtendedTabConfig extends TabVisibilityConfig { isExtraDevTab?: boolean; } interface BaseTabConfig { id: TabType; visible: boolean; window: 'user' | 'developer'; order: number; } export const ControlPanel = ({ open, onClose }: ControlPanelProps) => { // State const [activeTab, setActiveTab] = useState(null); const [loadingTab, setLoadingTab] = useState(null); // Store values const tabConfiguration = useStore(tabConfigurationStore); const profile = useStore(profileStore) as Profile; // Status hooks const { hasUnreadNotifications, unreadNotifications, markAllAsRead } = useNotifications(); const { hasActiveWarnings, activeIssues, acknowledgeAllIssues } = useDebugStatus(); // Memoize the base tab configurations to avoid recalculation const baseTabConfig = useMemo(() => { return new Map(DEFAULT_TAB_CONFIG.map((tab) => [tab.id, tab])); }, []); // Add visibleTabs logic using useMemo with optimized calculations const visibleTabs = useMemo(() => { if (!tabConfiguration?.userTabs || !Array.isArray(tabConfiguration.userTabs)) { logger.warn('Invalid tab configuration, resetting to defaults'); resetTabConfiguration(); return []; } const seenTabs = new Set(); const devTabs: ExtendedTabConfig[] = []; // Process tabs in order of priority: developer, user, default const processTab = (tab: BaseTabConfig) => { if (!seenTabs.has(tab.id)) { seenTabs.add(tab.id); devTabs.push({ id: tab.id, visible: true, window: 'developer', order: tab.order || devTabs.length, }); } }; // Process tabs in priority order tabConfiguration.developerTabs?.forEach((tab) => processTab(tab as BaseTabConfig)); tabConfiguration.userTabs.forEach((tab) => processTab(tab as BaseTabConfig)); DEFAULT_TAB_CONFIG.forEach((tab) => processTab(tab as BaseTabConfig)); return devTabs.sort((a, b) => a.order - b.order); }, [tabConfiguration, profile?.preferences?.notifications, baseTabConfig]); // Optimize animation performance with layout animations const gridLayoutVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.05, delayChildren: 0.1, }, }, }; const itemVariants: Variants = { hidden: { opacity: 0, scale: 0.8 }, visible: { opacity: 1, scale: 1, transition: { type: 'spring', stiffness: 200, damping: 20, mass: 0.6, }, }, }; // Reset to default view when modal opens/closes useEffect(() => { if (!open) { // Reset when closing setActiveTab(null); setLoadingTab(null); } else { // When opening, set to null to show the main view setActiveTab(null); } }, [open]); // Handle closing const handleClose = () => { setActiveTab(null); setLoadingTab(null); onClose(); }; // Handlers const handleBack = () => { setActiveTab(null); }; const getTabComponent = (tabId: TabType) => { switch (tabId) { case 'settings': return ; case 'notifications': return ; case 'debug': return ; case 'event-logs': return ; case 'task-manager': return ; default: return null; } }; const getTabUpdateStatus = (tabId: TabType): boolean => { switch (tabId) { case 'notifications': return hasUnreadNotifications; case 'debug': return hasActiveWarnings; default: return false; } }; const getStatusMessage = (tabId: TabType): string => { switch (tabId) { case 'notifications': return `${unreadNotifications.length} unread notification${unreadNotifications.length === 1 ? '' : 's'}`; case 'debug': { const warnings = activeIssues.filter((i) => i.type === 'warning').length; const errors = activeIssues.filter((i) => i.type === 'error').length; return `${warnings} warning${warnings === 1 ? '' : 's'}, ${errors} error${errors === 1 ? '' : 's'}`; } default: return ''; } }; const handleTabClick = (tabId: TabType) => { setLoadingTab(tabId); setActiveTab(tabId); // Acknowledge notifications based on tab switch (tabId) { case 'notifications': markAllAsRead(); break; case 'debug': acknowledgeAllIssues(); break; } // Clear loading state after a delay setTimeout(() => setLoadingTab(null), 500); }; return (
{/* Header */}
{activeTab && ( )}
{/* Avatar and Dropdown */}
{/* Close Button */}
{/* Content */}
{activeTab ? ( getTabComponent(activeTab) ) : ( {(visibleTabs as TabWithDevType[]).map((tab: TabWithDevType) => ( handleTabClick(tab.id as TabType)} isActive={activeTab === tab.id} hasUpdate={getTabUpdateStatus(tab.id)} statusMessage={getStatusMessage(tab.id)} description={TAB_DESCRIPTIONS[tab.id]} isLoading={loadingTab === tab.id} className="h-full relative" > ))} )}
); };