/** * Branding Settings Page * * Logo uploads, colors, and display preferences. * Features live preview of color changes that revert on navigation/reload if not saved. */ import React, { useState, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useOutletContext } from 'react-router-dom'; import { Palette, Save, Check, Upload, X, Image as ImageIcon } from 'lucide-react'; import { Business, User } from '../../types'; import { applyBrandColors } from '../../utils/colorUtils'; import { UpgradePrompt } from '../../components/UpgradePrompt'; import { FeatureKey } from '../../hooks/usePlanFeatures'; // Color palette options const colorPalettes = [ { name: 'Ocean Blue', primary: '#2563eb', secondary: '#0ea5e9' }, { name: 'Sky Blue', primary: '#0ea5e9', secondary: '#38bdf8' }, { name: 'Mint Green', primary: '#10b981', secondary: '#34d399' }, { name: 'Coral Reef', primary: '#f97316', secondary: '#fb923c' }, { name: 'Lavender', primary: '#a78bfa', secondary: '#c4b5fd' }, { name: 'Rose Pink', primary: '#ec4899', secondary: '#f472b6' }, { name: 'Forest Green', primary: '#059669', secondary: '#10b981' }, { name: 'Royal Purple', primary: '#7c3aed', secondary: '#a78bfa' }, { name: 'Slate Gray', primary: '#475569', secondary: '#64748b' }, { name: 'Crimson Red', primary: '#dc2626', secondary: '#ef4444' }, ]; const BrandingSettings: React.FC = () => { const { t } = useTranslation(); const { business, updateBusiness, user, isFeatureLocked, lockedFeature } = useOutletContext<{ business: Business; updateBusiness: (updates: Partial) => void; user: User; isFeatureLocked?: boolean; lockedFeature?: FeatureKey; }>(); const [formState, setFormState] = useState({ logoUrl: business.logoUrl, emailLogoUrl: business.emailLogoUrl, logoDisplayMode: business.logoDisplayMode || 'text-only', primaryColor: business.primaryColor, secondaryColor: business.secondaryColor || business.primaryColor, }); const [showToast, setShowToast] = useState(false); // Store the original saved colors to restore on unmount/navigation const savedColorsRef = useRef({ primary: business.primaryColor, secondary: business.secondaryColor || business.primaryColor, }); // Live preview: Update CSS variables as user cycles through palettes useEffect(() => { applyBrandColors(formState.primaryColor, formState.secondaryColor); // Cleanup: Restore saved colors when component unmounts (navigation away) return () => { applyBrandColors(savedColorsRef.current.primary, savedColorsRef.current.secondary); }; }, [formState.primaryColor, formState.secondaryColor]); // Update savedColorsRef when business data changes (after successful save) useEffect(() => { savedColorsRef.current = { primary: business.primaryColor, secondary: business.secondaryColor || business.primaryColor, }; }, [business.primaryColor, business.secondaryColor]); const handleSave = async () => { await updateBusiness(formState); // Update the saved reference so cleanup doesn't revert savedColorsRef.current = { primary: formState.primaryColor, secondary: formState.secondaryColor, }; setShowToast(true); setTimeout(() => setShowToast(false), 3000); }; const selectPalette = (primary: string, secondary: string) => { setFormState(prev => ({ ...prev, primaryColor: primary, secondaryColor: secondary })); }; const isOwner = user.role === 'owner'; if (!isOwner) { return (

Only the business owner can access these settings.

); } // Show upgrade prompt if feature is locked if (isFeatureLocked && lockedFeature) { return ; } return (
{/* Header */}

{t('settings.branding.title', 'Branding')}

Customize your business appearance with logos and colors.

{/* Logo Section */}

Brand Logos

Upload your logos for different purposes. PNG with transparent background recommended.

{/* Website Logo */}
Website Logo

Used in sidebar and customer-facing pages. Recommended: 500x500px

{formState.logoUrl ? (
Logo
) : (
)}
{ const file = e.target.files?.[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => { setFormState(prev => ({ ...prev, logoUrl: reader.result as string })); }; reader.readAsDataURL(file); } }} />
{/* Display Mode */}
{/* Email Logo */}
Email Logo

Used in email notifications. Recommended: 600x200px wide

{formState.emailLogoUrl ? (
Email Logo
) : (
)}
{ const file = e.target.files?.[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => { setFormState(prev => ({ ...prev, emailLogoUrl: reader.result as string })); }; reader.readAsDataURL(file); } }} />
{/* Colors Section */}

Brand Colors

Choose a color palette or customize your own colors.

{/* Palette Grid */}
{colorPalettes.map((palette) => ( ))}
{/* Custom Colors */}
setFormState(prev => ({ ...prev, primaryColor: e.target.value }))} className="w-10 h-10 rounded cursor-pointer" /> setFormState(prev => ({ ...prev, primaryColor: e.target.value }))} className="w-24 px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-white" />
setFormState(prev => ({ ...prev, secondaryColor: e.target.value }))} className="w-10 h-10 rounded cursor-pointer" /> setFormState(prev => ({ ...prev, secondaryColor: e.target.value }))} className="w-24 px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-white" />
{/* Save Button */}
{/* Toast */} {showToast && (
Changes saved successfully
)}
); }; export default BrandingSettings;