From 82d38b829ae268f217041880d861982c9fbbc2a5 Mon Sep 17 00:00:00 2001 From: PEDZEO Date: Wed, 21 Jan 2026 04:28:31 +0300 Subject: [PATCH] Improve global search in AdminSettings - Search across all settings regardless of active section - Search by translated names, descriptions, and categories - Add clear search button (X icon) - Show search results count --- src/components/admin/SettingsSearch.tsx | 80 +++++++++++++++++++++ src/components/admin/SettingsSidebar.tsx | 81 +++++++++++++++++++++ src/pages/AdminSettings.tsx | 91 ++++++++++++++++++++---- 3 files changed, 240 insertions(+), 12 deletions(-) create mode 100644 src/components/admin/SettingsSearch.tsx create mode 100644 src/components/admin/SettingsSidebar.tsx diff --git a/src/components/admin/SettingsSearch.tsx b/src/components/admin/SettingsSearch.tsx new file mode 100644 index 0000000..25d6384 --- /dev/null +++ b/src/components/admin/SettingsSearch.tsx @@ -0,0 +1,80 @@ +import { useTranslation } from 'react-i18next' +import { SearchIcon, CloseIcon } from './icons' + +interface SettingsSearchProps { + searchQuery: string + setSearchQuery: (query: string) => void + resultsCount: number +} + +export function SettingsSearch({ searchQuery, setSearchQuery, resultsCount }: SettingsSearchProps) { + const { t } = useTranslation() + + return ( + <> + {/* Search - desktop */} +
+ setSearchQuery(e.target.value)} + placeholder={t('admin.settings.searchPlaceholder')} + className="w-48 lg:w-64 pl-10 pr-10 py-2 rounded-xl bg-dark-800 border border-dark-700 text-dark-100 placeholder-dark-500 focus:outline-none focus:border-accent-500 text-sm" + /> +
+ +
+ {searchQuery && ( + + )} +
+ + ) +} + +export function SettingsSearchMobile({ searchQuery, setSearchQuery }: Omit) { + const { t } = useTranslation() + + return ( +
+ setSearchQuery(e.target.value)} + placeholder={t('admin.settings.searchPlaceholder')} + className="w-full pl-10 pr-10 py-2 rounded-xl bg-dark-800 border border-dark-700 text-dark-100 placeholder-dark-500 focus:outline-none focus:border-accent-500 text-sm" + /> +
+ +
+ {searchQuery && ( + + )} +
+ ) +} + +export function SettingsSearchResults({ searchQuery, resultsCount }: { searchQuery: string; resultsCount: number }) { + if (!searchQuery.trim()) return null + + return ( +
+ + {resultsCount > 0 ? `Найдено: ${resultsCount}` : 'Ничего не найдено'} + + {resultsCount > 0 && ( + по запросу «{searchQuery}» + )} +
+ ) +} diff --git a/src/components/admin/SettingsSidebar.tsx b/src/components/admin/SettingsSidebar.tsx new file mode 100644 index 0000000..5da961a --- /dev/null +++ b/src/components/admin/SettingsSidebar.tsx @@ -0,0 +1,81 @@ +import { useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' +import { BackIcon, StarIcon, CloseIcon, MENU_SECTIONS } from './index' + +interface SettingsSidebarProps { + activeSection: string + setActiveSection: (section: string) => void + mobileMenuOpen: boolean + setMobileMenuOpen: (open: boolean) => void + favoritesCount: number +} + +export function SettingsSidebar({ + activeSection, + setActiveSection, + mobileMenuOpen, + setMobileMenuOpen, + favoritesCount +}: SettingsSidebarProps) { + const { t } = useTranslation() + + return ( + + ) +} diff --git a/src/pages/AdminSettings.tsx b/src/pages/AdminSettings.tsx index 2b25a28..5e57acd 100644 --- a/src/pages/AdminSettings.tsx +++ b/src/pages/AdminSettings.tsx @@ -72,20 +72,44 @@ export default function AdminSettings() { })) }, [currentMenuItem, allSettings, t]) - // Filter settings for search + // Helper to format setting key for translation lookup + const formatSettingKey = (name: string): string => { + return name + .replace(/_/g, ' ') + .split(' ') + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(' ') + } + + // Filter settings for search - GLOBAL search across all settings const filteredSettings = useMemo(() => { if (!allSettings || !Array.isArray(allSettings) || !searchQuery) return [] - const q = searchQuery.toLowerCase() - let settings = allSettings.filter((s: SettingDefinition) => - currentMenuItem?.categories?.includes(s.category.key) - ) + const q = searchQuery.toLowerCase().trim() + if (!q) return [] - return settings.filter((s: SettingDefinition) => - s.key.toLowerCase().includes(q) || - s.name?.toLowerCase().includes(q) - ) - }, [allSettings, currentMenuItem, searchQuery]) + return allSettings.filter((s: SettingDefinition) => { + // Search by key + if (s.key.toLowerCase().includes(q)) return true + + // Search by original name + if (s.name?.toLowerCase().includes(q)) return true + + // Search by translated name + const formattedKey = formatSettingKey(s.name || s.key) + const translatedName = t(`admin.settings.settingNames.${formattedKey}`, formattedKey) + if (translatedName.toLowerCase().includes(q)) return true + + // Search by description + if (s.hint?.description?.toLowerCase().includes(q)) return true + + // Search by category + const categoryLabel = t(`admin.settings.categories.${s.category.key}`, s.category.key) + if (categoryLabel.toLowerCase().includes(q)) return true + + return false + }) + }, [allSettings, searchQuery, t]) // Favorite settings const favoriteSettings = useMemo(() => { @@ -95,6 +119,19 @@ export default function AdminSettings() { // 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 'branding': return @@ -218,11 +255,19 @@ export default function AdminSettings() { value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} placeholder={t('admin.settings.searchPlaceholder')} - className="w-48 lg:w-64 pl-10 pr-4 py-2 rounded-xl bg-dark-800 border border-dark-700 text-dark-100 placeholder-dark-500 focus:outline-none focus:border-accent-500 text-sm" + className="w-48 lg:w-64 pl-10 pr-10 py-2 rounded-xl bg-dark-800 border border-dark-700 text-dark-100 placeholder-dark-500 focus:outline-none focus:border-accent-500 text-sm" />
+ {searchQuery && ( + + )} @@ -233,12 +278,34 @@ export default function AdminSettings() { value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} placeholder={t('admin.settings.searchPlaceholder')} - className="w-full pl-10 pr-4 py-2 rounded-xl bg-dark-800 border border-dark-700 text-dark-100 placeholder-dark-500 focus:outline-none focus:border-accent-500 text-sm" + className="w-full pl-10 pr-10 py-2 rounded-xl bg-dark-800 border border-dark-700 text-dark-100 placeholder-dark-500 focus:outline-none focus:border-accent-500 text-sm" />
+ {searchQuery && ( + + )} + + {/* Search results count */} + {searchQuery.trim() && ( +
+ + {filteredSettings.length > 0 + ? `Найдено: ${filteredSettings.length}` + : 'Ничего не найдено'} + + {filteredSettings.length > 0 && ( + по запросу «{searchQuery}» + )} +
+ )} {/* Content */}