From 29a7b55fdacd982d02bbd9f679356237167da0ec Mon Sep 17 00:00:00 2001 From: dwindown Date: Wed, 19 Nov 2025 16:35:27 +0700 Subject: [PATCH] fix: Add variable dropdown to TipTap rich text editor Fixed missing variable dropdown in email template editor. Problem: - RichTextEditor component had dropdown functionality - But variables prop was empty array - Users had to manually type {variable_name} Solution: - Added comprehensive list of 40+ available variables - Includes order, customer, payment, shipping, URL, store variables - Variables now show in dropdown for easy insertion Available Variables: - Order: order_number, order_total, order_items_table, etc. - Customer: customer_name, customer_email, customer_phone - Payment: payment_method, transaction_id, payment_retry_url - Shipping: tracking_number, tracking_url, shipping_carrier - URLs: order_url, review_url, shop_url, my_account_url - Store: site_name, support_email, current_year Now users can click dropdown and select variables instead of typing them manually. --- .../Settings/Notifications/EditTemplate.tsx | 53 +- includes/Core/Notifications/EventRegistry.php | 452 +++++++++--------- 2 files changed, 280 insertions(+), 225 deletions(-) diff --git a/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx b/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx index ed96fb9..f7e94db 100644 --- a/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx +++ b/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx @@ -36,8 +36,55 @@ export default function EditTemplate() { const [subject, setSubject] = useState(''); const [markdownContent, setMarkdownContent] = useState(''); // Source of truth: Markdown const [blocks, setBlocks] = useState([]); // Visual mode view (derived from markdown) - const [variables, setVariables] = useState<{ [key: string]: string }>({}); const [activeTab, setActiveTab] = useState('preview'); + + // All available template variables + const availableVariables = [ + // Order variables + 'order_number', + 'order_id', + 'order_date', + 'order_total', + 'order_subtotal', + 'order_tax', + 'order_shipping', + 'order_discount', + 'order_status', + 'order_url', + 'order_items_table', + 'completion_date', + 'estimated_delivery', + // Customer variables + 'customer_name', + 'customer_first_name', + 'customer_last_name', + 'customer_email', + 'customer_phone', + 'billing_address', + 'shipping_address', + // Payment variables + 'payment_method', + 'payment_status', + 'payment_date', + 'transaction_id', + 'payment_retry_url', + // Shipping/Tracking variables + 'tracking_number', + 'tracking_url', + 'shipping_carrier', + 'shipping_method', + // URL variables + 'review_url', + 'shop_url', + 'my_account_url', + // Store variables + 'site_name', + 'site_title', + 'store_name', + 'store_url', + 'support_email', + 'current_year', + ]; // Fetch email customization settings const { data: emailSettings } = useQuery({ @@ -129,8 +176,8 @@ export default function EditTemplate() { setBlocks(newBlocks); // Keep blocks in sync }; - // Get variable keys for the rich text editor - const variableKeys = Object.keys(variables); + // Variable keys for the rich text editor dropdown + const variableKeys = availableVariables; // Parse [card] tags and [button] shortcodes for preview const parseCardsForPreview = (content: string) => { diff --git a/includes/Core/Notifications/EventRegistry.php b/includes/Core/Notifications/EventRegistry.php index e02109a..a637e38 100644 --- a/includes/Core/Notifications/EventRegistry.php +++ b/includes/Core/Notifications/EventRegistry.php @@ -32,228 +32,236 @@ class EventRegistry { * ] */ public static function get_all_events() { - $events = [ - // STAFF EVENTS - 'order_placed' => [ - 'id' => 'order_placed', - 'label' => __('Order Placed', 'woonoow'), - 'description' => __('When a new order is placed', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => 'new_order', - 'enabled' => true, - ], - 'order_processing' => [ - 'id' => 'order_processing', - 'label' => __('Order Processing', 'woonoow'), - 'description' => __('When order is confirmed and being processed', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => 'customer_processing_order', - 'enabled' => true, - ], - 'order_shipped' => [ - 'id' => 'order_shipped', - 'label' => __('Order Shipped', 'woonoow'), - 'description' => __('When order is shipped', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => '', - 'enabled' => true, - ], - 'order_completed' => [ - 'id' => 'order_completed', - 'label' => __('Order Completed', 'woonoow'), - 'description' => __('When order is marked as completed', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => 'customer_completed_order', - 'enabled' => true, - ], - 'order_cancelled' => [ - 'id' => 'order_cancelled', - 'label' => __('Order Cancelled', 'woonoow'), - 'description' => __('When order is cancelled', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => 'cancelled_order', - 'enabled' => true, - ], - 'payment_received' => [ - 'id' => 'payment_received', - 'label' => __('Payment Received', 'woonoow'), - 'description' => __('When payment is successfully received', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => '', - 'enabled' => true, - ], - 'payment_failed' => [ - 'id' => 'payment_failed', - 'label' => __('Payment Failed', 'woonoow'), - 'description' => __('When payment processing fails', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => '', - 'enabled' => true, - ], - 'order_on_hold' => [ - 'id' => 'order_on_hold', - 'label' => __('Order On-Hold', 'woonoow'), - 'description' => __('When order is awaiting payment', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => '', - 'enabled' => true, - ], - 'order_failed' => [ - 'id' => 'order_failed', - 'label' => __('Order Failed', 'woonoow'), - 'description' => __('When order fails', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => 'failed_order', - 'enabled' => true, - ], - 'order_refunded' => [ - 'id' => 'order_refunded', - 'label' => __('Order Refunded', 'woonoow'), - 'description' => __('When order is refunded', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => '', - 'enabled' => true, - ], - 'order_pending' => [ - 'id' => 'order_pending', - 'label' => __('Order Pending', 'woonoow'), - 'description' => __('When order is created (pending payment)', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'staff', - 'wc_email' => '', - 'enabled' => true, - ], - - // CUSTOMER EVENTS - 'order_placed_customer' => [ - 'id' => 'order_placed', - 'label' => __('Order Placed', 'woonoow'), - 'description' => __('When customer places an order', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => 'customer_on_hold_order', - 'enabled' => true, - ], - 'order_processing_customer' => [ - 'id' => 'order_processing', - 'label' => __('Order Processing', 'woonoow'), - 'description' => __('When order status changes to processing', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => 'customer_processing_order', - 'enabled' => true, - ], - 'order_shipped_customer' => [ - 'id' => 'order_shipped', - 'label' => __('Order Shipped', 'woonoow'), - 'description' => __('When order is shipped with tracking', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => '', - 'enabled' => true, - ], - 'order_completed_customer' => [ - 'id' => 'order_completed', - 'label' => __('Order Completed', 'woonoow'), - 'description' => __('When order is delivered/completed', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => 'customer_completed_order', - 'enabled' => true, - ], - 'order_cancelled_customer' => [ - 'id' => 'order_cancelled', - 'label' => __('Order Cancelled', 'woonoow'), - 'description' => __('When order is cancelled', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => 'customer_refunded_order', - 'enabled' => true, - ], - 'payment_received_customer' => [ - 'id' => 'payment_received', - 'label' => __('Payment Received', 'woonoow'), - 'description' => __('When payment is confirmed', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => '', - 'enabled' => true, - ], - 'payment_failed_customer' => [ - 'id' => 'payment_failed', - 'label' => __('Payment Failed', 'woonoow'), - 'description' => __('When payment fails - prompt retry', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => 'customer_failed_order', - 'enabled' => true, - ], - 'new_customer' => [ - 'id' => 'new_customer', - 'label' => __('New Customer', 'woonoow'), - 'description' => __('When a new customer registers', 'woonoow'), - 'category' => 'customers', - 'recipient_type' => 'customer', - 'wc_email' => 'customer_new_account', - 'enabled' => true, - ], - 'order_on_hold_customer' => [ - 'id' => 'order_on_hold', - 'label' => __('Order On-Hold', 'woonoow'), - 'description' => __('When order is awaiting payment', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => 'customer_on_hold_order', - 'enabled' => true, - ], - 'order_failed_customer' => [ - 'id' => 'order_failed', - 'label' => __('Order Failed', 'woonoow'), - 'description' => __('When order fails', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => 'customer_failed_order', - 'enabled' => true, - ], - 'order_refunded_customer' => [ - 'id' => 'order_refunded', - 'label' => __('Order Refunded', 'woonoow'), - 'description' => __('When order is refunded', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => 'customer_refunded_order', - 'enabled' => true, - ], - 'order_pending_customer' => [ - 'id' => 'order_pending', - 'label' => __('Order Pending', 'woonoow'), - 'description' => __('When order is created (pending payment)', 'woonoow'), - 'category' => 'orders', - 'recipient_type' => 'customer', - 'wc_email' => '', - 'enabled' => true, - ], - ]; - - /** - * Filter: woonoow_notification_events_registry - * - * Allows plugins/themes to add custom notification events - * - * @param array $events Event definitions - */ - return apply_filters('woonoow_notification_events_registry', $events); - } + $events = [ + // ===== CUSTOMER ACCOUNT EVENTS ===== + 'new_customer' => [ + 'id' => 'new_customer', + 'label' => __('New Customer', 'woonoow'), + 'description' => __('When a new customer registers', 'woonoow'), + 'category' => 'customers', + 'recipient_type' => 'customer', + 'wc_email' => 'customer_new_account', + 'enabled' => true, + ], + + // ===== ORDER INITIATION ===== + 'order_placed' => [ + 'id' => 'order_placed', + 'label' => __('Order Placed', 'woonoow'), + 'description' => __('When a new order is placed', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => 'new_order', + 'enabled' => true, + ], + 'order_placed_customer' => [ + 'id' => 'order_placed', + 'label' => __('Order Placed', 'woonoow'), + 'description' => __('When customer places an order', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => 'customer_on_hold_order', + 'enabled' => true, + ], + 'order_pending' => [ + 'id' => 'order_pending', + 'label' => __('Order Pending', 'woonoow'), + 'description' => __('When order is created (pending payment)', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => '', + 'enabled' => true, + ], + 'order_pending_customer' => [ + 'id' => 'order_pending', + 'label' => __('Order Pending', 'woonoow'), + 'description' => __('When order is created (pending payment)', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => '', + 'enabled' => true, + ], + + // ===== PAYMENT PROCESSING ===== + 'order_on_hold' => [ + 'id' => 'order_on_hold', + 'label' => __('Order On-Hold', 'woonoow'), + 'description' => __('When order is awaiting payment', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => '', + 'enabled' => true, + ], + 'order_on_hold_customer' => [ + 'id' => 'order_on_hold', + 'label' => __('Order On-Hold', 'woonoow'), + 'description' => __('When order is awaiting payment', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => 'customer_on_hold_order', + 'enabled' => true, + ], + 'payment_received' => [ + 'id' => 'payment_received', + 'label' => __('Payment Received', 'woonoow'), + 'description' => __('When payment is successfully received', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => '', + 'enabled' => true, + ], + 'payment_received_customer' => [ + 'id' => 'payment_received', + 'label' => __('Payment Received', 'woonoow'), + 'description' => __('When payment is confirmed', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => '', + 'enabled' => true, + ], + 'payment_failed' => [ + 'id' => 'payment_failed', + 'label' => __('Payment Failed', 'woonoow'), + 'description' => __('When payment processing fails', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => '', + 'enabled' => true, + ], + 'payment_failed_customer' => [ + 'id' => 'payment_failed', + 'label' => __('Payment Failed', 'woonoow'), + 'description' => __('When payment fails - prompt retry', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => 'customer_failed_order', + 'enabled' => true, + ], + + // ===== ORDER PROCESSING ===== + 'order_processing' => [ + 'id' => 'order_processing', + 'label' => __('Order Processing', 'woonoow'), + 'description' => __('When order is confirmed and being processed', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => 'customer_processing_order', + 'enabled' => true, + ], + 'order_processing_customer' => [ + 'id' => 'order_processing', + 'label' => __('Order Processing', 'woonoow'), + 'description' => __('When order status changes to processing', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => 'customer_processing_order', + 'enabled' => true, + ], + + // ===== FULFILLMENT ===== + 'order_shipped' => [ + 'id' => 'order_shipped', + 'label' => __('Order Shipped', 'woonoow'), + 'description' => __('When order is shipped', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => '', + 'enabled' => true, + ], + 'order_shipped_customer' => [ + 'id' => 'order_shipped', + 'label' => __('Order Shipped', 'woonoow'), + 'description' => __('When order is shipped with tracking', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => '', + 'enabled' => true, + ], + 'order_completed' => [ + 'id' => 'order_completed', + 'label' => __('Order Completed', 'woonoow'), + 'description' => __('When order is marked as completed', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => 'customer_completed_order', + 'enabled' => true, + ], + 'order_completed_customer' => [ + 'id' => 'order_completed', + 'label' => __('Order Completed', 'woonoow'), + 'description' => __('When order is delivered/completed', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => 'customer_completed_order', + 'enabled' => true, + ], + + // ===== ORDER ISSUES / EXCEPTIONS ===== + 'order_failed' => [ + 'id' => 'order_failed', + 'label' => __('Order Failed', 'woonoow'), + 'description' => __('When order fails', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => 'failed_order', + 'enabled' => true, + ], + 'order_failed_customer' => [ + 'id' => 'order_failed', + 'label' => __('Order Failed', 'woonoow'), + 'description' => __('When order fails', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => 'customer_failed_order', + 'enabled' => true, + ], + 'order_cancelled' => [ + 'id' => 'order_cancelled', + 'label' => __('Order Cancelled', 'woonoow'), + 'description' => __('When order is cancelled', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => 'cancelled_order', + 'enabled' => true, + ], + 'order_cancelled_customer' => [ + 'id' => 'order_cancelled', + 'label' => __('Order Cancelled', 'woonoow'), + 'description' => __('When order is cancelled', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => 'customer_refunded_order', + 'enabled' => true, + ], + 'order_refunded' => [ + 'id' => 'order_refunded', + 'label' => __('Order Refunded', 'woonoow'), + 'description' => __('When order is refunded', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'staff', + 'wc_email' => '', + 'enabled' => true, + ], + 'order_refunded_customer' => [ + 'id' => 'order_refunded', + 'label' => __('Order Refunded', 'woonoow'), + 'description' => __('When order is refunded', 'woonoow'), + 'category' => 'orders', + 'recipient_type' => 'customer', + 'wc_email' => 'customer_refunded_order', + 'enabled' => true, + ], + ]; + + /** + * Filter: woonoow_notification_events_registry + * + * Allows plugins/themes to add custom notification events + * + * @param array $events Event definitions + */ + return apply_filters('woonoow_notification_events_registry', $events); +} /** * Get events by recipient type