import { useState, useMemo, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { adminSettingsApi, SettingDefinition } from '../api/adminSettings'; import { themeColorsApi } from '../api/themeColors'; import { useFavoriteSettings } from '../hooks/useFavoriteSettings'; import { SETTINGS_TREE, findTreeLocation, formatSettingKey } from '../components/admin'; import { usePlatform } from '../platform/hooks/usePlatform'; import { AnalyticsTab } from '../components/admin/AnalyticsTab'; import { BrandingTab } from '../components/admin/BrandingTab'; import { MenuEditorTab } from '../components/admin/MenuEditorTab'; import { ThemeTab } from '../components/admin/ThemeTab'; import { FavoritesTab } from '../components/admin/FavoritesTab'; import { SettingsTab } from '../components/admin/SettingsTab'; import { SettingsTreeSidebar } from '../components/admin/SettingsTreeSidebar'; import { SettingsMobileTabs } from '../components/admin/SettingsMobileTabs'; import { SettingsSearchMobile, SettingsSearchResults } from '../components/admin/SettingsSearch'; // BackIcon const BackIcon = () => ( ); // ChevronRight for breadcrumbs const ChevronRightIcon = () => ( ); export default function AdminSettings() { const { t } = useTranslation(); const navigate = useNavigate(); const { capabilities } = usePlatform(); // State const [activeSection, setActiveSection] = useState('branding'); const [searchQuery, setSearchQuery] = useState(''); // Favorites hook const { favorites, toggleFavorite, isFavorite } = useFavoriteSettings(); // Scroll to top on section change useEffect(() => { window.scrollTo({ top: 0, behavior: 'instant' }); }, [activeSection]); // Queries const { data: themeColors } = useQuery({ queryKey: ['theme-colors'], queryFn: themeColorsApi.getColors, }); const { data: allSettings } = useQuery({ queryKey: ['admin-settings'], queryFn: () => adminSettingsApi.getSettings(), }); // Find active tree info (group + child for tree sub-items) // No useMemo needed — SETTINGS_TREE is a static constant, iteration is trivial let activeTreeInfo: { group: (typeof SETTINGS_TREE.groups)[number]; child: (typeof SETTINGS_TREE.groups)[number]['children'][number]; } | null = null; for (const group of SETTINGS_TREE.groups) { const child = group.children.find((c) => c.id === activeSection); if (child) { activeTreeInfo = { group, child }; break; } } // Settings that require SALES_MODE=tariffs to be visible const TARIFF_MODE_SETTINGS = ['MULTI_TARIFF_ENABLED', 'MAX_ACTIVE_SUBSCRIPTIONS']; // Check if tariffs mode is active const isTariffsMode = useMemo(() => { if (!allSettings || !Array.isArray(allSettings)) return false; const salesMode = allSettings.find((s: SettingDefinition) => s.key === 'SALES_MODE'); return salesMode?.current === 'tariffs'; }, [allSettings]); // Get categories for current sub-item const currentCategories = useMemo(() => { if (!activeTreeInfo || !allSettings || !Array.isArray(allSettings)) return []; const categoryKeys = activeTreeInfo.child.categories; const categoryMap = new Map(); for (const setting of allSettings) { if (categoryKeys.includes(setting.category.key)) { // Hide tariff-dependent settings when not in tariffs mode if (!isTariffsMode && TARIFF_MODE_SETTINGS.includes(setting.key)) continue; if (!categoryMap.has(setting.category.key)) { categoryMap.set(setting.category.key, []); } categoryMap.get(setting.category.key)!.push(setting); } } return Array.from(categoryMap.entries()).map(([key, settings]) => ({ key, label: t(`admin.settings.categories.${key}`, key), settings, })); // eslint-disable-next-line react-hooks/exhaustive-deps -- activeTreeInfo derived from activeSection }, [activeSection, allSettings, isTariffsMode, t]); // Filter settings for search - GLOBAL search across all settings const filteredSettings = useMemo(() => { if (!allSettings || !Array.isArray(allSettings) || !searchQuery) return []; const q = searchQuery.toLowerCase().trim(); if (!q) return []; return allSettings.filter((s: SettingDefinition) => { // Hide tariff-dependent settings when not in tariffs mode if (!isTariffsMode && TARIFF_MODE_SETTINGS.includes(s.key)) return false; if (s.key.toLowerCase().includes(q)) return true; if (s.name?.toLowerCase().includes(q)) return true; const formattedKey = formatSettingKey(s.name || s.key); const translatedName = t(`admin.settings.settingNames.${formattedKey}`, formattedKey); if (translatedName.toLowerCase().includes(q)) return true; if (s.hint?.description?.toLowerCase().includes(q)) return true; const categoryLabel = t(`admin.settings.categories.${s.category.key}`, s.category.key); if (categoryLabel.toLowerCase().includes(q)) return true; return false; }); }, [allSettings, searchQuery, isTariffsMode, t]); // Favorite settings const favoriteSettings = useMemo(() => { if (!allSettings || !Array.isArray(allSettings)) return []; return allSettings.filter((s: SettingDefinition) => favorites.includes(s.key)); }, [allSettings, favorites]); // Count of modified settings const modifiedCount = useMemo(() => { if (!allSettings || !Array.isArray(allSettings)) return 0; return allSettings.filter((s: SettingDefinition) => s.has_override).length; }, [allSettings]); // Total settings count const totalCount = useMemo(() => { if (!allSettings || !Array.isArray(allSettings)) return 0; return allSettings.length; }, [allSettings]); // Handle setting selection from autocomplete const handleSelectSetting = useCallback( (setting: SettingDefinition) => { const location = findTreeLocation(setting.category.key); if (location) { setActiveSection(location.subItemId); } setSearchQuery(setting.key); }, [setActiveSection, setSearchQuery], ); // Get the display title for the current section const sectionTitle = useMemo(() => { // Special items const specialItem = SETTINGS_TREE.specialItems.find((item) => item.id === activeSection); if (specialItem) return t(`admin.settings.${specialItem.id}`); // Tree sub-items if (activeTreeInfo) return t(`admin.settings.tree.${activeTreeInfo.child.id}`); return t('admin.settings.title'); // eslint-disable-next-line react-hooks/exhaustive-deps -- activeTreeInfo derived from activeSection }, [activeSection, t]); // Render content based on active section const renderContent = () => { // If searching, always show search results regardless of active section if (searchQuery.trim()) { return ( ); } switch (activeSection) { case 'analytics': return ; case 'branding': return ; case 'theme': return ; case 'buttons': return ; case 'favorites': return ( ); default: if (activeTreeInfo) { return ( ); } // Unknown section — fallback to branding setActiveSection('branding'); return null; } }; return ( <> {/* Mobile Layout */}
{renderContent()}
{/* Desktop Layout - fixed sidebar, scrollable content */}
{/* Fixed Sidebar */}
{/* Show back button only on web, not in Telegram Mini App */} {!capabilities.hasBackButton && ( )}

{t('admin.settings.title')}

{/* Scrollable Content */}
{/* Breadcrumb for tree sub-items */} {activeTreeInfo && !searchQuery.trim() && (
{t(`admin.settings.tree.${activeTreeInfo.child.id}`)}
)} {/* Title + count badges */}

{sectionTitle}

{totalCount > 0 && !searchQuery.trim() && activeTreeInfo && (
{t('admin.settings.totalCount', { count: totalCount })} {modifiedCount > 0 && ( {t('admin.settings.modifiedCount', { count: modifiedCount })} )}
)}
{renderContent()}
); }