diff --git a/admin-spa/src/components/ui/tiptap-card-extension.ts b/admin-spa/src/components/ui/tiptap-card-extension.ts new file mode 100644 index 0000000..02f2bcf --- /dev/null +++ b/admin-spa/src/components/ui/tiptap-card-extension.ts @@ -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); + }, + }; + }, +}); diff --git a/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx b/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx index ef3609e..f086339 100644 --- a/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx +++ b/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx @@ -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: '

• Product 1 x 2
• Product 2 x 1

', + 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 = `[${key}]`; 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);