Files
picpeak/frontend/src/pages/admin/SettingsPage.tsx
T
Paul Nothaft 0da45e699a feat: Add CSS template system with custom gallery styling support
## Changes

### CSS Template System
- Added CSS class hooks to gallery components for custom template targeting
- Gallery sidebar, header, footer, and photo cards can now be styled via CSS templates
- CSS variables on :root allow themes to override colors, effects, and spacing

### Gallery Component CSS Classes Added
- `.gallery-page` - Main gallery container
- `.gallery-header` - Top header bar
- `.gallery-sidebar` - Filter/download sidebar
- `.gallery-sidebar-header`, `.gallery-sidebar-title`, `.gallery-sidebar-close`
- `.gallery-sidebar-content`, `.gallery-sidebar-section`
- `.gallery-sidebar-search-input`, `.gallery-sidebar-search-icon`
- `.gallery-sidebar-backdrop` - Mobile overlay
- `.gallery-btn`, `.gallery-btn-download` - Sidebar buttons
- `.gallery-footer` - Footer section
- `.photo-card`, `.photo-grid` - Photo display elements

### CSS Templates (Database)
- Elegant Dark (id=1): Dark navy theme with light text and red accents
- Liquid Glass Light (id=2): iOS 26 frosted glass effect with gradient background

### Bug Fixes
- Fixed CSS variables not inheriting (moved from .gallery-page to :root)
- Fixed sidebar position breaking layout (removed position: relative override)
- Fixed Elegant Dark sidebar text visibility (white on white issue)

### Other Changes
- Settings page refactoring and cleanup
- i18n locale updates for new gallery features
- Vite proxy port configuration fix
- Admin auth route improvements
- CSS templates service updates
2026-01-03 08:59:01 +01:00

170 lines
5.2 KiB
TypeScript

import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Loading } from '../../components/common';
import {
useSettingsState,
GeneralTab,
EventsTab,
StatusTab,
SecurityTab,
CategoriesTab,
AnalyticsTab,
ModerationTab,
StylingTab,
} from '../../features/settings';
type TabType = 'general' | 'events' | 'status' | 'security' | 'categories' | 'analytics' | 'moderation' | 'styling';
export const SettingsPage: React.FC = () => {
const [activeTab, setActiveTab] = useState<TabType>('general');
const { t } = useTranslation();
const {
isLoading,
adminProfileLoading,
generalSettings,
setGeneralSettings,
securitySettings,
setSecuritySettings,
analyticsSettings,
setAnalyticsSettings,
eventSettings,
setEventSettings,
accountForm,
accountErrors,
handleAccountChange,
handleAccountSubmit,
updateAdminProfileMutation,
softLimitGb,
setSoftLimitGb,
softLimitDirty,
setSoftLimitDirty,
capacityOverrideGb,
setCapacityOverrideGb,
availableOverrideGb,
setAvailableOverrideGb,
overrideDirty,
setOverrideDirty,
handleSaveSoftLimit,
handleSaveCapacityOverride,
saveSoftLimitMutation,
saveCapacityOverrideMutation,
saveGeneralMutation,
saveSecurityMutation,
saveAnalyticsMutation,
saveEventSettingsMutation,
} = useSettingsState();
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<Loading size="lg" text={t('settings.loadingSettings')} />
</div>
);
}
const tabs: { key: TabType; label: string }[] = [
{ key: 'general', label: t('settings.general.title') },
{ key: 'events', label: t('settings.events.title', 'Event Creation') },
{ key: 'status', label: t('settings.systemStatus.title') },
{ key: 'security', label: t('settings.security.title') },
{ key: 'categories', label: t('settings.categories.title') },
{ key: 'analytics', label: t('settings.analytics.title') },
{ key: 'moderation', label: t('settings.moderation.title', 'Moderation') },
{ key: 'styling', label: t('settings.styling.title', 'Custom CSS') },
];
return (
<div>
<div className="mb-6">
<h1 className="text-2xl font-bold text-neutral-900">{t('settings.title')}</h1>
<p className="text-neutral-600 mt-1">{t('settings.subtitle')}</p>
</div>
{/* Tab Navigation */}
<div className="border-b border-neutral-200 mb-6">
<nav className="-mb-px flex gap-6">
{tabs.map((tab) => (
<button
key={tab.key}
onClick={() => setActiveTab(tab.key)}
className={`py-2 px-1 border-b-2 font-medium text-sm transition-colors ${
activeTab === tab.key
? 'border-primary-600 text-primary-600'
: 'border-transparent text-neutral-500 hover:text-neutral-700'
}`}
>
{tab.label}
</button>
))}
</nav>
</div>
{/* Tab Content */}
{activeTab === 'general' && (
<GeneralTab
generalSettings={generalSettings}
setGeneralSettings={setGeneralSettings}
saveGeneralMutation={saveGeneralMutation}
accountForm={accountForm}
accountErrors={accountErrors}
handleAccountChange={handleAccountChange}
handleAccountSubmit={handleAccountSubmit}
updateAdminProfileMutation={updateAdminProfileMutation}
adminProfileLoading={adminProfileLoading}
/>
)}
{activeTab === 'events' && (
<EventsTab
eventSettings={eventSettings}
setEventSettings={setEventSettings}
saveEventSettingsMutation={saveEventSettingsMutation}
/>
)}
{activeTab === 'status' && (
<StatusTab
isActive={activeTab === 'status'}
handleSaveSoftLimit={handleSaveSoftLimit}
handleSaveCapacityOverride={handleSaveCapacityOverride}
saveSoftLimitMutation={saveSoftLimitMutation}
saveCapacityOverrideMutation={saveCapacityOverrideMutation}
softLimitGb={softLimitGb}
setSoftLimitGb={setSoftLimitGb}
softLimitDirty={softLimitDirty}
setSoftLimitDirty={setSoftLimitDirty}
capacityOverrideGb={capacityOverrideGb}
setCapacityOverrideGb={setCapacityOverrideGb}
availableOverrideGb={availableOverrideGb}
setAvailableOverrideGb={setAvailableOverrideGb}
overrideDirty={overrideDirty}
setOverrideDirty={setOverrideDirty}
/>
)}
{activeTab === 'security' && (
<SecurityTab
securitySettings={securitySettings}
setSecuritySettings={setSecuritySettings}
saveSecurityMutation={saveSecurityMutation}
/>
)}
{activeTab === 'categories' && <CategoriesTab />}
{activeTab === 'analytics' && (
<AnalyticsTab
analyticsSettings={analyticsSettings}
setAnalyticsSettings={setAnalyticsSettings}
saveAnalyticsMutation={saveAnalyticsMutation}
/>
)}
{activeTab === 'moderation' && <ModerationTab />}
{activeTab === 'styling' && <StylingTab />}
</div>
);
};