refactor: migrate to eslint flat config and format codebase with prettier

- Remove legacy .eslintrc.cjs and .eslintignore
- Add eslint.config.js with flat config, security rules (no-eval, no-implied-eval, no-new-func, no-script-url)
- Add .prettierrc and .prettierignore
- Format entire codebase with prettier
This commit is contained in:
c0mrade
2026-01-27 17:37:31 +03:00
parent 111ccc4e7a
commit bc90ba3779
133 changed files with 19972 additions and 15523 deletions

View File

@@ -1,121 +1,120 @@
import { useState, useMemo, useEffect } from 'react'
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 { useState, useMemo, useEffect } from 'react';
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 { MenuIcon, MENU_SECTIONS, MenuItem, formatSettingKey } from '../components/admin';
import { AnalyticsTab } from '../components/admin/AnalyticsTab';
import { BrandingTab } from '../components/admin/BrandingTab';
import { ThemeTab } from '../components/admin/ThemeTab';
import { FavoritesTab } from '../components/admin/FavoritesTab';
import { SettingsTab } from '../components/admin/SettingsTab';
import { SettingsSidebar } from '../components/admin/SettingsSidebar';
import {
MenuIcon,
MENU_SECTIONS,
MenuItem,
formatSettingKey
} from '../components/admin'
import { AnalyticsTab } from '../components/admin/AnalyticsTab'
import { BrandingTab } from '../components/admin/BrandingTab'
import { ThemeTab } from '../components/admin/ThemeTab'
import { FavoritesTab } from '../components/admin/FavoritesTab'
import { SettingsTab } from '../components/admin/SettingsTab'
import { SettingsSidebar } from '../components/admin/SettingsSidebar'
import { SettingsSearch, SettingsSearchMobile, SettingsSearchResults } from '../components/admin/SettingsSearch'
SettingsSearch,
SettingsSearchMobile,
SettingsSearchResults,
} from '../components/admin/SettingsSearch';
export default function AdminSettings() {
const { t } = useTranslation()
const { t } = useTranslation();
// State
const [activeSection, setActiveSection] = useState('branding')
const [searchQuery, setSearchQuery] = useState('')
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
const [activeSection, setActiveSection] = useState('branding');
const [searchQuery, setSearchQuery] = useState('');
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
// Favorites hook
const { favorites, toggleFavorite, isFavorite } = useFavoriteSettings()
const { favorites, toggleFavorite, isFavorite } = useFavoriteSettings();
// Scroll to top on mount and section change (fix for mobile webviews)
useEffect(() => {
// Use requestAnimationFrame for smoother scroll on mobile
requestAnimationFrame(() => {
window.scrollTo({ top: 0, behavior: 'instant' })
document.documentElement.scrollTop = 0
document.body.scrollTop = 0
})
}, [activeSection])
window.scrollTo({ top: 0, behavior: 'instant' });
document.documentElement.scrollTop = 0;
document.body.scrollTop = 0;
});
}, [activeSection]);
// Queries
const { data: themeColors } = useQuery({
queryKey: ['theme-colors'],
queryFn: themeColorsApi.getColors,
})
});
const { data: allSettings } = useQuery({
queryKey: ['admin-settings'],
queryFn: () => adminSettingsApi.getSettings(),
})
});
// Get current menu item configuration
const currentMenuItem = useMemo(() => {
for (const section of MENU_SECTIONS) {
const item = section.items.find((i: MenuItem) => i.id === activeSection)
if (item) return item
const item = section.items.find((i: MenuItem) => i.id === activeSection);
if (item) return item;
}
return null
}, [activeSection])
return null;
}, [activeSection]);
// Get categories for current section
const currentCategories = useMemo(() => {
if (!currentMenuItem?.categories || !allSettings || !Array.isArray(allSettings)) return []
if (!currentMenuItem?.categories || !allSettings || !Array.isArray(allSettings)) return [];
const categoryMap = new Map<string, SettingDefinition[]>()
const categoryMap = new Map<string, SettingDefinition[]>();
for (const setting of allSettings) {
if (currentMenuItem.categories.includes(setting.category.key)) {
if (!categoryMap.has(setting.category.key)) {
categoryMap.set(setting.category.key, [])
categoryMap.set(setting.category.key, []);
}
categoryMap.get(setting.category.key)!.push(setting)
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])
settings,
}));
}, [currentMenuItem, allSettings, t]);
// Filter settings for search - GLOBAL search across all settings
const filteredSettings = useMemo(() => {
if (!allSettings || !Array.isArray(allSettings) || !searchQuery) return []
if (!allSettings || !Array.isArray(allSettings) || !searchQuery) return [];
const q = searchQuery.toLowerCase().trim()
if (!q) return []
const q = searchQuery.toLowerCase().trim();
if (!q) return [];
return allSettings.filter((s: SettingDefinition) => {
// Search by key
if (s.key.toLowerCase().includes(q)) return true
if (s.key.toLowerCase().includes(q)) return true;
// Search by original name
if (s.name?.toLowerCase().includes(q)) return true
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
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
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
const categoryLabel = t(`admin.settings.categories.${s.category.key}`, s.category.key);
if (categoryLabel.toLowerCase().includes(q)) return true;
return false
})
}, [allSettings, searchQuery, t])
return false;
});
}, [allSettings, searchQuery, t]);
// Favorite settings
const favoriteSettings = useMemo(() => {
if (!allSettings || !Array.isArray(allSettings)) return []
return allSettings.filter((s: SettingDefinition) => favorites.includes(s.key))
}, [allSettings, favorites])
if (!allSettings || !Array.isArray(allSettings)) return [];
return allSettings.filter((s: SettingDefinition) => favorites.includes(s.key));
}, [allSettings, favorites]);
// Render content based on active section
const renderContent = () => {
@@ -129,16 +128,16 @@ export default function AdminSettings() {
isFavorite={isFavorite}
toggleFavorite={toggleFavorite}
/>
)
);
}
switch (activeSection) {
case 'analytics':
return <AnalyticsTab />
return <AnalyticsTab />;
case 'branding':
return <BrandingTab accentColor={themeColors?.accent} />
return <BrandingTab accentColor={themeColors?.accent} />;
case 'theme':
return <ThemeTab />
return <ThemeTab />;
case 'favorites':
return (
<FavoritesTab
@@ -146,9 +145,19 @@ export default function AdminSettings() {
isFavorite={isFavorite}
toggleFavorite={toggleFavorite}
/>
)
);
default:
if (['payments', 'subscriptions', 'interface', 'notifications', 'database', 'system', 'users'].includes(activeSection)) {
if (
[
'payments',
'subscriptions',
'interface',
'notifications',
'database',
'system',
'users',
].includes(activeSection)
) {
return (
<SettingsTab
categories={currentCategories}
@@ -157,18 +166,18 @@ export default function AdminSettings() {
isFavorite={isFavorite}
toggleFavorite={toggleFavorite}
/>
)
);
}
return null
return null;
}
}
};
return (
<div className="flex min-h-screen pt-safe">
<div className="pt-safe flex min-h-screen">
{/* Mobile overlay */}
{mobileMenuOpen && (
<div
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
className="fixed inset-0 z-40 bg-black/50 lg:hidden"
onClick={() => setMobileMenuOpen(false)}
/>
)}
@@ -183,18 +192,18 @@ export default function AdminSettings() {
/>
{/* Main content */}
<main className="flex-1 min-w-0">
<main className="min-w-0 flex-1">
{/* Header */}
<div className="sticky top-0 z-30 bg-dark-900/95 backdrop-blur-xl border-b border-dark-700/50 p-3 sm:p-4">
<div className="sticky top-0 z-30 border-b border-dark-700/50 bg-dark-900/95 p-3 backdrop-blur-xl sm:p-4">
<div className="flex items-center gap-3">
<button
onClick={() => setMobileMenuOpen(true)}
className="p-2 rounded-xl bg-dark-800 hover:bg-dark-700 transition-colors lg:hidden"
className="rounded-xl bg-dark-800 p-2 transition-colors hover:bg-dark-700 lg:hidden"
>
<MenuIcon />
</button>
<h2 className="text-lg sm:text-xl font-semibold text-dark-100 truncate">
<h2 className="truncate text-lg font-semibold text-dark-100 sm:text-xl">
{t(`admin.settings.${activeSection}`)}
</h2>
@@ -207,22 +216,14 @@ export default function AdminSettings() {
/>
</div>
<SettingsSearchMobile
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
/>
<SettingsSearchMobile searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
<SettingsSearchResults
searchQuery={searchQuery}
resultsCount={filteredSettings.length}
/>
<SettingsSearchResults searchQuery={searchQuery} resultsCount={filteredSettings.length} />
</div>
{/* Content */}
<div className="p-3 sm:p-4 lg:p-6">
{renderContent()}
</div>
<div className="p-3 sm:p-4 lg:p-6">{renderContent()}</div>
</main>
</div>
)
);
}