mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 10:27:49 +00:00
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:
@@ -1,101 +1,107 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { themeColorsApi } from '../../api/themeColors'
|
||||
import { DEFAULT_THEME_COLORS } from '../../types/theme'
|
||||
import { ColorPicker } from '../ColorPicker'
|
||||
import { applyThemeColors } from '../../hooks/useThemeColors'
|
||||
import { updateEnabledThemesCache } from '../../hooks/useTheme'
|
||||
import { MoonIcon, SunIcon, ChevronDownIcon } from './icons'
|
||||
import { Toggle } from './Toggle'
|
||||
import { THEME_PRESETS } from './constants'
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { themeColorsApi } from '../../api/themeColors';
|
||||
import { DEFAULT_THEME_COLORS } from '../../types/theme';
|
||||
import { ColorPicker } from '../ColorPicker';
|
||||
import { applyThemeColors } from '../../hooks/useThemeColors';
|
||||
import { updateEnabledThemesCache } from '../../hooks/useTheme';
|
||||
import { MoonIcon, SunIcon, ChevronDownIcon } from './icons';
|
||||
import { Toggle } from './Toggle';
|
||||
import { THEME_PRESETS } from './constants';
|
||||
|
||||
export function ThemeTab() {
|
||||
const { t } = useTranslation()
|
||||
const queryClient = useQueryClient()
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [expandedSections, setExpandedSections] = useState<Set<string>>(new Set(['presets']))
|
||||
const [expandedSections, setExpandedSections] = useState<Set<string>>(new Set(['presets']));
|
||||
|
||||
const toggleSection = (section: string) => {
|
||||
setExpandedSections(prev => {
|
||||
const next = new Set(prev)
|
||||
setExpandedSections((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(section)) {
|
||||
next.delete(section)
|
||||
next.delete(section);
|
||||
} else {
|
||||
next.add(section)
|
||||
next.add(section);
|
||||
}
|
||||
return next
|
||||
})
|
||||
}
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
// Queries
|
||||
const { data: themeColors } = useQuery({
|
||||
queryKey: ['theme-colors'],
|
||||
queryFn: themeColorsApi.getColors,
|
||||
})
|
||||
});
|
||||
|
||||
const { data: enabledThemes } = useQuery({
|
||||
queryKey: ['enabled-themes'],
|
||||
queryFn: themeColorsApi.getEnabledThemes,
|
||||
})
|
||||
});
|
||||
|
||||
// Mutations
|
||||
const updateColorsMutation = useMutation({
|
||||
mutationFn: themeColorsApi.updateColors,
|
||||
onSuccess: (data) => {
|
||||
applyThemeColors(data)
|
||||
queryClient.invalidateQueries({ queryKey: ['theme-colors'] })
|
||||
applyThemeColors(data);
|
||||
queryClient.invalidateQueries({ queryKey: ['theme-colors'] });
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
const resetColorsMutation = useMutation({
|
||||
mutationFn: themeColorsApi.resetColors,
|
||||
onSuccess: (data) => {
|
||||
applyThemeColors(data)
|
||||
queryClient.invalidateQueries({ queryKey: ['theme-colors'] })
|
||||
applyThemeColors(data);
|
||||
queryClient.invalidateQueries({ queryKey: ['theme-colors'] });
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
const updateEnabledThemesMutation = useMutation({
|
||||
mutationFn: themeColorsApi.updateEnabledThemes,
|
||||
onSuccess: (data) => {
|
||||
updateEnabledThemesCache(data)
|
||||
queryClient.invalidateQueries({ queryKey: ['enabled-themes'] })
|
||||
updateEnabledThemesCache(data);
|
||||
queryClient.invalidateQueries({ queryKey: ['enabled-themes'] });
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Theme toggles */}
|
||||
<div className="p-6 rounded-2xl bg-dark-800/50 border border-dark-700/50">
|
||||
<h3 className="text-lg font-semibold text-dark-100 mb-4">{t('admin.settings.availableThemes')}</h3>
|
||||
<div className="rounded-2xl border border-dark-700/50 bg-dark-800/50 p-6">
|
||||
<h3 className="mb-4 text-lg font-semibold text-dark-100">
|
||||
{t('admin.settings.availableThemes')}
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
|
||||
<div className="flex items-center justify-between p-3 sm:p-4 rounded-xl bg-dark-700/30">
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-4">
|
||||
<div className="flex items-center justify-between rounded-xl bg-dark-700/30 p-3 sm:p-4">
|
||||
<div className="flex items-center gap-2 sm:gap-3">
|
||||
<MoonIcon />
|
||||
<span className="font-medium text-dark-200 text-sm sm:text-base">{t('admin.settings.darkTheme')}</span>
|
||||
<span className="text-sm font-medium text-dark-200 sm:text-base">
|
||||
{t('admin.settings.darkTheme')}
|
||||
</span>
|
||||
</div>
|
||||
<Toggle
|
||||
checked={enabledThemes?.dark ?? true}
|
||||
onChange={() => {
|
||||
if ((enabledThemes?.dark ?? true) && !(enabledThemes?.light ?? true)) return
|
||||
updateEnabledThemesMutation.mutate({ dark: !(enabledThemes?.dark ?? true) })
|
||||
if ((enabledThemes?.dark ?? true) && !(enabledThemes?.light ?? true)) return;
|
||||
updateEnabledThemesMutation.mutate({ dark: !(enabledThemes?.dark ?? true) });
|
||||
}}
|
||||
disabled={updateEnabledThemesMutation.isPending}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-3 sm:p-4 rounded-xl bg-dark-700/30">
|
||||
<div className="flex items-center justify-between rounded-xl bg-dark-700/30 p-3 sm:p-4">
|
||||
<div className="flex items-center gap-2 sm:gap-3">
|
||||
<SunIcon />
|
||||
<span className="font-medium text-dark-200 text-sm sm:text-base">{t('admin.settings.lightTheme')}</span>
|
||||
<span className="text-sm font-medium text-dark-200 sm:text-base">
|
||||
{t('admin.settings.lightTheme')}
|
||||
</span>
|
||||
</div>
|
||||
<Toggle
|
||||
checked={enabledThemes?.light ?? true}
|
||||
onChange={() => {
|
||||
if ((enabledThemes?.light ?? true) && !(enabledThemes?.dark ?? true)) return
|
||||
updateEnabledThemesMutation.mutate({ light: !(enabledThemes?.light ?? true) })
|
||||
if ((enabledThemes?.light ?? true) && !(enabledThemes?.dark ?? true)) return;
|
||||
updateEnabledThemesMutation.mutate({ light: !(enabledThemes?.light ?? true) });
|
||||
}}
|
||||
disabled={updateEnabledThemesMutation.isPending}
|
||||
/>
|
||||
@@ -104,30 +110,34 @@ export function ThemeTab() {
|
||||
</div>
|
||||
|
||||
{/* Quick Presets */}
|
||||
<div className="p-6 rounded-2xl bg-dark-800/50 border border-dark-700/50">
|
||||
<div className="rounded-2xl border border-dark-700/50 bg-dark-800/50 p-6">
|
||||
<button
|
||||
onClick={() => toggleSection('presets')}
|
||||
className="w-full flex items-center justify-between"
|
||||
className="flex w-full items-center justify-between"
|
||||
>
|
||||
<h3 className="text-lg font-semibold text-dark-100">{t('admin.settings.quickPresets')}</h3>
|
||||
<div className={`transition-transform ${expandedSections.has('presets') ? 'rotate-180' : ''}`}>
|
||||
<h3 className="text-lg font-semibold text-dark-100">
|
||||
{t('admin.settings.quickPresets')}
|
||||
</h3>
|
||||
<div
|
||||
className={`transition-transform ${expandedSections.has('presets') ? 'rotate-180' : ''}`}
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{expandedSections.has('presets') && (
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3 mt-4">
|
||||
<div className="mt-4 grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||
{THEME_PRESETS.map((preset) => (
|
||||
<button
|
||||
key={preset.id}
|
||||
onClick={() => 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]"
|
||||
className="rounded-xl border border-dark-600 p-3 transition-all hover:scale-[1.02] hover:border-dark-500"
|
||||
style={{ backgroundColor: preset.colors.darkBackground }}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 rounded-full ring-2 ring-white/20"
|
||||
className="h-4 w-4 rounded-full ring-2 ring-white/20"
|
||||
style={{ backgroundColor: preset.colors.accent }}
|
||||
/>
|
||||
<span className="text-xs font-medium" style={{ color: preset.colors.darkText }}>
|
||||
@@ -135,9 +145,18 @@ export function ThemeTab() {
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<div className="w-3 h-3 rounded" style={{ backgroundColor: preset.colors.success }} />
|
||||
<div className="w-3 h-3 rounded" style={{ backgroundColor: preset.colors.warning }} />
|
||||
<div className="w-3 h-3 rounded" style={{ backgroundColor: preset.colors.error }} />
|
||||
<div
|
||||
className="h-3 w-3 rounded"
|
||||
style={{ backgroundColor: preset.colors.success }}
|
||||
/>
|
||||
<div
|
||||
className="h-3 w-3 rounded"
|
||||
style={{ backgroundColor: preset.colors.warning }}
|
||||
/>
|
||||
<div
|
||||
className="h-3 w-3 rounded"
|
||||
style={{ backgroundColor: preset.colors.error }}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
@@ -146,13 +165,17 @@ export function ThemeTab() {
|
||||
</div>
|
||||
|
||||
{/* Custom Colors */}
|
||||
<div className="p-6 rounded-2xl bg-dark-800/50 border border-dark-700/50">
|
||||
<div className="rounded-2xl border border-dark-700/50 bg-dark-800/50 p-6">
|
||||
<button
|
||||
onClick={() => toggleSection('colors')}
|
||||
className="w-full flex items-center justify-between"
|
||||
className="flex w-full items-center justify-between"
|
||||
>
|
||||
<h3 className="text-lg font-semibold text-dark-100">{t('admin.settings.customColors')}</h3>
|
||||
<div className={`transition-transform ${expandedSections.has('colors') ? 'rotate-180' : ''}`}>
|
||||
<h3 className="text-lg font-semibold text-dark-100">
|
||||
{t('admin.settings.customColors')}
|
||||
</h3>
|
||||
<div
|
||||
className={`transition-transform ${expandedSections.has('colors') ? 'rotate-180' : ''}`}
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
</div>
|
||||
</button>
|
||||
@@ -161,7 +184,9 @@ export function ThemeTab() {
|
||||
<div className="mt-4 space-y-6">
|
||||
{/* Accent */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-dark-300 mb-3">{t('admin.settings.accentColor')}</h4>
|
||||
<h4 className="mb-3 text-sm font-medium text-dark-300">
|
||||
{t('admin.settings.accentColor')}
|
||||
</h4>
|
||||
<ColorPicker
|
||||
label={t('theme.accent')}
|
||||
value={themeColors?.accent || DEFAULT_THEME_COLORS.accent}
|
||||
@@ -172,10 +197,10 @@ export function ThemeTab() {
|
||||
|
||||
{/* Dark theme */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-dark-300 mb-3 flex items-center gap-2">
|
||||
<h4 className="mb-3 flex items-center gap-2 text-sm font-medium text-dark-300">
|
||||
<MoonIcon /> {t('admin.settings.darkTheme')}
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<ColorPicker
|
||||
label={t('admin.settings.colors.background')}
|
||||
value={themeColors?.darkBackground || DEFAULT_THEME_COLORS.darkBackground}
|
||||
@@ -205,10 +230,10 @@ export function ThemeTab() {
|
||||
|
||||
{/* Light theme */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-dark-300 mb-3 flex items-center gap-2">
|
||||
<h4 className="mb-3 flex items-center gap-2 text-sm font-medium text-dark-300">
|
||||
<SunIcon /> {t('admin.settings.lightTheme')}
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<ColorPicker
|
||||
label={t('admin.settings.colors.background')}
|
||||
value={themeColors?.lightBackground || DEFAULT_THEME_COLORS.lightBackground}
|
||||
@@ -238,8 +263,10 @@ export function ThemeTab() {
|
||||
|
||||
{/* Status colors */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-dark-300 mb-3">{t('admin.settings.statusColors')}</h4>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
<h4 className="mb-3 text-sm font-medium text-dark-300">
|
||||
{t('admin.settings.statusColors')}
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
||||
<ColorPicker
|
||||
label={t('admin.settings.colors.success')}
|
||||
value={themeColors?.success || DEFAULT_THEME_COLORS.success}
|
||||
@@ -265,7 +292,7 @@ export function ThemeTab() {
|
||||
<button
|
||||
onClick={() => 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"
|
||||
className="rounded-xl bg-dark-700 px-4 py-2 text-dark-300 transition-colors hover:bg-dark-600 disabled:opacity-50"
|
||||
>
|
||||
{t('admin.settings.resetAllColors')}
|
||||
</button>
|
||||
@@ -273,5 +300,5 @@ export function ThemeTab() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user