+
+
+
Анимированный фон
+
Волны на фоне приложения
-
- Включите нужные темы для пользователей. Минимум одна тема должна быть активна.
-
-
-
- {/* Quick Presets - Collapsible */}
-
-
- {expandedThemeSections.has('presets') && (
-
-
- {THEME_PRESETS.map((preset) => (
-
- ))}
-
-
+ {renderToggle(
+ animationSettings?.enabled ?? true,
+ () => updateAnimationMutation.mutate(!(animationSettings?.enabled ?? true)),
+ updateAnimationMutation.isPending
)}
- {/* Custom Colors - Collapsible */}
-
-
- {expandedThemeSections.has('custom') && (
-
- {/* Accent Color */}
-
-
{t('theme.accent')}
-
updateColorsMutation.mutate({ accent: color })}
- disabled={updateColorsMutation.isPending}
+
+
+
Авто-Fullscreen
+
В Telegram WebApp
+
+ {renderToggle(
+ fullscreenSettings?.enabled ?? false,
+ () => updateFullscreenMutation.mutate(!(fullscreenSettings?.enabled ?? false)),
+ updateFullscreenMutation.isPending
+ )}
+
+
+
+
+ )
+
+ const renderThemeTab = () => (
+
+ {/* Theme toggles */}
+
+
Доступные темы
+
+
+
+
+
+ Тёмная
+
+ {renderToggle(
+ enabledThemes?.dark ?? true,
+ () => {
+ if ((enabledThemes?.dark ?? true) && !(enabledThemes?.light ?? true)) return
+ updateEnabledThemesMutation.mutate({ dark: !(enabledThemes?.dark ?? true) })
+ },
+ updateEnabledThemesMutation.isPending
+ )}
+
+
+
+
+
+ Светлая
+
+ {renderToggle(
+ enabledThemes?.light ?? true,
+ () => {
+ if ((enabledThemes?.light ?? true) && !(enabledThemes?.dark ?? true)) return
+ updateEnabledThemesMutation.mutate({ light: !(enabledThemes?.light ?? true) })
+ },
+ updateEnabledThemesMutation.isPending
+ )}
+
+
+
+
+ {/* Quick Presets */}
+
+
+
+ {expandedSections.has('presets') && (
+
+ {THEME_PRESETS.map((preset) => (
+
+ ))}
-
-
-
- {/* Search */}
-
-
setSearchQuery(e.target.value)}
- placeholder={t('admin.settings.searchCategoriesPlaceholder')}
- className="input w-full pl-10"
- />
-
-
-
- {searchQuery && (
-
setSearchQuery('')}
- className="absolute right-3 top-1/2 -translate-y-1/2 text-dark-500 hover:text-dark-300"
- >
-
-
)}
- {/* Meta-category Cards Grid */}
- {filteredMetaCategories && Object.keys(filteredMetaCategories).length > 0 ? (
-
- {Object.entries(filteredMetaCategories).map(([metaKey, data]) => {
- const meta = META_CATEGORIES[metaKey] || { label: 'Другое', emoji: '📁', categories: [] }
- return (
- setSelectedMeta(metaKey)}
- />
- )
- })}
-
- ) : (
-
-
-
+ {/* Custom Colors */}
+
+
toggleSection('colors')}
+ className="w-full flex items-center justify-between"
+ >
+ Ручная настройка
+
+
-
- {searchQuery ? t('admin.settings.noSearchResults') : t('admin.settings.noSettings')}
+
+
+ {expandedSections.has('colors') && (
+
+ {/* Accent */}
+
+
Акцентный цвет
+ updateColorsMutation.mutate({ accent: color })}
+ disabled={updateColorsMutation.isPending}
+ />
+
+
+ {/* Dark theme */}
+
+
+ Тёмная тема
+
+
+ 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}
+ />
+
+
+
+ {/* Light theme */}
+
+
+ Светлая тема
+
+
+ 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}
+ />
+
+
+
+ {/* Status colors */}
+
+
Статусные цвета
+
+ updateColorsMutation.mutate({ success: color })}
+ disabled={updateColorsMutation.isPending}
+ />
+ updateColorsMutation.mutate({ warning: color })}
+ disabled={updateColorsMutation.isPending}
+ />
+ updateColorsMutation.mutate({ error: color })}
+ disabled={updateColorsMutation.isPending}
+ />
+
+
+
+ {/* Reset button */}
+
resetColorsMutation.mutate()}
+ 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"
+ >
+ Сбросить все цвета
+
+
+ )}
+
+
+ )
+
+ const renderFavoritesTab = () => (
+
+ {favoriteSettings.length === 0 ? (
+
+
+
Нет избранных настроек
+
+ Нажмите ⭐ рядом с настройкой, чтобы добавить её сюда
- )}
-
- {/* Settings Modal */}
- {selectedMeta && metaCategoriesData && metaCategoriesData[selectedMeta] && (
-
setSelectedMeta(null)}
- onUpdate={handleUpdate}
- onReset={handleReset}
- isUpdating={updateMutation.isPending || resetMutation.isPending}
- />
+ ) : (
+ favoriteSettings.map(renderSettingRow)
)}
)
+
+ const renderSettingsTab = () => (
+
+ {filteredSettings.length === 0 ? (
+
+
Нет настроек в этой категории
+
+ ) : (
+ filteredSettings.map(renderSettingRow)
+ )}
+
+ )
+
+ // ============ 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"
+ />
+
+
+
+
+
+
+ {/* 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}
+
+ )}
+
+ )
+ })}
+
+
+ {/* Tab Content */}
+
+ {activeTab === 'favorites' && renderFavoritesTab()}
+ {activeTab === 'branding' && renderBrandingTab()}
+ {activeTab === 'theme' && renderThemeTab()}
+ {['payments', 'subscriptions', 'interface', 'notifications', 'database', 'system', 'users'].includes(activeTab) && renderSettingsTab()}
+
+
+ )
+}
+
+// ============ SETTING INPUT COMPONENT ============
+function SettingInput({
+ setting,
+ onUpdate,
+ disabled
+}: {
+ setting: SettingDefinition
+ onUpdate: (value: string) => void
+ disabled?: boolean
+}) {
+ const [isEditing, setIsEditing] = useState(false)
+ const [value, setValue] = useState('')
+
+ const handleStart = () => {
+ setValue(String(setting.current ?? ''))
+ setIsEditing(true)
+ }
+
+ const handleSave = () => {
+ onUpdate(value)
+ setIsEditing(false)
+ }
+
+ const handleCancel = () => {
+ setIsEditing(false)
+ setValue('')
+ }
+
+ if (setting.choices && setting.choices.length > 0) {
+ return (
+
+ )
+ }
+
+ if (isEditing) {
+ return (
+
+ setValue(e.target.value)}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') handleSave()
+ if (e.key === 'Escape') handleCancel()
+ }}
+ autoFocus
+ className="bg-dark-700 border border-accent-500 rounded-lg px-3 py-1.5 text-sm text-dark-100 focus:outline-none w-32"
+ />
+
+
+
+
+
+
+
+ )
+ }
+
+ return (
+
+ {String(setting.current ?? '-')}
+
+ )
}