import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import { useStore } from '@nanostores/react'; import { Switch } from '~/components/ui/Switch'; import { classNames } from '~/utils/classNames'; import { tabConfigurationStore } from '~/lib/stores/settings'; import { TAB_LABELS } from '~/components/@settings/core/constants'; import type { TabType } from '~/components/@settings/core/types'; import { toast } from 'react-toastify'; import { TbLayoutGrid } from 'react-icons/tb'; import { useSettingsStore } from '~/lib/stores/settings'; // Define tab icons mapping const TAB_ICONS: Record = { profile: 'i-ph:user-circle-fill', settings: 'i-ph:gear-six-fill', notifications: 'i-ph:bell-fill', features: 'i-ph:star-fill', data: 'i-ph:database-fill', 'cloud-providers': 'i-ph:cloud-fill', 'local-providers': 'i-ph:desktop-fill', 'service-status': 'i-ph:activity-fill', connection: 'i-ph:wifi-high-fill', debug: 'i-ph:bug-fill', 'event-logs': 'i-ph:list-bullets-fill', notes: 'i-ph:note-pencil', update: 'i-ph:arrow-clockwise-fill', 'task-manager': 'i-ph:chart-line-fill', 'tab-management': 'i-ph:squares-four-fill', }; // Define which tabs are default in user mode const DEFAULT_USER_TABS: TabType[] = [ 'features', 'data', 'cloud-providers', 'local-providers', 'connection', 'notifications', 'event-logs', 'notes', ]; // Define which tabs can be added to user mode const OPTIONAL_USER_TABS: TabType[] = ['profile', 'settings', 'task-manager', 'service-status', 'debug', 'update']; // All available tabs for user mode const ALL_USER_TABS = [...DEFAULT_USER_TABS, ...OPTIONAL_USER_TABS]; // Define which tabs are beta const BETA_TABS = new Set(['task-manager', 'service-status', 'update', 'local-providers']); // Beta label component const BetaLabel = () => ( BETA ); export const TabManagement = () => { const [searchQuery, setSearchQuery] = useState(''); const tabConfiguration = useStore(tabConfigurationStore); const { setSelectedTab } = useSettingsStore(); const handleTabVisibilityChange = (tabId: TabType, checked: boolean) => { // Get current tab configuration const currentTab = tabConfiguration.userTabs.find((tab) => tab.id === tabId); // If tab doesn't exist in configuration, create it if (!currentTab) { const newTab = { id: tabId, visible: checked, window: 'user' as const, order: tabConfiguration.userTabs.length, }; const updatedTabs = [...tabConfiguration.userTabs, newTab]; tabConfigurationStore.set({ ...tabConfiguration, userTabs: updatedTabs, }); toast.success(`Tab ${checked ? 'enabled' : 'disabled'} successfully`); return; } // Check if tab can be enabled in user mode const canBeEnabled = DEFAULT_USER_TABS.includes(tabId) || OPTIONAL_USER_TABS.includes(tabId); if (!canBeEnabled && checked) { toast.error('This tab cannot be enabled in user mode'); return; } // Update tab visibility const updatedTabs = tabConfiguration.userTabs.map((tab) => { if (tab.id === tabId) { return { ...tab, visible: checked }; } return tab; }); // Update store tabConfigurationStore.set({ ...tabConfiguration, userTabs: updatedTabs, }); // Show success message toast.success(`Tab ${checked ? 'enabled' : 'disabled'} successfully`); }; // Create a map of existing tab configurations const tabConfigMap = new Map(tabConfiguration.userTabs.map((tab) => [tab.id, tab])); // Generate the complete list of tabs, including those not in the configuration const allTabs = ALL_USER_TABS.map((tabId) => { return ( tabConfigMap.get(tabId) || { id: tabId, visible: false, window: 'user' as const, order: -1, } ); }); // Filter tabs based on search query const filteredTabs = allTabs.filter((tab) => TAB_LABELS[tab.id].toLowerCase().includes(searchQuery.toLowerCase())); useEffect(() => { // Reset to first tab when component unmounts return () => { setSelectedTab('user'); // Reset to user tab when unmounting }; }, [setSelectedTab]); return (
{/* Header */}

Tab Management

Configure visible tabs and their order

{/* Search */}
setSearchQuery(e.target.value)} placeholder="Search tabs..." className={classNames( 'w-full pl-10 pr-4 py-2 rounded-lg', 'bg-bolt-elements-background-depth-2', 'border border-bolt-elements-borderColor', 'text-bolt-elements-textPrimary', 'placeholder-bolt-elements-textTertiary', 'focus:outline-none focus:ring-2 focus:ring-purple-500/30', 'transition-all duration-200', )} />
{/* Tab Grid */}
{/* Default Section Header */} {filteredTabs.some((tab) => DEFAULT_USER_TABS.includes(tab.id)) && (
Default Tabs
)} {/* Default Tabs */} {filteredTabs .filter((tab) => DEFAULT_USER_TABS.includes(tab.id)) .map((tab, index) => ( {/* Status Badges */}
Default

{TAB_LABELS[tab.id]}

{BETA_TABS.has(tab.id) && }

{tab.visible ? 'Visible in user mode' : 'Hidden in user mode'}

{ const isDisabled = !DEFAULT_USER_TABS.includes(tab.id) && !OPTIONAL_USER_TABS.includes(tab.id); if (!isDisabled) { handleTabVisibilityChange(tab.id, checked); } }} className={classNames('data-[state=checked]:bg-purple-500 ml-4', { 'opacity-50 pointer-events-none': !DEFAULT_USER_TABS.includes(tab.id) && !OPTIONAL_USER_TABS.includes(tab.id), })} />
))} {/* Optional Section Header */} {filteredTabs.some((tab) => OPTIONAL_USER_TABS.includes(tab.id)) && (
Optional Tabs
)} {/* Optional Tabs */} {filteredTabs .filter((tab) => OPTIONAL_USER_TABS.includes(tab.id)) .map((tab, index) => ( {/* Status Badges */}
Optional

{TAB_LABELS[tab.id]}

{BETA_TABS.has(tab.id) && }

{tab.visible ? 'Visible in user mode' : 'Hidden in user mode'}

{ const isDisabled = !DEFAULT_USER_TABS.includes(tab.id) && !OPTIONAL_USER_TABS.includes(tab.id); if (!isDisabled) { handleTabVisibilityChange(tab.id, checked); } }} className={classNames('data-[state=checked]:bg-purple-500 ml-4', { 'opacity-50 pointer-events-none': !DEFAULT_USER_TABS.includes(tab.id) && !OPTIONAL_USER_TABS.includes(tab.id), })} />
))}
); };