From dcac738aebea2c8b5215a24958797bae441ea357 Mon Sep 17 00:00:00 2001 From: PEDZEO Date: Wed, 21 Jan 2026 03:48:44 +0300 Subject: [PATCH] Add subscription translations and collapsible categories in AdminSettings - Add 65+ new translations for subscription, trial, traffic, autopay settings - Replace flat navigation with accordion-style collapsible subcategories - Categories are collapsed by default, click to expand - Search shows flat list when query is present - Remove unused ChevronRightIcon and activeSubCategory state --- src/locales/ru.json | 69 ++++++++++++++++- src/pages/AdminSettings.tsx | 143 ++++++++++++++++++------------------ 2 files changed, 139 insertions(+), 73 deletions(-) diff --git a/src/locales/ru.json b/src/locales/ru.json index 7568b7e..866e250 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -925,7 +925,74 @@ "Channel Id": "ID канала", "Channel Url": "URL канала", "Timezone": "Часовой пояс", - "Locale Default": "Язык" + "Locale Default": "Язык", + "Simple Subscription Device Limit": "Лимит устройств", + "Simple Subscription Enabled": "Быстрая покупка", + "Simple Subscription Period Days": "Период (дни)", + "Simple Subscription Squad Uuid": "UUID сквада", + "Simple Subscription Traffic Gb": "Трафик (ГБ)", + "Trial Activation Price": "Цена активации", + "Trial Add Remaining Days To Paid": "Добавить дни к платной", + "Trial Device Limit": "Лимит устройств", + "Trial Duration Days": "Длительность (дни)", + "Trial Payment Enabled": "Платный триал", + "Trial Tariff Id": "ID тарифа", + "Trial Traffic Limit Gb": "Лимит трафика (ГБ)", + "Trial User Tag": "Тег пользователя", + "Trial Warning Hours": "Предупреждение (час)", + "Paid Subscription User Tag": "Тег платной подписки", + "Price 14 Days": "Цена 14 дней", + "Price 180 Days": "Цена 180 дней", + "Price 30 Days": "Цена 30 дней", + "Price 360 Days": "Цена 360 дней", + "Price 60 Days": "Цена 60 дней", + "Price 90 Days": "Цена 90 дней", + "Base Promo Group Period Discounts": "Скидки по периодам", + "Base Promo Group Period Discounts Enabled": "Скидки по периодам", + "Base Subscription Price": "Базовая цена", + "Default Device Limit": "Лимит устройств", + "Default Traffic Limit Gb": "Лимит трафика (ГБ)", + "Devices Selection Disabled Amount": "Устройств без выбора", + "Devices Selection Enabled": "Выбор устройств", + "Max Devices Limit": "Макс. устройств", + "Price Per Device": "Цена за устройство", + "Sales Mode": "Режим продаж", + "Available Renewal Periods": "Периоды продления", + "Available Subscription Periods": "Периоды подписки", + "Default Traffic Reset Strategy": "Стратегия сброса", + "Fixed Traffic Limit Gb": "Фикс. лимит (ГБ)", + "Reset Traffic On Payment": "Сброс при оплате", + "Traffic Check Batch Size": "Размер пакета", + "Traffic Check Concurrency": "Параллелизм", + "Traffic Daily Check Enabled": "Ежедневная проверка", + "Traffic Daily Check Time": "Время проверки", + "Traffic Daily Threshold Gb": "Порог в день (ГБ)", + "Traffic Excluded User Uuids": "Исключённые UUID", + "Traffic Fast Check Enabled": "Быстрая проверка", + "Traffic Fast Check Interval Minutes": "Интервал (мин)", + "Traffic Fast Check Threshold Gb": "Порог (ГБ)", + "Traffic Ignored Nodes": "Игнор. ноды", + "Traffic Notification Cooldown Minutes": "Кулдаун уведомлений", + "Traffic Reset Base Price": "Цена сброса", + "Traffic Reset Price Mode": "Режим цены сброса", + "Traffic Selection Mode": "Режим выбора", + "Traffic Threshold Gb Per Day": "Порог в день (ГБ)", + "Traffic Topup Enabled": "Докупка трафика", + "Traffic Topup Packages Config": "Конфиг докупки", + "Price Traffic 1000gb": "Цена 1000 ГБ", + "Price Traffic 100gb": "Цена 100 ГБ", + "Price Traffic 10gb": "Цена 10 ГБ", + "Price Traffic 250gb": "Цена 250 ГБ", + "Price Traffic 25gb": "Цена 25 ГБ", + "Price Traffic 500gb": "Цена 500 ГБ", + "Price Traffic 50gb": "Цена 50 ГБ", + "Price Traffic 5gb": "Цена 5 ГБ", + "Price Traffic Unlimited": "Цена безлимита", + "Traffic Packages Config": "Конфиг пакетов", + "Autopay Warning Days": "Предупреждение (дни)", + "Default Autopay Days Before": "Дней до автоплатежа", + "Default Autopay Enabled": "Автоплатёж по умолчанию", + "Min Balance For Autopay Kopeks": "Мин. баланс (коп.)" }, "categories": { "PAYMENT": "Платежи", diff --git a/src/pages/AdminSettings.tsx b/src/pages/AdminSettings.tsx index fe8dd20..096beb2 100644 --- a/src/pages/AdminSettings.tsx +++ b/src/pages/AdminSettings.tsx @@ -38,12 +38,6 @@ const ChevronDownIcon = () => ( ) -const ChevronRightIcon = () => ( - - - -) - const UploadIcon = () => ( @@ -196,7 +190,6 @@ export default function AdminSettings() { // State const [activeSection, setActiveSection] = useState('branding') - const [activeSubCategory, setActiveSubCategory] = useState(null) const [searchQuery, setSearchQuery] = useState('') const [editingName, setEditingName] = useState(false) const [newName, setNewName] = useState('') @@ -368,30 +361,20 @@ export default function AdminSettings() { })) }, [currentMenuItem, allSettings, t]) - // Filter settings for current view + // Filter settings for search const filteredSettings = useMemo(() => { - if (!allSettings || !Array.isArray(allSettings)) return [] + if (!allSettings || !Array.isArray(allSettings) || !searchQuery) return [] - let settings: SettingDefinition[] = [] + const q = searchQuery.toLowerCase() + let settings = allSettings.filter((s: SettingDefinition) => + currentMenuItem?.categories?.includes(s.category.key) + ) - 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) - ) - } - - 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]) + return settings.filter((s: SettingDefinition) => + s.key.toLowerCase().includes(q) || + s.name?.toLowerCase().includes(q) + ) + }, [allSettings, currentMenuItem, searchQuery]) // Favorite settings const favoriteSettings = useMemo(() => { @@ -856,50 +839,70 @@ export default function AdminSettings() { ) - const renderSettingsContent = () => ( -
- {/* Sub-categories navigation */} - {currentCategories.length > 1 && !activeSubCategory && ( -
- {currentCategories.map((cat) => ( - - ))} -
- )} + {/* Accordion header */} + - {/* Back button if in sub-category */} - {activeSubCategory && ( - - )} + {/* Accordion content */} + {isExpanded && ( +
+
+ {cat.settings.map(renderSettingRow)} +
+
+ )} +
+ ) + })} - {/* Settings grid/list */} - {filteredSettings.length === 0 ? ( -
-

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

-
- ) : ( -
- {filteredSettings.map(renderSettingRow)} -
- )} - - ) + {currentCategories.length === 0 && ( +
+

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

+
+ )} + + ) + } // ============ RENDER ============ return ( @@ -949,7 +952,6 @@ export default function AdminSettings() { key={item.id} onClick={() => { setActiveSection(item.id) - setActiveSubCategory(null) setMobileMenuOpen(false) }} className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all ${ @@ -988,9 +990,6 @@ export default function AdminSettings() {

{t(`admin.settings.${activeSection}`)} - {activeSubCategory && ( - / {t(`admin.settings.categories.${activeSubCategory}`, activeSubCategory)} - )}