fix: Email Preview Issues - All 5 Fixed! 🔧

## Issues Fixed:

### 1. Button Not Rendering 
- Buttons now use custom primary_color
- Button text uses button_text_color
- Outline buttons use secondary_color
- Applied to .button and .button-outline classes

### 2. Double Hash in Order Number 
- Changed order_number from "#12345" to "12345"
- Templates already have # prefix
- Prevents ##12345 display

### 3. Duplicate Icons in Social Selector 
- Removed duplicate icon from SelectTrigger
- SelectValue already shows the icon
- Clean single icon display

### 4. Header/Footer Not Reflecting Customization 
- Fetch email settings in EditTemplate
- Apply logo_url or header_text to header
- Apply footer_text with {current_year} replacement
- Render social icons in footer

### 5. Hero Heading Not Using Custom Color 
- Apply hero_text_color to all hero card types
- .card-hero, .card-success, .card-highlight
- All text and headings use custom color

## Preview Now Shows:
 Custom logo (if set) or header text
 Custom hero gradient colors
 Custom hero text color (white/custom)
 Custom button colors (primary & secondary)
 Custom footer text with {current_year}
 Social icons in footer

## Files:
- `routes/Settings/Notifications/EditTemplate.tsx` - Preview integration
- `routes/Settings/Notifications/EmailCustomization.tsx` - UI fix

Everything synced! Preview matches actual emails! 🎉
This commit is contained in:
dwindown
2025-11-13 14:05:39 +07:00
parent 1032e659de
commit e52429603b
2 changed files with 59 additions and 12 deletions

View File

