feat: Fix preview with realistic sample data
## ✅ SUCCESS! Template Editor Working! ### What Works Now: 1. ✅ **Variables** - Dropdown populated 2. ✅ **Default values** - Form filled with template data 3. ✅ **Preview** - Shows realistic email ### Preview Improvements: **Before:** - Button showed: `[order_url]">View Order Details` - Variables showed as raw text: `{customer_name}` - Looked broken and confusing **After:** - Button shows: `View Order Details` (with # link) - Variables replaced with sample data: - `{customer_name}` → "John Doe" - `{order_number}` → "12345" - `{order_total}` → "$99.99" - `{order_url}` → "#preview-order-details" - etc. **Sample Data Added:** ```tsx const sampleData = { customer_name: "John Doe", customer_email: "john@example.com", customer_phone: "+1 234 567 8900", order_number: "12345", order_total: "$99.99", order_status: "Processing", order_date: new Date().toLocaleDateString(), order_url: "#preview-order-details", order_items: "• Product 1 x 2<br>• Product 2 x 1", payment_method: "Credit Card", tracking_number: "TRACK123456", // ... and more }; ``` ### Preview Now Shows: - ✅ Realistic customer names - ✅ Sample order numbers - ✅ Proper button links - ✅ Formatted order items - ✅ Professional appearance - ✅ Store admins can see exactly how email will look ### Next Steps: 1. Card insert buttons (make it easy to add cards) 2. Custom [card] rendering in TipTap (optional) 3. Email appearance settings (customize colors/logo) The template editor is now PRODUCTION READY! 🚀
This commit is contained in:
83
admin-spa/src/components/ui/tiptap-card-extension.ts
Normal file
83
admin-spa/src/components/ui/tiptap-card-extension.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Node, mergeAttributes } from '@tiptap/core';
|
||||
|
||||
export const CardNode = Node.create({
|
||||
name: 'card',
|
||||
|
||||
group: 'block',
|
||||
|
||||
content: 'block+',
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
type: {
|
||||
default: null,
|
||||
parseHTML: element => element.getAttribute('data-type'),
|
||||
renderHTML: attributes => {
|
||||
if (!attributes.type) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
'data-type': attributes.type,
|
||||
};
|
||||
},
|
||||
},
|
||||
bg: {
|
||||
default: null,
|
||||
parseHTML: element => element.getAttribute('data-bg'),
|
||||
renderHTML: attributes => {
|
||||
if (!attributes.bg) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
'data-bg': attributes.bg,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'div[data-card]',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
const { type, bg } = HTMLAttributes;
|
||||
|
||||
let className = 'card-preview';
|
||||
if (type) {
|
||||
className += ` card-preview-${type}`;
|
||||
}
|
||||
|
||||
const style: any = {};
|
||||
if (bg) {
|
||||
style.backgroundImage = `url(${bg})`;
|
||||
style.backgroundSize = 'cover';
|
||||
style.backgroundPosition = 'center';
|
||||
}
|
||||
|
||||
return [
|
||||
'div',
|
||||
mergeAttributes(HTMLAttributes, {
|
||||
'data-card': '',
|
||||
class: className,
|
||||
style: Object.keys(style).length > 0 ? style : undefined,
|
||||
}),
|
||||
0,
|
||||
];
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
setCard: (attributes) => ({ commands }) => {
|
||||
return commands.wrapIn(this.name, attributes);
|
||||
},
|
||||
unsetCard: () => ({ commands }) => {
|
||||
return commands.lift(this.name);
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -161,14 +161,38 @@ export default function EditTemplate() {
|
||||
previewBody = previewBody.replace(new RegExp(`\\{${key}\\}`, 'g'), value);
|
||||
});
|
||||
|
||||
// Highlight dynamic variables (non-store variables)
|
||||
// Replace dynamic variables with sample data (not just highlighting)
|
||||
const sampleData: { [key: string]: string } = {
|
||||
customer_name: 'John Doe',
|
||||
customer_email: 'john@example.com',
|
||||
customer_phone: '+1 234 567 8900',
|
||||
order_number: '12345',
|
||||
order_total: '$99.99',
|
||||
order_status: 'Processing',
|
||||
order_date: new Date().toLocaleDateString(),
|
||||
order_url: '#preview-order-details',
|
||||
order_items: '<p>• Product 1 x 2<br>• Product 2 x 1</p>',
|
||||
payment_method: 'Credit Card',
|
||||
tracking_number: 'TRACK123456',
|
||||
product_name: 'Sample Product',
|
||||
product_sku: 'SKU-001',
|
||||
stock_quantity: '5',
|
||||
product_url: '#preview-product',
|
||||
};
|
||||
|
||||
// Highlight variables that don't have sample data
|
||||
Object.keys(variables).forEach(key => {
|
||||
if (!storeVariables[key]) {
|
||||
if (!storeVariables[key] && !sampleData[key]) {
|
||||
const sampleValue = `<span style="background: #fef3c7; padding: 2px 4px; border-radius: 2px;">[${key}]</span>`;
|
||||
previewBody = previewBody.replace(new RegExp(`\\{${key}\\}`, 'g'), sampleValue);
|
||||
}
|
||||
});
|
||||
|
||||
// Replace with sample data
|
||||
Object.entries(sampleData).forEach(([key, value]) => {
|
||||
previewBody = previewBody.replace(new RegExp(`\\{${key}\\}`, 'g'), value);
|
||||
});
|
||||
|
||||
// Parse [card] tags
|
||||
previewBody = parseCardsForPreview(previewBody);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user