Integrate branding and theme settings with database
- Update BrandingPage to save settings to database instead of localStorage - Add public settings endpoint for galleries to fetch branding/theme - Update GalleryView to apply branding settings in footer - Apply theme settings from database to gallery pages - Support event-specific themes that override global settings - Ensure watermark and all branding settings are stored in database
This commit is contained in:
@@ -1,24 +1,79 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Save, Eye, Palette } from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Button, Card, Input, ErrorBoundary } from '../../components/common';
|
||||
import { Button, Card, Input, ErrorBoundary, Loading } from '../../components/common';
|
||||
import { ThemeCustomizer } from '../../components/admin/ThemeCustomizer';
|
||||
import { useTheme, type ThemeConfig } from '../../contexts/ThemeContext';
|
||||
import { useQuery, useMutation } from '@tanstack/react-query';
|
||||
import { settingsService } from '../../services/settings.service';
|
||||
|
||||
export const BrandingPage: React.FC = () => {
|
||||
const { theme, setTheme, themeName, setThemeByName } = useTheme();
|
||||
const [brandingSettings, setBrandingSettings] = useState({
|
||||
companyName: localStorage.getItem('branding-company-name') || '',
|
||||
companyTagline: localStorage.getItem('branding-company-tagline') || '',
|
||||
footerText: localStorage.getItem('branding-footer-text') || '© 2024 Your Company. All rights reserved.',
|
||||
supportEmail: localStorage.getItem('branding-support-email') || '',
|
||||
watermarkEnabled: localStorage.getItem('branding-watermark-enabled') === 'true',
|
||||
company_name: '',
|
||||
company_tagline: '',
|
||||
footer_text: '© 2024 Your Company. All rights reserved.',
|
||||
support_email: '',
|
||||
watermark_enabled: false,
|
||||
});
|
||||
|
||||
const [currentTheme, setCurrentTheme] = useState<ThemeConfig>(theme);
|
||||
const [currentThemeName, setCurrentThemeName] = useState(themeName);
|
||||
const [isPreviewMode, setIsPreviewMode] = useState(false);
|
||||
|
||||
// Fetch current settings
|
||||
const { data: settings, isLoading } = useQuery({
|
||||
queryKey: ['admin-settings', 'branding'],
|
||||
queryFn: () => settingsService.getSettingsByType('branding'),
|
||||
});
|
||||
|
||||
// Fetch theme settings
|
||||
const { data: themeSettings } = useQuery({
|
||||
queryKey: ['admin-settings', 'theme'],
|
||||
queryFn: () => settingsService.getSettingsByType('theme'),
|
||||
});
|
||||
|
||||
// Update branding mutation
|
||||
const brandingMutation = useMutation({
|
||||
mutationFn: settingsService.updateBranding,
|
||||
onSuccess: () => {
|
||||
toast.success('Branding settings saved successfully!');
|
||||
},
|
||||
onError: () => {
|
||||
toast.error('Failed to save branding settings');
|
||||
},
|
||||
});
|
||||
|
||||
// Update theme mutation
|
||||
const themeMutation = useMutation({
|
||||
mutationFn: settingsService.updateTheme,
|
||||
onSuccess: () => {
|
||||
toast.success('Theme settings saved successfully!');
|
||||
},
|
||||
onError: () => {
|
||||
toast.error('Failed to save theme settings');
|
||||
},
|
||||
});
|
||||
|
||||
// Initialize settings from database
|
||||
useEffect(() => {
|
||||
if (settings) {
|
||||
const formatted = settingsService.formatBrandingSettings(settings);
|
||||
setBrandingSettings(formatted);
|
||||
}
|
||||
}, [settings]);
|
||||
|
||||
// Initialize theme from database
|
||||
useEffect(() => {
|
||||
if (themeSettings) {
|
||||
const formatted = settingsService.formatThemeSettings(themeSettings);
|
||||
if (formatted && Object.keys(formatted).length > 0) {
|
||||
setCurrentTheme(formatted);
|
||||
setTheme(formatted);
|
||||
}
|
||||
}
|
||||
}, [themeSettings]);
|
||||
|
||||
const handleBrandingChange = (key: string, value: any) => {
|
||||
setBrandingSettings(prev => ({ ...prev, [key]: value }));
|
||||
};
|
||||
@@ -37,16 +92,19 @@ export const BrandingPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
// Save branding settings to localStorage
|
||||
Object.entries(brandingSettings).forEach(([key, value]) => {
|
||||
localStorage.setItem(`branding-${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`, String(value));
|
||||
});
|
||||
|
||||
// Apply theme
|
||||
setTheme(currentTheme);
|
||||
|
||||
toast.success('Branding settings saved successfully!');
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
// Save branding settings to database
|
||||
await brandingMutation.mutateAsync(brandingSettings);
|
||||
|
||||
// Save theme settings to database
|
||||
await themeMutation.mutateAsync(currentTheme);
|
||||
|
||||
// Apply theme globally
|
||||
setTheme(currentTheme);
|
||||
} catch (error) {
|
||||
console.error('Failed to save settings:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePreview = () => {
|
||||
@@ -63,6 +121,14 @@ export const BrandingPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<Loading size="lg" text="Loading branding settings..." />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<div>
|
||||
@@ -96,23 +162,23 @@ export const BrandingPage: React.FC = () => {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<Input
|
||||
label="Company Name"
|
||||
value={brandingSettings.companyName}
|
||||
onChange={(e) => handleBrandingChange('companyName', e.target.value)}
|
||||
value={brandingSettings.company_name}
|
||||
onChange={(e) => handleBrandingChange('company_name', e.target.value)}
|
||||
placeholder="Your Photography Studio"
|
||||
helperText="Displayed in email notifications and footers"
|
||||
/>
|
||||
<Input
|
||||
label="Company Tagline"
|
||||
value={brandingSettings.companyTagline}
|
||||
onChange={(e) => handleBrandingChange('companyTagline', e.target.value)}
|
||||
value={brandingSettings.company_tagline}
|
||||
onChange={(e) => handleBrandingChange('company_tagline', e.target.value)}
|
||||
placeholder="Capturing moments that last forever"
|
||||
helperText="Optional tagline for branding"
|
||||
/>
|
||||
<Input
|
||||
label="Support Email"
|
||||
type="email"
|
||||
value={brandingSettings.supportEmail}
|
||||
onChange={(e) => handleBrandingChange('supportEmail', e.target.value)}
|
||||
value={brandingSettings.support_email}
|
||||
onChange={(e) => handleBrandingChange('support_email', e.target.value)}
|
||||
placeholder="support@yourcompany.com"
|
||||
helperText="Contact email for gallery visitors"
|
||||
/>
|
||||
@@ -121,8 +187,8 @@ export const BrandingPage: React.FC = () => {
|
||||
Footer Text
|
||||
</label>
|
||||
<textarea
|
||||
value={brandingSettings.footerText}
|
||||
onChange={(e) => handleBrandingChange('footerText', e.target.value)}
|
||||
value={brandingSettings.footer_text}
|
||||
onChange={(e) => handleBrandingChange('footer_text', e.target.value)}
|
||||
className="w-full px-3 py-2 border border-neutral-300 rounded-lg focus:ring-2 focus:ring-primary-500"
|
||||
rows={2}
|
||||
placeholder="© 2024 Your Company. All rights reserved."
|
||||
@@ -134,8 +200,8 @@ export const BrandingPage: React.FC = () => {
|
||||
<label className="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={brandingSettings.watermarkEnabled}
|
||||
onChange={(e) => handleBrandingChange('watermarkEnabled', e.target.checked)}
|
||||
checked={brandingSettings.watermark_enabled}
|
||||
onChange={(e) => handleBrandingChange('watermark_enabled', e.target.checked)}
|
||||
className="rounded border-neutral-300 text-primary-600 focus:ring-primary-500"
|
||||
/>
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user