@@ -38,6 +38,12 @@ export default function EditTemplate() {
const [activeTab, setActiveTab] = useState('editor');
const [codeMode, setCodeMode] = useState(false);
// Fetch email customization settings
const { data: emailSettings } = useQuery({
queryKey: ['email-settings'],
queryFn: () => api.get('/notifications/email-settings'),
});
// Fetch template
const { data: template, isLoading, error } = useQuery({
queryKey: ['notification-template', eventId, channelId],
@@ -187,7 +193,7 @@ export default function EditTemplate() {
// Replace dynamic variables with sample data (not just highlighting)
const sampleData: { [key: string]: string } = {
order_number: '#12345',
order_number: '12345',
order_total: '$99.99',
order_status: 'Processing',
order_date: new Date().toLocaleDateString(),
@@ -262,6 +268,33 @@ export default function EditTemplate() {
// Parse [card] tags
previewBody = parseCardsForPreview(previewBody);
// Get email settings for preview
const settings = emailSettings || {};
const primaryColor = settings.primary_color || '#7f54b3';
const secondaryColor = settings.secondary_color || '#7f54b3';
const heroGradientStart = settings.hero_gradient_start || '#667eea';
const heroGradientEnd = settings.hero_gradient_end || '#764ba2';
const heroTextColor = settings.hero_text_color || '#ffffff';
const buttonTextColor = settings.button_text_color || '#ffffff';
const logoUrl = settings.logo_url || '';
const headerText = settings.header_text || 'My WordPress Store';
const footerText = settings.footer_text || `© ${new Date().getFullYear()} My WordPress Store. All rights reserved.`;
const socialLinks = settings.social_links || [];
// Replace {current_year} in footer
const processedFooter = footerText.replace('{current_year}', new Date().getFullYear().toString());
// Generate social icons HTML
const socialIconsHtml = socialLinks.length > 0 ? `
<div style="margin-top: 16px;">
${socialLinks.map((link: any) => `
<a href="${link.url}" style="display: inline-block; margin: 0 8px; text-decoration: none;">
<span style="font-size: 24px;">${getSocialIcon(link.platform)}</span>
</a>
`).join('')}
</div>
` : '';
return `
<!DOCTYPE html>
<html>
@@ -272,17 +305,20 @@ export default function EditTemplate() {
.header { padding: 32px; text-align: center; background: #f8f8f8; }
.card-gutter { padding: 0 16px; }
.card { background: #ffffff; border-radius: 8px; margin-bottom: 24px; padding: 32px 40px; }
.card-success { background: #e8f5e9; border: 1px solid #4caf50; }
.card-highlight { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff; }
.card-highlight * { color: #fff !important; }
.card-success { background: linear-gradient(135deg, ${heroGradientStart} 0%, ${heroGradientEnd} 100%); color: ${heroTextColor}; }
.card-success * { color: ${heroTextColor} !important; }
.card-highlight { background: linear-gradient(135deg, ${heroGradientStart} 0%, ${heroGradientEnd} 100%); color: ${heroTextColor}; }
.card-highlight * { color: ${heroTextColor} !important; }
.card-hero { background: linear-gradient(135deg, ${heroGradientStart} 0%, ${heroGradientEnd} 100%); color: ${heroTextColor}; }
.card-hero * { color: ${heroTextColor} !important; }
.card-info { background: #f0f7ff; border: 1px solid #0071e3; }
.card-warning { background: #fff8e1; border: 1px solid #ff9800; }
h1 { font-size: 26px; margin-top: 0; margin-bottom: 16px; color: #333; }
h2 { font-size: 18px; margin-top: 0; margin-bottom: 16px; color: #333; }
h3 { font-size: 16px; margin-top: 0; margin-bottom: 8px; color: #333; }
p { font-size: 16px; line-height: 1.6; color: #555; margin-bottom: 16px; }
.button { display: inline-block; background: #7f54b3; color: #fff !important; padding: 14px 28px; border-radius: 6px; text-decoration: none; font-weight: 600; }
.button-outline { display: inline-block; background: transparent; color: #7f54b3 !important; padding: 12px 26px; border: 2px solid #7f54b3; border-radius: 6px; text-decoration: none; font-weight: 600; }
.button { display: inline-block; background: ${primaryColor}; color: ${buttonTextColor} !important; padding: 14px 28px; border-radius: 6px; text-decoration: none; font-weight: 600; }
.button-outline { display: inline-block; background: transparent; color: ${secondaryColor} !important; padding: 12px 26px; border: 2px solid ${secondaryColor}; border-radius: 6px; text-decoration: none; font-weight: 600; }
.info-box { background: #f6f6f6; border-radius: 6px; padding: 20px; margin: 16px 0; }
.footer { padding: 32px; text-align: center; color: #888; font-size: 13px; }
</style>
@@ -290,19 +326,33 @@ export default function EditTemplate() {
<body>
<div class="container">
<div class="header">
<strong style="font-size: 24px; color: #333;">My WordPress Store</strong>
${logoUrl ? `<img src="${logoUrl}" alt="${headerText}" style="max-width: 200px; max-height: 60px;">` : `<strong style="font-size: 24px; color: #333;">${headerText}</strong>`}
</div>
<div class="card-gutter">
${previewBody}
</div>
<div class="footer">
<p>© ${new Date().getFullYear()} My WordPress Store. All rights reserved.</p>
<p>${processedFooter}</p>
${socialIconsHtml}
</div>
</div>
</body>
</html>
`;
};
// Helper function to get social icon emoji
const getSocialIcon = (platform: string) => {
const icons: Record<string, string> = {
facebook: '📘',
twitter: '🐦',
instagram: '📷',
linkedin: '💼',
youtube: '📺',
website: '🌐',
};
return icons[platform] || '🔗';
};
if (!eventId || !channelId) {
return (

View File

@@ -496,10 +496,7 @@ export default function EmailCustomization() {
onValueChange={(value) => updateSocialLink(index, 'platform', value)}
>
<SelectTrigger className="h-9">
<div className="flex items-center gap-2">
{getSocialIcon(link.platform)}
<SelectValue />
</div>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="facebook">