## Implemented (Tasks 1-6): ### 1. All Social Platforms Added ✅ **Platforms:** - Facebook, X (Twitter), Instagram - LinkedIn, YouTube - Discord, Spotify, Telegram - WhatsApp, Threads, Website **Frontend:** Updated select dropdown with all platforms **Backend:** Added to allowed_platforms whitelist ### 2. PNG Icons Instead of Emoji ✅ - Use local PNG files from `/assets/icons/` - Format: `mage--{platform}-{color}.png` - Applied to email rendering and preview - Much more accurate than emoji ### 3. Icon Color Option (Black/White) ✅ - New setting: `social_icon_color` - Select dropdown: White Icons / Black Icons - White for dark backgrounds - Black for light backgrounds - Applied to all social icons ### 4. Body Background Color Setting ✅ - New setting: `body_bg_color` - Color picker + hex input - Default: #f8f8f8 - Applied to email body background - Applied to preview ### 5. Editor Mode Styling 📝 **Note:** Editor mode intentionally shows structure/content Preview mode shows final styled result with all customizations This is standard email builder UX pattern ### 6. Hero Preview Text Color Fixed ✅ - Applied `hero_text_color` directly to h3 and p - Now correctly shows selected color - Both heading and paragraph use custom color ## Technical Changes: **Frontend:** - Added body_bg_color and social_icon_color to interface - Updated all social platform icons (Lucide) - PNG icon URLs in preview - Hero preview color fix **Backend:** - Added body_bg_color and social_icon_color to defaults - Sanitization for new fields - Updated allowed_platforms array - PNG icon URL generation with color param **Email Rendering:** - Use PNG icons with color selection - Apply body_bg_color - get_social_icon_url() updated for PNG files ## Files Modified: - `routes/Settings/Notifications/EmailCustomization.tsx` - `routes/Settings/Notifications/EditTemplate.tsx` - `includes/Api/NotificationsController.php` - `includes/Core/Notifications/EmailRenderer.php` Task 7 (default email content) pending - separate commit.
669 lines
25 KiB
TypeScript
669 lines
25 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { api } from '@/lib/api';
|
|
import { SettingsLayout } from '../components/SettingsLayout';
|
|
import { SettingsCard } from '../components/SettingsCard';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { __ } from '@/lib/i18n';
|
|
import { ArrowLeft, RefreshCw, Upload, Plus, Trash2, Facebook, Twitter, Instagram, Linkedin, Youtube, Globe, MessageCircle, Music, Send, AtSign } from 'lucide-react';
|
|
import { toast } from 'sonner';
|
|
import { openWPMediaLogo } from '@/lib/wp-media';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
|
|
interface SocialLink {
|
|
platform: string;
|
|
url: string;
|
|
}
|
|
|
|
interface EmailSettings {
|
|
primary_color: string;
|
|
secondary_color: string;
|
|
hero_gradient_start: string;
|
|
hero_gradient_end: string;
|
|
hero_text_color: string;
|
|
button_text_color: string;
|
|
body_bg_color: string;
|
|
social_icon_color: string;
|
|
logo_url: string;
|
|
header_text: string;
|
|
footer_text: string;
|
|
social_links: SocialLink[];
|
|
}
|
|
|
|
export default function EmailCustomization() {
|
|
const navigate = useNavigate();
|
|
const queryClient = useQueryClient();
|
|
|
|
// Fetch email settings
|
|
const { data: settings, isLoading } = useQuery({
|
|
queryKey: ['email-settings'],
|
|
queryFn: () => api.get('/notifications/email-settings'),
|
|
placeholderData: {
|
|
primary_color: '#7f54b3',
|
|
secondary_color: '#7f54b3',
|
|
hero_gradient_start: '#667eea',
|
|
hero_gradient_end: '#764ba2',
|
|
hero_text_color: '#ffffff',
|
|
button_text_color: '#ffffff',
|
|
body_bg_color: '#f8f8f8',
|
|
social_icon_color: 'white',
|
|
logo_url: '',
|
|
header_text: '',
|
|
footer_text: '',
|
|
social_links: [],
|
|
},
|
|
});
|
|
|
|
const [formData, setFormData] = useState<EmailSettings>(settings || {
|
|
primary_color: '#7f54b3',
|
|
secondary_color: '#7f54b3',
|
|
hero_gradient_start: '#667eea',
|
|
hero_gradient_end: '#764ba2',
|
|
hero_text_color: '#ffffff',
|
|
button_text_color: '#ffffff',
|
|
body_bg_color: '#f8f8f8',
|
|
social_icon_color: 'white',
|
|
logo_url: '',
|
|
header_text: '',
|
|
footer_text: '',
|
|
social_links: [],
|
|
});
|
|
|
|
// Update form when settings load
|
|
React.useEffect(() => {
|
|
if (settings) {
|
|
setFormData(settings);
|
|
}
|
|
}, [settings]);
|
|
|
|
const saveMutation = useMutation({
|
|
mutationFn: async () => {
|
|
return api.post('/notifications/email-settings', formData);
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['email-settings'] });
|
|
toast.success(__('Email settings saved successfully'));
|
|
},
|
|
onError: (error: any) => {
|
|
toast.error(error.message || __('Failed to save email settings'));
|
|
},
|
|
});
|
|
|
|
const resetMutation = useMutation({
|
|
mutationFn: async () => {
|
|
return api.del('/notifications/email-settings');
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['email-settings'] });
|
|
toast.success(__('Email settings reset to defaults'));
|
|
},
|
|
onError: (error: any) => {
|
|
toast.error(error.message || __('Failed to reset email settings'));
|
|
},
|
|
});
|
|
|
|
const handleSave = async () => {
|
|
return new Promise<void>((resolve, reject) => {
|
|
saveMutation.mutate(undefined, {
|
|
onSuccess: () => resolve(),
|
|
onError: () => reject(),
|
|
});
|
|
});
|
|
};
|
|
|
|
const handleReset = () => {
|
|
if (!confirm(__('Are you sure you want to reset all email customization to defaults?'))) return;
|
|
resetMutation.mutate();
|
|
};
|
|
|
|
const handleChange = (field: keyof EmailSettings, value: string) => {
|
|
setFormData(prev => ({ ...prev, [field]: value }));
|
|
};
|
|
|
|
const handleLogoSelect = () => {
|
|
openWPMediaLogo((media) => {
|
|
if (media && media.url) {
|
|
handleChange('logo_url', media.url);
|
|
}
|
|
});
|
|
};
|
|
|
|
const addSocialLink = () => {
|
|
setFormData(prev => ({
|
|
...prev,
|
|
social_links: [...prev.social_links, { platform: 'facebook', url: '' }],
|
|
}));
|
|
};
|
|
|
|
const removeSocialLink = (index: number) => {
|
|
setFormData(prev => ({
|
|
...prev,
|
|
social_links: prev.social_links.filter((_, i) => i !== index),
|
|
}));
|
|
};
|
|
|
|
const updateSocialLink = (index: number, field: 'platform' | 'url', value: string) => {
|
|
setFormData(prev => ({
|
|
...prev,
|
|
social_links: prev.social_links.map((link, i) =>
|
|
i === index ? { ...link, [field]: value } : link
|
|
),
|
|
}));
|
|
};
|
|
|
|
const getSocialIcon = (platform: string) => {
|
|
const icons: Record<string, any> = {
|
|
facebook: Facebook,
|
|
x: AtSign,
|
|
instagram: Instagram,
|
|
linkedin: Linkedin,
|
|
youtube: Youtube,
|
|
discord: MessageCircle,
|
|
spotify: Music,
|
|
telegram: Send,
|
|
whatsapp: MessageCircle,
|
|
threads: AtSign,
|
|
website: Globe,
|
|
};
|
|
const Icon = icons[platform] || Globe;
|
|
return <Icon className="h-4 w-4" />;
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<SettingsLayout
|
|
title={__('Email Customization')}
|
|
description={__('Loading...')}
|
|
>
|
|
<div className="flex items-center justify-center py-12">
|
|
<RefreshCw className="h-6 w-6 animate-spin text-muted-foreground" />
|
|
</div>
|
|
</SettingsLayout>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<SettingsLayout
|
|
title={__('Email Customization')}
|
|
description={__('Customize the appearance and branding of all email templates')}
|
|
onSave={handleSave}
|
|
saveLabel={__('Save Settings')}
|
|
isLoading={saveMutation.isPending}
|
|
action={
|
|
<div className="flex items-center gap-2">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => navigate('/settings/notifications')}
|
|
className="gap-2"
|
|
>
|
|
<ArrowLeft className="h-4 w-4" />
|
|
<span className="hidden sm:inline">{__('Back')}</span>
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={handleReset}
|
|
disabled={resetMutation.isPending}
|
|
>
|
|
{resetMutation.isPending ? (
|
|
<RefreshCw className="h-4 w-4 animate-spin" />
|
|
) : (
|
|
__('Reset to Defaults')
|
|
)}
|
|
</Button>
|
|
</div>
|
|
}
|
|
>
|
|
<div className="space-y-6">
|
|
{/* Brand Colors */}
|
|
<SettingsCard
|
|
title={__('Brand Colors')}
|
|
description={__('Set your primary and secondary brand colors for buttons and accents')}
|
|
>
|
|
<div className="grid gap-6 sm:grid-cols-2">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="primary_color">{__('Primary Color')}</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="primary_color"
|
|
type="color"
|
|
value={formData.primary_color}
|
|
onChange={(e) => handleChange('primary_color', e.target.value)}
|
|
className="w-20 h-10 p-1 cursor-pointer"
|
|
/>
|
|
<Input
|
|
type="text"
|
|
value={formData.primary_color}
|
|
onChange={(e) => handleChange('primary_color', e.target.value)}
|
|
placeholder="#7f54b3"
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
{__('Used for primary buttons and main accents')}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="secondary_color">{__('Secondary Color')}</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="secondary_color"
|
|
type="color"
|
|
value={formData.secondary_color}
|
|
onChange={(e) => handleChange('secondary_color', e.target.value)}
|
|
className="w-20 h-10 p-1 cursor-pointer"
|
|
/>
|
|
<Input
|
|
type="text"
|
|
value={formData.secondary_color}
|
|
onChange={(e) => handleChange('secondary_color', e.target.value)}
|
|
placeholder="#7f54b3"
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
{__('Used for outline buttons and borders')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</SettingsCard>
|
|
|
|
{/* Hero Card Gradient */}
|
|
<SettingsCard
|
|
title={__('Hero Card Gradient')}
|
|
description={__('Customize the gradient colors for hero/success card backgrounds')}
|
|
>
|
|
<div className="grid gap-6 sm:grid-cols-2">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="hero_gradient_start">{__('Gradient Start')}</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="hero_gradient_start"
|
|
type="color"
|
|
value={formData.hero_gradient_start}
|
|
onChange={(e) => handleChange('hero_gradient_start', e.target.value)}
|
|
className="w-20 h-10 p-1 cursor-pointer"
|
|
/>
|
|
<Input
|
|
type="text"
|
|
value={formData.hero_gradient_start}
|
|
onChange={(e) => handleChange('hero_gradient_start', e.target.value)}
|
|
placeholder="#667eea"
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="hero_gradient_end">{__('Gradient End')}</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="hero_gradient_end"
|
|
type="color"
|
|
value={formData.hero_gradient_end}
|
|
onChange={(e) => handleChange('hero_gradient_end', e.target.value)}
|
|
className="w-20 h-10 p-1 cursor-pointer"
|
|
/>
|
|
<Input
|
|
type="text"
|
|
value={formData.hero_gradient_end}
|
|
onChange={(e) => handleChange('hero_gradient_end', e.target.value)}
|
|
placeholder="#764ba2"
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="hero_text_color">{__('Text Color')}</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="hero_text_color"
|
|
type="color"
|
|
value={formData.hero_text_color}
|
|
onChange={(e) => handleChange('hero_text_color', e.target.value)}
|
|
className="w-20 h-10 p-1 cursor-pointer"
|
|
/>
|
|
<Input
|
|
type="text"
|
|
value={formData.hero_text_color}
|
|
onChange={(e) => handleChange('hero_text_color', e.target.value)}
|
|
placeholder="#ffffff"
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
{__('Text and heading color for hero cards (usually white)')}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Preview */}
|
|
<div className="mt-4 p-6 rounded-lg text-center" style={{
|
|
background: `linear-gradient(135deg, ${formData.hero_gradient_start} 0%, ${formData.hero_gradient_end} 100%)`
|
|
}}>
|
|
<h3 className="text-xl font-bold mb-2" style={{ color: formData.hero_text_color }}>{__('Preview')}</h3>
|
|
<p className="text-sm opacity-90" style={{ color: formData.hero_text_color }}>{__('This is how your hero cards will look')}</p>
|
|
</div>
|
|
</SettingsCard>
|
|
|
|
{/* Button Styling */}
|
|
<SettingsCard
|
|
title={__('Button Styling')}
|
|
description={__('Customize button text color and appearance')}
|
|
>
|
|
<div className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="button_text_color">{__('Button Text Color')}</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="button_text_color"
|
|
type="color"
|
|
value={formData.button_text_color}
|
|
onChange={(e) => handleChange('button_text_color', e.target.value)}
|
|
className="w-20 h-10 p-1 cursor-pointer"
|
|
/>
|
|
<Input
|
|
type="text"
|
|
value={formData.button_text_color}
|
|
onChange={(e) => handleChange('button_text_color', e.target.value)}
|
|
placeholder="#ffffff"
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
{__('Text color for buttons (usually white for dark buttons)')}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Button Preview */}
|
|
<div className="flex gap-3 flex-wrap">
|
|
<button
|
|
className="px-6 py-3 rounded-lg font-medium"
|
|
style={{
|
|
backgroundColor: formData.primary_color,
|
|
color: formData.button_text_color,
|
|
}}
|
|
>
|
|
{__('Primary Button')}
|
|
</button>
|
|
<button
|
|
className="px-6 py-3 rounded-lg font-medium border-2"
|
|
style={{
|
|
borderColor: formData.secondary_color,
|
|
color: formData.secondary_color,
|
|
backgroundColor: 'transparent',
|
|
}}
|
|
>
|
|
{__('Secondary Button')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</SettingsCard>
|
|
|
|
{/* Email Background & Social Icons */}
|
|
<SettingsCard
|
|
title={__('Email Background & Social Icons')}
|
|
description={__('Customize email background and social icon colors')}
|
|
>
|
|
<div className="grid gap-6 sm:grid-cols-2">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="body_bg_color">{__('Email Background Color')}</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="body_bg_color"
|
|
type="color"
|
|
value={formData.body_bg_color}
|
|
onChange={(e) => handleChange('body_bg_color', e.target.value)}
|
|
className="w-20 h-10 p-1 cursor-pointer"
|
|
/>
|
|
<Input
|
|
type="text"
|
|
value={formData.body_bg_color}
|
|
onChange={(e) => handleChange('body_bg_color', e.target.value)}
|
|
placeholder="#f8f8f8"
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
{__('Background color for the email body')}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="social_icon_color">{__('Social Icon Color')}</Label>
|
|
<Select
|
|
value={formData.social_icon_color}
|
|
onValueChange={(value) => handleChange('social_icon_color', value)}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="white">{__('White Icons')}</SelectItem>
|
|
<SelectItem value="black">{__('Black Icons')}</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<p className="text-xs text-muted-foreground">
|
|
{__('Choose white icons for dark backgrounds, black for light backgrounds')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</SettingsCard>
|
|
|
|
{/* Logo & Branding */}
|
|
<SettingsCard
|
|
title={__('Logo & Branding')}
|
|
description={__('Add your logo and custom header/footer text')}
|
|
>
|
|
<div className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="logo_url">{__('Logo URL')}</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
id="logo_url"
|
|
type="url"
|
|
value={formData.logo_url}
|
|
onChange={(e) => handleChange('logo_url', e.target.value)}
|
|
placeholder="https://example.com/logo.png"
|
|
className="flex-1"
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={handleLogoSelect}
|
|
className="gap-2"
|
|
>
|
|
<Upload className="h-4 w-4" />
|
|
{__('Select')}
|
|
</Button>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
{__('Full URL to your logo image (recommended: 200x60px)')}
|
|
</p>
|
|
{formData.logo_url && (
|
|
<div className="mt-2 p-4 border rounded-lg bg-muted/30">
|
|
<img
|
|
src={formData.logo_url}
|
|
alt="Logo preview"
|
|
className="max-h-16 object-contain"
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="header_text">{__('Header Text')}</Label>
|
|
<Input
|
|
id="header_text"
|
|
type="text"
|
|
value={formData.header_text}
|
|
onChange={(e) => handleChange('header_text', e.target.value)}
|
|
placeholder={__('Your Store Name')}
|
|
/>
|
|
<p className="text-xs text-muted-foreground">
|
|
{__('Text shown in email header (leave empty to use store name)')}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="footer_text">{__('Footer Text')}</Label>
|
|
<Input
|
|
id="footer_text"
|
|
type="text"
|
|
value={formData.footer_text}
|
|
onChange={(e) => handleChange('footer_text', e.target.value)}
|
|
placeholder={__('© {current_year} Your Store. All rights reserved.')}
|
|
/>
|
|
<p className="text-xs text-muted-foreground">
|
|
{__('Text shown in email footer. Use {current_year} for dynamic year.')}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Social Links */}
|
|
<div className="space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<Label>{__('Social Links')}</Label>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={addSocialLink}
|
|
className="gap-2"
|
|
>
|
|
<Plus className="h-4 w-4" />
|
|
{__('Add Social Link')}
|
|
</Button>
|
|
</div>
|
|
|
|
{formData.social_links.length === 0 ? (
|
|
<p className="text-sm text-muted-foreground">
|
|
{__('No social links added. Click "Add Social Link" to get started.')}
|
|
</p>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{formData.social_links.map((link, index) => (
|
|
<div key={index} className="flex gap-2 items-start p-3 border rounded-lg">
|
|
<div className="flex-1 grid grid-cols-2 gap-2">
|
|
<div>
|
|
<Select
|
|
value={link.platform}
|
|
onValueChange={(value) => updateSocialLink(index, 'platform', value)}
|
|
>
|
|
<SelectTrigger className="h-9">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="facebook">
|
|
<div className="flex items-center gap-2">
|
|
<Facebook className="h-4 w-4" />
|
|
Facebook
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="x">
|
|
<div className="flex items-center gap-2">
|
|
<AtSign className="h-4 w-4" />
|
|
X (Twitter)
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="instagram">
|
|
<div className="flex items-center gap-2">
|
|
<Instagram className="h-4 w-4" />
|
|
Instagram
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="linkedin">
|
|
<div className="flex items-center gap-2">
|
|
<Linkedin className="h-4 w-4" />
|
|
LinkedIn
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="youtube">
|
|
<div className="flex items-center gap-2">
|
|
<Youtube className="h-4 w-4" />
|
|
YouTube
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="discord">
|
|
<div className="flex items-center gap-2">
|
|
<MessageCircle className="h-4 w-4" />
|
|
Discord
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="spotify">
|
|
<div className="flex items-center gap-2">
|
|
<Music className="h-4 w-4" />
|
|
Spotify
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="telegram">
|
|
<div className="flex items-center gap-2">
|
|
<Send className="h-4 w-4" />
|
|
Telegram
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="whatsapp">
|
|
<div className="flex items-center gap-2">
|
|
<MessageCircle className="h-4 w-4" />
|
|
WhatsApp
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="threads">
|
|
<div className="flex items-center gap-2">
|
|
<AtSign className="h-4 w-4" />
|
|
Threads
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="website">
|
|
<div className="flex items-center gap-2">
|
|
<Globe className="h-4 w-4" />
|
|
Website
|
|
</div>
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<Input
|
|
type="url"
|
|
value={link.url}
|
|
onChange={(e) => updateSocialLink(index, 'url', e.target.value)}
|
|
placeholder="https://..."
|
|
className="h-9"
|
|
/>
|
|
</div>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => removeSocialLink(index)}
|
|
className="h-9 w-9 p-0 text-destructive hover:text-destructive"
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
<p className="text-xs text-muted-foreground">
|
|
{__('Social links will appear as icons in the email footer')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</SettingsCard>
|
|
|
|
{/* Info Box */}
|
|
<div className="bg-blue-50 dark:bg-blue-950/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
|
|
<p className="text-sm text-blue-900 dark:text-blue-100">
|
|
<strong>{__('Note:')}</strong> {__('These settings will apply to all email templates. Individual templates can still override specific content, but colors and branding will be consistent across all emails.')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</SettingsLayout>
|
|
);
|
|
}
|