import React, { useState, useEffect, useRef } from 'react'; import { Save, Eye, Palette, Upload } from 'lucide-react'; import { toast } from 'react-toastify'; import { Button, Card, Input, ErrorBoundary, Loading } from '../../components/common'; import { ThemeCustomizerEnhanced, GalleryPreview } from '../../components/admin'; import { useTheme, type ThemeConfig, GALLERY_THEME_PRESETS } from '../../contexts/ThemeContext'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { settingsService, type BrandingSettings } from '../../services/settings.service'; import { useTranslation } from 'react-i18next'; import { buildResourceUrl } from '../../utils/url'; export const BrandingPage: React.FC = () => { const { t } = useTranslation(); const { theme, setTheme } = useTheme(); const [brandingSettings, setBrandingSettings] = useState({ company_name: '', company_tagline: '', footer_text: '© 2024 Your Company. All rights reserved.', support_email: '', watermark_enabled: false, watermark_position: 'bottom-right', watermark_opacity: 50, watermark_size: 15, watermark_logo_url: '', favicon_url: '', }); const [currentTheme, setCurrentTheme] = useState(theme); const [currentThemeName, setCurrentThemeName] = useState('default'); const [isPreviewMode, setIsPreviewMode] = useState(false); const faviconInputRef = useRef(null); // 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 queryClient = useQueryClient(); const brandingMutation = useMutation({ mutationFn: settingsService.updateBranding, onSuccess: () => { toast.success(t('toast.brandingUpdated')); // Invalidate all settings queries to refresh data queryClient.invalidateQueries({ queryKey: ['admin-settings'] }); queryClient.invalidateQueries({ queryKey: ['public-settings'] }); }, onError: () => { toast.error(t('toast.saveError')); }, }); // Update theme mutation const themeMutation = useMutation({ mutationFn: settingsService.updateTheme, onSuccess: () => { toast.success(t('toast.themeUpdated')); }, onError: () => { toast.error(t('toast.saveError')); }, }); // Initialize settings from database useEffect(() => { if (settings) { const formatted = settingsService.formatBrandingSettings(settings); // Don't set logo_url here - it will be synced from theme const { logo_url, ...brandingWithoutLogo } = formatted; setBrandingSettings(prev => ({ ...prev, ...brandingWithoutLogo })); } }, [settings]); // Initialize theme from database useEffect(() => { if (themeSettings) { const formatted = settingsService.formatThemeSettings(themeSettings) as ThemeConfig; if (formatted && Object.keys(formatted).length > 0) { // Use the theme's logo URL as stored in the theme config setCurrentTheme(formatted); setTheme(formatted); // Always sync the logo URL from theme to branding settings - theme is source of truth setBrandingSettings(prev => ({ ...prev, logo_url: formatted.logoUrl || '' })); // Try to identify which preset this matches for (const [key, preset] of Object.entries(GALLERY_THEME_PRESETS)) { if (JSON.stringify(preset.config) === JSON.stringify(formatted)) { setCurrentThemeName(key); break; } } } } }, [themeSettings, setTheme]); const handleBrandingChange = (key: string, value: any) => { setBrandingSettings(prev => ({ ...prev, [key]: value })); }; const handleThemeChange = (newTheme: ThemeConfig) => { setCurrentTheme(newTheme); // Also update logo URL in branding settings if it changed if (newTheme.logoUrl !== currentTheme.logoUrl) { setBrandingSettings(prev => ({ ...prev, logo_url: newTheme.logoUrl || '' })); } if (isPreviewMode) { setTheme(newTheme); } }; const handlePresetChange = (presetName: string) => { setCurrentThemeName(presetName); // Get the preset theme config const preset = GALLERY_THEME_PRESETS[presetName]; if (preset) { setCurrentTheme(preset.config); if (isPreviewMode) { setTheme(preset.config); } } }; const handleFaviconUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { try { const faviconUrl = await settingsService.uploadFavicon(file); setBrandingSettings(prev => ({ ...prev, favicon_url: faviconUrl })); toast.success(t('toast.uploadSuccess')); } catch (error) { console.error('Failed to upload favicon:', error); toast.error(t('toast.uploadError')); } } }; const handleWatermarkLogoUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { try { const watermarkLogoUrl = await settingsService.uploadWatermarkLogo(file); setBrandingSettings(prev => ({ ...prev, watermark_logo_url: watermarkLogoUrl })); toast.success(t('toast.uploadSuccess')); } catch (error) { console.error('Failed to upload watermark logo:', error); toast.error(t('toast.uploadError')); } } }; const handleSave = async () => { try { // Sync logo URL from theme to branding settings const updatedBrandingSettings = { ...brandingSettings, logo_url: currentTheme.logoUrl || '' }; // Save branding settings to database await brandingMutation.mutateAsync(updatedBrandingSettings); // Save theme settings to database await themeMutation.mutateAsync(currentTheme); // Apply theme globally setTheme(currentTheme); // Update local state to reflect saved values setBrandingSettings(updatedBrandingSettings); } catch (error) { console.error('Failed to save settings:', error); } }; const handlePreview = () => { const previewWindow = window.open('/gallery/preview', '_blank'); if (previewWindow) { // Send theme data to preview window setTimeout(() => { previewWindow.postMessage({ type: 'THEME_PREVIEW', theme: currentTheme, branding: brandingSettings }, window.location.origin); }, 1000); } }; if (isLoading) { return (
); } return (
{/* Page Header */}

{t('branding.title')}

{t('branding.subtitle')}

{/* Company Branding */}

{t('branding.companyInfo')}

handleBrandingChange('company_name', e.target.value)} placeholder={t('branding.companyName')} helperText={t('branding.companyNameHelp')} /> handleBrandingChange('company_tagline', e.target.value)} placeholder={t('branding.companyTagline')} helperText={t('branding.companyTaglineHelp')} /> handleBrandingChange('support_email', e.target.value)} placeholder="support@yourcompany.com" helperText={t('branding.supportEmailHelp')} />