diff --git a/src/locales/ru.json b/src/locales/ru.json index 9c993c1..a32c61e 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -674,7 +674,108 @@ "searchPlaceholder": "Поиск настроек...", "searchCategoriesPlaceholder": "Поиск категорий...", "noSearchResults": "Ничего не найдено", - "example": "Пример" + "example": "Пример", + "favorites": "Избранное", + "favoritesEmpty": "Нет избранных настроек", + "favoritesHint": "Нажмите ⭐ рядом с настройкой, чтобы добавить её сюда", + "branding": "Брендинг", + "theme": "Тема", + "payments": "Платежи", + "subscriptions": "Подписки", + "interface": "Интерфейс", + "notifications": "Уведомления", + "database": "База данных", + "system": "Система", + "users": "Пользователи", + "logoAndName": "Логотип и название", + "projectName": "Название проекта", + "interfaceOptions": "Опции интерфейса", + "animatedBackground": "Анимированный фон", + "animatedBackgroundDesc": "Волны на фоне приложения", + "autoFullscreen": "Авто-Fullscreen", + "autoFullscreenDesc": "В Telegram WebApp", + "availableThemes": "Доступные темы", + "darkTheme": "Тёмная", + "lightTheme": "Светлая", + "quickPresets": "Быстрые пресеты", + "customColors": "Ручная настройка", + "accentColor": "Акцентный цвет", + "statusColors": "Статусные цвета", + "resetAllColors": "Сбросить все цвета", + "notSpecified": "Не указано", + "presets": { + "standard": "Стандарт", + "ocean": "Океан", + "forest": "Лес", + "sunset": "Закат", + "violet": "Фиолет", + "rose": "Роза", + "midnight": "Полночь", + "turquoise": "Бирюза" + }, + "colors": { + "background": "Фон", + "surface": "Поверхность", + "text": "Текст", + "textSecondary": "Вторичный текст", + "success": "Успех", + "warning": "Предупреждение", + "error": "Ошибка" + }, + "categories": { + "PAYMENT": "Платежи", + "PAYMENT_VERIFICATION": "Верификация платежей", + "YOOKASSA": "YooKassa", + "CRYPTOBOT": "CryptoBot", + "HELEKET": "Heleket", + "PLATEGA": "Platega", + "TRIBUTE": "Tribute", + "MULENPAY": "MulenPay", + "PAL24": "PAL24", + "WATA": "Wata", + "TELEGRAM": "Telegram", + "SUBSCRIPTIONS_CORE": "Основные", + "SIMPLE_SUBSCRIPTION": "Простая подписка", + "PERIODS": "Периоды", + "SUBSCRIPTION_PRICES": "Цены", + "TRAFFIC": "Трафик", + "TRAFFIC_PACKAGES": "Пакеты трафика", + "TRIAL": "Пробный период", + "AUTOPAY": "Автоплатёж", + "INTERFACE": "Интерфейс", + "INTERFACE_BRANDING": "Брендинг", + "INTERFACE_SUBSCRIPTION": "Подписка", + "CONNECT_BUTTON": "Кнопка подключения", + "MINIAPP": "MiniApp", + "HAPP": "HAPP", + "SKIP": "Пропуск", + "ADDITIONAL": "Дополнительно", + "NOTIFICATIONS": "Уведомления", + "ADMIN_NOTIFICATIONS": "Уведомления админов", + "ADMIN_REPORTS": "Отчёты админов", + "DATABASE": "База данных", + "POSTGRES": "PostgreSQL", + "SQLITE": "SQLite", + "REDIS": "Redis", + "CORE": "Ядро", + "REMNAWAVE": "RemnaWave", + "SERVER_STATUS": "Статус сервера", + "MONITORING": "Мониторинг", + "MAINTENANCE": "Обслуживание", + "BACKUP": "Бэкап", + "VERSION": "Версия", + "WEB_API": "Web API", + "WEBHOOK": "Webhook", + "LOG": "Логи", + "DEBUG": "Отладка", + "EXTERNAL_ADMIN": "Внешний админ", + "SUPPORT": "Поддержка", + "LOCALIZATION": "Локализация", + "CHANNEL": "Канал", + "TIMEZONE": "Часовой пояс", + "REFERRAL": "Реферальная система", + "MODERATION": "Модерация" + } }, "apps": { "title": "Управление приложениями", diff --git a/src/pages/AdminSettings.tsx b/src/pages/AdminSettings.tsx index ad30119..3749d3a 100644 --- a/src/pages/AdminSettings.tsx +++ b/src/pages/AdminSettings.tsx @@ -38,6 +38,12 @@ const ChevronDownIcon = () => ( ) +const ChevronRightIcon = () => ( + + + +) + const UploadIcon = () => ( @@ -92,83 +98,52 @@ const MoonIcon = () => ( ) -// ============ TAB DEFINITIONS ============ -const TABS = [ - { id: 'favorites', label: 'Избранное', icon: StarIcon }, - { id: 'branding', label: 'Брендинг', icon: null }, - { id: 'theme', label: 'Тема', icon: null }, - { id: 'payments', label: 'Платежи', icon: null }, - { id: 'subscriptions', label: 'Подписки', icon: null }, - { id: 'interface', label: 'Интерфейс', icon: null }, - { id: 'more', label: 'Ещё', icon: ChevronDownIcon, isDropdown: true }, -] as const - -const MORE_TABS = [ - { id: 'notifications', label: 'Уведомления' }, - { id: 'database', label: 'База данных' }, - { id: 'system', label: 'Система' }, - { id: 'users', label: 'Пользователи' }, -] as const - -// ============ META CATEGORIES ============ -const META_CATEGORIES: Record = { - payments: { - label: 'Платежные системы', - categories: ['PAYMENT', 'PAYMENT_VERIFICATION', 'YOOKASSA', 'CRYPTOBOT', 'HELEKET', 'PLATEGA', 'TRIBUTE', 'MULENPAY', 'PAL24', 'WATA', 'TELEGRAM'], - }, - subscriptions: { - label: 'Подписки и тарифы', - categories: ['SUBSCRIPTIONS_CORE', 'SIMPLE_SUBSCRIPTION', 'PERIODS', 'SUBSCRIPTION_PRICES', 'TRAFFIC', 'TRAFFIC_PACKAGES', 'TRIAL', 'AUTOPAY'], - }, - interface: { - label: 'Интерфейс', - categories: ['INTERFACE', 'INTERFACE_BRANDING', 'INTERFACE_SUBSCRIPTION', 'CONNECT_BUTTON', 'MINIAPP', 'HAPP', 'SKIP', 'ADDITIONAL'], - }, - notifications: { - label: 'Уведомления', - categories: ['NOTIFICATIONS', 'ADMIN_NOTIFICATIONS', 'ADMIN_REPORTS'], - }, - database: { - label: 'База данных', - categories: ['DATABASE', 'POSTGRES', 'SQLITE', 'REDIS'], - }, - system: { - label: 'Система', - categories: ['CORE', 'REMNAWAVE', 'SERVER_STATUS', 'MONITORING', 'MAINTENANCE', 'BACKUP', 'VERSION', 'WEB_API', 'WEBHOOK', 'LOG', 'DEBUG', 'EXTERNAL_ADMIN'], - }, - users: { - label: 'Пользователи', - categories: ['SUPPORT', 'LOCALIZATION', 'CHANNEL', 'TIMEZONE', 'REFERRAL', 'MODERATION'], - }, +// ============ SIDEBAR MENU ITEMS ============ +interface MenuItem { + id: string + icon: ((props: { filled?: boolean }) => JSX.Element) | null + categories?: string[] } -// ============ THEME PRESETS ============ -const THEME_PRESETS: { name: string; colors: ThemeColors }[] = [ - { name: 'Стандарт', colors: DEFAULT_THEME_COLORS }, - { name: 'Океан', colors: { accent: '#0ea5e9', darkBackground: '#0c1222', darkSurface: '#1e293b', darkText: '#f1f5f9', darkTextSecondary: '#94a3b8', lightBackground: '#e0f2fe', lightSurface: '#f0f9ff', lightText: '#0c4a6e', lightTextSecondary: '#0369a1', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, - { name: 'Лес', colors: { accent: '#22c55e', darkBackground: '#0a1a0f', darkSurface: '#14532d', darkText: '#f0fdf4', darkTextSecondary: '#86efac', lightBackground: '#dcfce7', lightSurface: '#f0fdf4', lightText: '#14532d', lightTextSecondary: '#166534', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, - { name: 'Закат', colors: { accent: '#f97316', darkBackground: '#1c1009', darkSurface: '#2d1a0e', darkText: '#fff7ed', darkTextSecondary: '#fdba74', lightBackground: '#ffedd5', lightSurface: '#fff7ed', lightText: '#7c2d12', lightTextSecondary: '#c2410c', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, - { name: 'Фиолет', colors: { accent: '#a855f7', darkBackground: '#0f0a1a', darkSurface: '#1e1b2e', darkText: '#faf5ff', darkTextSecondary: '#c4b5fd', lightBackground: '#f3e8ff', lightSurface: '#faf5ff', lightText: '#581c87', lightTextSecondary: '#7e22ce', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, - { name: 'Роза', colors: { accent: '#f43f5e', darkBackground: '#1a0a10', darkSurface: '#2d1520', darkText: '#fff1f2', darkTextSecondary: '#fda4af', lightBackground: '#ffe4e6', lightSurface: '#fff1f2', lightText: '#881337', lightTextSecondary: '#be123c', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, - { name: 'Полночь', colors: { accent: '#6366f1', darkBackground: '#030712', darkSurface: '#111827', darkText: '#f9fafb', darkTextSecondary: '#9ca3af', lightBackground: '#e5e7eb', lightSurface: '#f3f4f6', lightText: '#111827', lightTextSecondary: '#4b5563', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, - { name: 'Бирюза', colors: { accent: '#14b8a6', darkBackground: '#0a1614', darkSurface: '#134e4a', darkText: '#f0fdfa', darkTextSecondary: '#5eead4', lightBackground: '#ccfbf1', lightSurface: '#f0fdfa', lightText: '#134e4a', lightTextSecondary: '#0f766e', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, +interface MenuSection { + id: string + items: MenuItem[] +} + +const MENU_SECTIONS: MenuSection[] = [ + { + id: 'main', + items: [ + { id: 'favorites', icon: StarIcon }, + { id: 'branding', icon: null }, + { id: 'theme', icon: null }, + ] + }, + { + id: 'settings', + items: [ + { id: 'payments', icon: null, categories: ['PAYMENT', 'PAYMENT_VERIFICATION', 'YOOKASSA', 'CRYPTOBOT', 'HELEKET', 'PLATEGA', 'TRIBUTE', 'MULENPAY', 'PAL24', 'WATA', 'TELEGRAM'] }, + { id: 'subscriptions', icon: null, categories: ['SUBSCRIPTIONS_CORE', 'SIMPLE_SUBSCRIPTION', 'PERIODS', 'SUBSCRIPTION_PRICES', 'TRAFFIC', 'TRAFFIC_PACKAGES', 'TRIAL', 'AUTOPAY'] }, + { id: 'interface', icon: null, categories: ['INTERFACE', 'INTERFACE_BRANDING', 'INTERFACE_SUBSCRIPTION', 'CONNECT_BUTTON', 'MINIAPP', 'HAPP', 'SKIP', 'ADDITIONAL'] }, + { id: 'notifications', icon: null, categories: ['NOTIFICATIONS', 'ADMIN_NOTIFICATIONS', 'ADMIN_REPORTS'] }, + { id: 'database', icon: null, categories: ['DATABASE', 'POSTGRES', 'SQLITE', 'REDIS'] }, + { id: 'system', icon: null, categories: ['CORE', 'REMNAWAVE', 'SERVER_STATUS', 'MONITORING', 'MAINTENANCE', 'BACKUP', 'VERSION', 'WEB_API', 'WEBHOOK', 'LOG', 'DEBUG', 'EXTERNAL_ADMIN'] }, + { id: 'users', icon: null, categories: ['SUPPORT', 'LOCALIZATION', 'CHANNEL', 'TIMEZONE', 'REFERRAL', 'MODERATION'] }, + ] + } ] -// ============ SETTING TRANSLATIONS ============ -const SETTING_TRANSLATIONS: Record = { - BOT_USERNAME: { name: 'Username бота', description: 'Имя бота в Telegram' }, - SUPPORT_USERNAME: { name: 'Username поддержки', description: 'Telegram @username поддержки' }, - SUPPORT_MENU_ENABLED: { name: 'Меню поддержки', description: 'Показывать раздел поддержки' }, - DEFAULT_LANGUAGE: { name: 'Язык по умолчанию', description: 'Язык интерфейса' }, - TIMEZONE: { name: 'Часовой пояс', description: 'Временная зона системы' }, - REFERRAL_PROGRAM_ENABLED: { name: 'Реферальная программа', description: 'Включить рефералку' }, - YOOKASSA_ENABLED: { name: 'YooKassa', description: 'Приём платежей через YooKassa' }, - CRYPTOBOT_ENABLED: { name: 'CryptoBot', description: 'Приём криптовалюты' }, - TELEGRAM_STARS_ENABLED: { name: 'Telegram Stars', description: 'Оплата звёздами' }, - TRIAL_DURATION_DAYS: { name: 'Триал (дней)', description: 'Длительность пробного периода' }, - DEFAULT_DEVICE_LIMIT: { name: 'Устройств по умолчанию', description: 'Лимит устройств' }, - // Add more as needed... -} +// ============ THEME PRESETS ============ +const THEME_PRESETS: { id: string; colors: ThemeColors }[] = [ + { id: 'standard', colors: DEFAULT_THEME_COLORS }, + { id: 'ocean', colors: { accent: '#0ea5e9', darkBackground: '#0c1222', darkSurface: '#1e293b', darkText: '#f1f5f9', darkTextSecondary: '#94a3b8', lightBackground: '#e0f2fe', lightSurface: '#f0f9ff', lightText: '#0c4a6e', lightTextSecondary: '#0369a1', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, + { id: 'forest', colors: { accent: '#22c55e', darkBackground: '#0a1a0f', darkSurface: '#14532d', darkText: '#f0fdf4', darkTextSecondary: '#86efac', lightBackground: '#dcfce7', lightSurface: '#f0fdf4', lightText: '#14532d', lightTextSecondary: '#166534', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, + { id: 'sunset', colors: { accent: '#f97316', darkBackground: '#1c1009', darkSurface: '#2d1a0e', darkText: '#fff7ed', darkTextSecondary: '#fdba74', lightBackground: '#ffedd5', lightSurface: '#fff7ed', lightText: '#7c2d12', lightTextSecondary: '#c2410c', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, + { id: 'violet', colors: { accent: '#a855f7', darkBackground: '#0f0a1a', darkSurface: '#1e1b2e', darkText: '#faf5ff', darkTextSecondary: '#c4b5fd', lightBackground: '#f3e8ff', lightSurface: '#faf5ff', lightText: '#581c87', lightTextSecondary: '#7e22ce', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, + { id: 'rose', colors: { accent: '#f43f5e', darkBackground: '#1a0a10', darkSurface: '#2d1520', darkText: '#fff1f2', darkTextSecondary: '#fda4af', lightBackground: '#ffe4e6', lightSurface: '#fff1f2', lightText: '#881337', lightTextSecondary: '#be123c', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, + { id: 'midnight', colors: { accent: '#6366f1', darkBackground: '#030712', darkSurface: '#111827', darkText: '#f9fafb', darkTextSecondary: '#9ca3af', lightBackground: '#e5e7eb', lightSurface: '#f3f4f6', lightText: '#111827', lightTextSecondary: '#4b5563', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, + { id: 'turquoise', colors: { accent: '#14b8a6', darkBackground: '#0a1614', darkSurface: '#134e4a', darkText: '#f0fdfa', darkTextSecondary: '#5eead4', lightBackground: '#ccfbf1', lightSurface: '#f0fdfa', lightText: '#134e4a', lightTextSecondary: '#0f766e', success: '#22c55e', warning: '#f59e0b', error: '#ef4444' } }, +] // ============ MAIN COMPONENT ============ export default function AdminSettings() { @@ -177,12 +152,13 @@ export default function AdminSettings() { const fileInputRef = useRef(null) // State - const [activeTab, setActiveTab] = useState('branding') + const [activeSection, setActiveSection] = useState('branding') + const [activeSubCategory, setActiveSubCategory] = useState(null) const [searchQuery, setSearchQuery] = useState('') - const [showMoreDropdown, setShowMoreDropdown] = useState(false) const [editingName, setEditingName] = useState(false) const [newName, setNewName] = useState('') const [expandedSections, setExpandedSections] = useState>(new Set(['presets'])) + const [sidebarCollapsed, setSidebarCollapsed] = useState(false) // Favorites hook const { favorites, toggleFavorite, isFavorite } = useFavoriteSettings() @@ -318,24 +294,61 @@ export default function AdminSettings() { }) } - // Filter settings by tab + // Get current menu item configuration + const currentMenuItem = useMemo(() => { + for (const section of MENU_SECTIONS) { + const item = section.items.find(i => i.id === activeSection) + if (item) return item + } + return null + }, [activeSection]) + + // Get categories for current section + const currentCategories = useMemo(() => { + if (!currentMenuItem?.categories || !allSettings || !Array.isArray(allSettings)) return [] + + const categoryMap = new Map() + + for (const setting of allSettings) { + if (currentMenuItem.categories.includes(setting.category.key)) { + 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 + })) + }, [currentMenuItem, allSettings, t]) + + // Filter settings for current view const filteredSettings = useMemo(() => { if (!allSettings || !Array.isArray(allSettings)) return [] - const metaCategories = META_CATEGORIES[activeTab]?.categories || [] - let filtered = allSettings.filter((s: SettingDefinition) => metaCategories.includes(s.category.key)) + let settings: SettingDefinition[] = [] - if (searchQuery) { - const q = searchQuery.toLowerCase() - filtered = filtered.filter((s: SettingDefinition) => - s.key.toLowerCase().includes(q) || - s.name?.toLowerCase().includes(q) || - SETTING_TRANSLATIONS[s.key]?.name.toLowerCase().includes(q) + if (activeSubCategory) { + settings = allSettings.filter((s: SettingDefinition) => s.category.key === activeSubCategory) + } else if (currentMenuItem?.categories) { + settings = allSettings.filter((s: SettingDefinition) => + currentMenuItem.categories!.includes(s.category.key) ) } - return filtered - }, [allSettings, activeTab, searchQuery]) + if (searchQuery) { + const q = searchQuery.toLowerCase() + settings = settings.filter((s: SettingDefinition) => + s.key.toLowerCase().includes(q) || + s.name?.toLowerCase().includes(q) + ) + } + + return settings + }, [allSettings, activeSection, activeSubCategory, currentMenuItem, searchQuery]) // Favorite settings const favoriteSettings = useMemo(() => { @@ -359,9 +372,6 @@ export default function AdminSettings() { ) const renderSettingRow = (setting: SettingDefinition) => { - const trans = SETTING_TRANSLATIONS[setting.key] - const name = trans?.name || setting.name || setting.key - const desc = trans?.description || setting.hint?.description const isFav = isFavorite(setting.key) return ( @@ -371,14 +381,16 @@ export default function AdminSettings() { > - {name} + {setting.name || setting.key} {setting.has_override && ( - Изменено + {t('admin.settings.modified')} )} - {desc && {desc}} + {setting.hint?.description && ( + {setting.hint.description} + )} @@ -423,7 +435,7 @@ export default function AdminSettings() { onClick={() => resetSettingMutation.mutate(setting.key)} disabled={resetSettingMutation.isPending} className="p-1.5 rounded-lg text-dark-400 hover:text-dark-200 hover:bg-dark-700 transition-colors" - title="Сбросить" + title={t('admin.settings.reset')} > @@ -433,12 +445,12 @@ export default function AdminSettings() { ) } - // ============ TAB CONTENT ============ - const renderBrandingTab = () => ( + // ============ CONTENT RENDERERS ============ + const renderBrandingContent = () => ( {/* Logo & Name */} - Логотип и название + {t('admin.settings.logoAndName')} {/* Logo */} @@ -485,7 +497,7 @@ export default function AdminSettings() { {/* Name */} - Название проекта + {t('admin.settings.projectName')} {editingName ? ( setNewName(e.target.value)} className="flex-1 px-4 py-2 rounded-xl bg-dark-700 border border-dark-600 text-dark-100 focus:outline-none focus:border-accent-500" - placeholder="Название" maxLength={50} /> ) : ( - {branding?.name || 'Не указано'} + {branding?.name || t('admin.settings.notSpecified')} { setNewName(branding?.name ?? '') @@ -530,13 +541,13 @@ export default function AdminSettings() { {/* Animation & Fullscreen toggles */} - Опции интерфейса + {t('admin.settings.interfaceOptions')} - Анимированный фон - Волны на фоне приложения + {t('admin.settings.animatedBackground')} + {t('admin.settings.animatedBackgroundDesc')} {renderToggle( animationSettings?.enabled ?? true, @@ -547,8 +558,8 @@ export default function AdminSettings() { - Авто-Fullscreen - В Telegram WebApp + {t('admin.settings.autoFullscreen')} + {t('admin.settings.autoFullscreenDesc')} {renderToggle( fullscreenSettings?.enabled ?? false, @@ -561,17 +572,17 @@ export default function AdminSettings() { ) - const renderThemeTab = () => ( + const renderThemeContent = () => ( {/* Theme toggles */} - Доступные темы + {t('admin.settings.availableThemes')} - Тёмная + {t('admin.settings.darkTheme')} {renderToggle( enabledThemes?.dark ?? true, @@ -586,7 +597,7 @@ export default function AdminSettings() { - Светлая + {t('admin.settings.lightTheme')} {renderToggle( enabledThemes?.light ?? true, @@ -606,7 +617,7 @@ export default function AdminSettings() { onClick={() => toggleSection('presets')} className="w-full flex items-center justify-between" > - Быстрые пресеты + {t('admin.settings.quickPresets')} @@ -616,7 +627,7 @@ export default function AdminSettings() { {THEME_PRESETS.map((preset) => ( updateColorsMutation.mutate(preset.colors)} disabled={updateColorsMutation.isPending} className="p-3 rounded-xl border border-dark-600 hover:border-dark-500 transition-all hover:scale-[1.02]" @@ -628,7 +639,7 @@ export default function AdminSettings() { style={{ backgroundColor: preset.colors.accent }} /> - {preset.name} + {t(`admin.settings.presets.${preset.id}`)} @@ -648,7 +659,7 @@ export default function AdminSettings() { onClick={() => toggleSection('colors')} className="w-full flex items-center justify-between" > - Ручная настройка + {t('admin.settings.customColors')} @@ -658,7 +669,7 @@ export default function AdminSettings() { {/* Accent */} - Акцентный цвет + {t('admin.settings.accentColor')} - Тёмная тема + {t('admin.settings.darkTheme')} updateColorsMutation.mutate({ darkBackground: color })} disabled={updateColorsMutation.isPending} /> updateColorsMutation.mutate({ darkSurface: color })} disabled={updateColorsMutation.isPending} /> updateColorsMutation.mutate({ darkText: color })} disabled={updateColorsMutation.isPending} /> updateColorsMutation.mutate({ darkTextSecondary: color })} disabled={updateColorsMutation.isPending} @@ -703,29 +714,29 @@ export default function AdminSettings() { {/* Light theme */} - Светлая тема + {t('admin.settings.lightTheme')} updateColorsMutation.mutate({ lightBackground: color })} disabled={updateColorsMutation.isPending} /> updateColorsMutation.mutate({ lightSurface: color })} disabled={updateColorsMutation.isPending} /> updateColorsMutation.mutate({ lightText: color })} disabled={updateColorsMutation.isPending} /> updateColorsMutation.mutate({ lightTextSecondary: color })} disabled={updateColorsMutation.isPending} @@ -735,22 +746,22 @@ export default function AdminSettings() { {/* Status colors */} - Статусные цвета + {t('admin.settings.statusColors')} updateColorsMutation.mutate({ success: color })} disabled={updateColorsMutation.isPending} /> updateColorsMutation.mutate({ warning: color })} disabled={updateColorsMutation.isPending} /> updateColorsMutation.mutate({ error: color })} disabled={updateColorsMutation.isPending} @@ -764,7 +775,7 @@ export default function AdminSettings() { disabled={resetColorsMutation.isPending} className="px-4 py-2 rounded-xl bg-dark-700 text-dark-300 hover:bg-dark-600 transition-colors disabled:opacity-50" > - Сбросить все цвета + {t('admin.settings.resetAllColors')} )} @@ -772,15 +783,15 @@ export default function AdminSettings() { ) - const renderFavoritesTab = () => ( + const renderFavoritesContent = () => ( {favoriteSettings.length === 0 ? ( - - Нет избранных настроек - - Нажмите ⭐ рядом с настройкой, чтобы добавить её сюда - + + + + {t('admin.settings.favoritesEmpty')} + {t('admin.settings.favoritesHint')} ) : ( favoriteSettings.map(renderSettingRow) @@ -788,11 +799,42 @@ export default function AdminSettings() { ) - const renderSettingsTab = () => ( + const renderSettingsContent = () => ( + {/* Sub-categories navigation */} + {currentCategories.length > 1 && !activeSubCategory && ( + + {currentCategories.map((cat) => ( + setActiveSubCategory(cat.key)} + className="p-4 rounded-xl bg-dark-800/50 border border-dark-700/50 hover:border-dark-600 transition-all text-left" + > + + {cat.label} + + + {cat.settings.length} {t('admin.settings.settingsCount')} + + ))} + + )} + + {/* Back button if in sub-category */} + {activeSubCategory && ( + setActiveSubCategory(null)} + className="flex items-center gap-2 text-dark-400 hover:text-dark-200 transition-colors mb-4" + > + + {t('common.back')} + + )} + + {/* Settings list */} {filteredSettings.length === 0 ? ( - Нет настроек в этой категории + {t('admin.settings.noSettings')} ) : ( filteredSettings.map(renderSettingRow) @@ -802,101 +844,105 @@ export default function AdminSettings() { // ============ RENDER ============ return ( - - {/* Header */} - - - - - - Настройки - - - {/* Search */} - - setSearchQuery(e.target.value)} - placeholder="Поиск..." - className="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" - /> - - + + {/* Sidebar */} + + {/* Header */} + + + + + + {!sidebarCollapsed && ( + {t('admin.settings.title')} + )} + + {/* Menu */} + + {MENU_SECTIONS.map((section, sectionIdx) => ( + + {sectionIdx > 0 && } + {section.items.map((item) => { + const isActive = activeSection === item.id + const Icon = item.icon + return ( + { + setActiveSection(item.id) + setActiveSubCategory(null) + }} + className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all ${ + isActive + ? 'bg-accent-500/10 text-accent-400' + : 'text-dark-400 hover:text-dark-200 hover:bg-dark-800/50' + }`} + > + {Icon && } + {!sidebarCollapsed && ( + {t(`admin.settings.${item.id}`)} + )} + {item.id === 'favorites' && favorites.length > 0 && !sidebarCollapsed && ( + + {favorites.length} + + )} + + ) + })} + + ))} + + + {/* Collapse toggle */} + setSidebarCollapsed(!sidebarCollapsed)} + className="absolute bottom-4 left-4 p-2 rounded-lg text-dark-500 hover:text-dark-300 hover:bg-dark-800 transition-colors" + > + + + + - {/* Tabs */} - - {TABS.map((tab) => { - if (tab.id === 'more') { - return ( - - setShowMoreDropdown(!showMoreDropdown)} - className={`flex items-center gap-1 px-4 py-2.5 rounded-xl text-sm font-medium transition-all whitespace-nowrap ${ - MORE_TABS.some(t => t.id === activeTab) - ? 'bg-accent-500 text-white' - : 'text-dark-400 hover:text-dark-200 hover:bg-dark-700/50' - }`} - > - {tab.label} - - - - {showMoreDropdown && ( - - {MORE_TABS.map((moreTab) => ( - { - setActiveTab(moreTab.id) - setShowMoreDropdown(false) - }} - className={`w-full px-4 py-2 text-left text-sm transition-colors ${ - activeTab === moreTab.id - ? 'text-accent-400 bg-accent-500/10' - : 'text-dark-300 hover:text-dark-100 hover:bg-dark-700/50' - }`} - > - {moreTab.label} - - ))} - - )} - - ) - } - - return ( - setActiveTab(tab.id)} - className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium transition-all whitespace-nowrap ${ - activeTab === tab.id - ? 'bg-accent-500 text-white' - : 'text-dark-400 hover:text-dark-200 hover:bg-dark-700/50' - }`} - > - {tab.id === 'favorites' && } - {tab.label} - {tab.id === 'favorites' && favorites.length > 0 && ( - - {favorites.length} - + {/* Main content */} + + {/* Search header */} + + + + {t(`admin.settings.${activeSection}`)} + {activeSubCategory && ( + / {t(`admin.settings.categories.${activeSubCategory}`, activeSubCategory)} )} - - ) - })} - + + + + setSearchQuery(e.target.value)} + placeholder={t('admin.settings.searchPlaceholder')} + className="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" + /> + + + + + + - {/* Tab Content */} - - {activeTab === 'favorites' && renderFavoritesTab()} - {activeTab === 'branding' && renderBrandingTab()} - {activeTab === 'theme' && renderThemeTab()} - {['payments', 'subscriptions', 'interface', 'notifications', 'database', 'system', 'users'].includes(activeTab) && renderSettingsTab()} + {/* Content */} + + {activeSection === 'favorites' && renderFavoritesContent()} + {activeSection === 'branding' && renderBrandingContent()} + {activeSection === 'theme' && renderThemeContent()} + {['payments', 'subscriptions', 'interface', 'notifications', 'database', 'system', 'users'].includes(activeSection) && renderSettingsContent()} + )
{desc}
{setting.hint.description}
Волны на фоне приложения
{t('admin.settings.animatedBackgroundDesc')}
В Telegram WebApp
{t('admin.settings.autoFullscreenDesc')}
Нет избранных настроек
- Нажмите ⭐ рядом с настройкой, чтобы добавить её сюда -
{t('admin.settings.favoritesEmpty')}
{t('admin.settings.favoritesHint')}
Нет настроек в этой категории
{t('admin.settings.noSettings')}