fix: template save API + contextual variables per event
1. API Route Fix (NotificationsController.php): - Changed PUT to POST for /templates/:eventId/:channelId - Frontend was using api.post() but backend only accepted PUT - Templates can now be saved 2. Contextual Variables (EventRegistry.php): - Added get_variables_for_event() method - Returns category-based variables (order, customer, product, etc.) - Merges event-specific variables from event definition - Sorted alphabetically for easy browsing 3. API Response (NotificationsController.php): - Template API now returns available_variables for the event - Frontend can show only relevant variables 4. Frontend (EditTemplate.tsx): - Removed hardcoded 50+ variable list - Now uses template.available_variables from API - Variables update based on selected event type
This commit is contained in:
@@ -38,54 +38,6 @@ export default function EditTemplate() {
|
|||||||
const [blocks, setBlocks] = useState<EmailBlock[]>([]); // Visual mode view (derived from markdown)
|
const [blocks, setBlocks] = useState<EmailBlock[]>([]); // Visual mode view (derived from markdown)
|
||||||
const [activeTab, setActiveTab] = useState('preview');
|
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
|
// Fetch email customization settings
|
||||||
const { data: emailSettings } = useQuery({
|
const { data: emailSettings } = useQuery({
|
||||||
queryKey: ['email-settings'],
|
queryKey: ['email-settings'],
|
||||||
@@ -176,8 +128,10 @@ export default function EditTemplate() {
|
|||||||
setBlocks(newBlocks); // Keep blocks in sync
|
setBlocks(newBlocks); // Keep blocks in sync
|
||||||
};
|
};
|
||||||
|
|
||||||
// Variable keys for the rich text editor dropdown
|
// Variable keys for the rich text editor dropdown - from API (contextual per event)
|
||||||
const variableKeys = availableVariables;
|
const variableKeys = template?.available_variables
|
||||||
|
? Object.keys(template.available_variables).map(k => k.replace(/^\{|}$/g, ''))
|
||||||
|
: [];
|
||||||
|
|
||||||
// Parse [card] tags and [button] shortcodes for preview
|
// Parse [card] tags and [button] shortcodes for preview
|
||||||
const parseCardsForPreview = (content: string) => {
|
const parseCardsForPreview = (content: string) => {
|
||||||
@@ -318,7 +272,7 @@ export default function EditTemplate() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Highlight variables that don't have sample data
|
// Highlight variables that don't have sample data
|
||||||
availableVariables.forEach(key => {
|
variableKeys.forEach((key: string) => {
|
||||||
if (!storeVariables[key] && !sampleData[key]) {
|
if (!storeVariables[key] && !sampleData[key]) {
|
||||||
const sampleValue = `<span style="background: #fef3c7; padding: 2px 4px; border-radius: 2px;">[${key}]</span>`;
|
const sampleValue = `<span style="background: #fef3c7; padding: 2px 4px; border-radius: 2px;">[${key}]</span>`;
|
||||||
previewBody = previewBody.replace(new RegExp(`\\{${key}\\}`, 'g'), sampleValue);
|
previewBody = previewBody.replace(new RegExp(`\\{${key}\\}`, 'g'), sampleValue);
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class NotificationsController {
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// GET/PUT /woonoow/v1/notifications/templates/:eventId/:channelId
|
// GET/POST /woonoow/v1/notifications/templates/:eventId/:channelId
|
||||||
register_rest_route($this->namespace, '/' . $this->rest_base . '/templates/(?P<eventId>[a-zA-Z0-9_-]+)/(?P<channelId>[a-zA-Z0-9_-]+)', [
|
register_rest_route($this->namespace, '/' . $this->rest_base . '/templates/(?P<eventId>[a-zA-Z0-9_-]+)/(?P<channelId>[a-zA-Z0-9_-]+)', [
|
||||||
[
|
[
|
||||||
'methods' => 'GET',
|
'methods' => 'GET',
|
||||||
@@ -77,7 +77,7 @@ class NotificationsController {
|
|||||||
'permission_callback' => [$this, 'check_permission'],
|
'permission_callback' => [$this, 'check_permission'],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'methods' => 'PUT',
|
'methods' => 'POST',
|
||||||
'callback' => [$this, 'save_template'],
|
'callback' => [$this, 'save_template'],
|
||||||
'permission_callback' => [$this, 'check_permission'],
|
'permission_callback' => [$this, 'check_permission'],
|
||||||
],
|
],
|
||||||
@@ -486,6 +486,9 @@ class NotificationsController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add available variables for this event (contextual)
|
||||||
|
$template['available_variables'] = EventRegistry::get_variables_for_event($event_id, $recipient_type);
|
||||||
|
|
||||||
return new WP_REST_Response($template, 200);
|
return new WP_REST_Response($template, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -371,4 +371,150 @@ class EventRegistry {
|
|||||||
public static function event_exists($event_id, $recipient_type) {
|
public static function event_exists($event_id, $recipient_type) {
|
||||||
return self::get_event($event_id, $recipient_type) !== null;
|
return self::get_event($event_id, $recipient_type) !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get variables available for a specific event
|
||||||
|
*
|
||||||
|
* Returns both common variables and event-specific variables
|
||||||
|
*
|
||||||
|
* @param string $event_id Event ID
|
||||||
|
* @param string $recipient_type Recipient type
|
||||||
|
* @return array Array of variable definitions with key => description
|
||||||
|
*/
|
||||||
|
public static function get_variables_for_event($event_id, $recipient_type = 'customer') {
|
||||||
|
// Common variables available for ALL events
|
||||||
|
$common = [
|
||||||
|
'{site_name}' => __('Store/Site name', 'woonoow'),
|
||||||
|
'{site_title}' => __('Site title', 'woonoow'),
|
||||||
|
'{store_url}' => __('Store URL', 'woonoow'),
|
||||||
|
'{shop_url}' => __('Shop page URL', 'woonoow'),
|
||||||
|
'{my_account_url}' => __('My Account page URL', 'woonoow'),
|
||||||
|
'{login_url}' => __('Login page URL', 'woonoow'),
|
||||||
|
'{support_email}' => __('Support email address', 'woonoow'),
|
||||||
|
'{current_year}' => __('Current year', 'woonoow'),
|
||||||
|
'{current_date}' => __('Current date', 'woonoow'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Customer variables (for customer-facing events)
|
||||||
|
$customer_vars = [
|
||||||
|
'{customer_name}' => __('Customer full name', 'woonoow'),
|
||||||
|
'{customer_first_name}' => __('Customer first name', 'woonoow'),
|
||||||
|
'{customer_last_name}' => __('Customer last name', 'woonoow'),
|
||||||
|
'{customer_email}' => __('Customer email', 'woonoow'),
|
||||||
|
'{customer_phone}' => __('Customer phone', 'woonoow'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Order variables (for order-related events)
|
||||||
|
$order_vars = [
|
||||||
|
'{order_id}' => __('Order ID/number', 'woonoow'),
|
||||||
|
'{order_number}' => __('Order number', 'woonoow'),
|
||||||
|
'{order_date}' => __('Order date', 'woonoow'),
|
||||||
|
'{order_total}' => __('Order total', 'woonoow'),
|
||||||
|
'{order_subtotal}' => __('Order subtotal', 'woonoow'),
|
||||||
|
'{order_tax}' => __('Order tax', 'woonoow'),
|
||||||
|
'{order_shipping}' => __('Shipping cost', 'woonoow'),
|
||||||
|
'{order_discount}' => __('Discount amount', 'woonoow'),
|
||||||
|
'{order_status}' => __('Order status', 'woonoow'),
|
||||||
|
'{order_url}' => __('Order details URL', 'woonoow'),
|
||||||
|
'{order_items_table}' => __('Order items table (HTML)', 'woonoow'),
|
||||||
|
'{billing_address}' => __('Billing address', 'woonoow'),
|
||||||
|
'{shipping_address}' => __('Shipping address', 'woonoow'),
|
||||||
|
'{payment_method}' => __('Payment method', 'woonoow'),
|
||||||
|
'{payment_status}' => __('Payment status', 'woonoow'),
|
||||||
|
'{shipping_method}' => __('Shipping method', 'woonoow'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Shipping/tracking variables (for shipped/delivered events)
|
||||||
|
$shipping_vars = [
|
||||||
|
'{tracking_number}' => __('Tracking number', 'woonoow'),
|
||||||
|
'{tracking_url}' => __('Tracking URL', 'woonoow'),
|
||||||
|
'{shipping_carrier}' => __('Shipping carrier', 'woonoow'),
|
||||||
|
'{estimated_delivery}' => __('Estimated delivery date', 'woonoow'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Product variables (for stock alerts)
|
||||||
|
$product_vars = [
|
||||||
|
'{product_name}' => __('Product name', 'woonoow'),
|
||||||
|
'{product_sku}' => __('Product SKU', 'woonoow'),
|
||||||
|
'{product_url}' => __('Product URL', 'woonoow'),
|
||||||
|
'{product_price}' => __('Product price', 'woonoow'),
|
||||||
|
'{stock_quantity}' => __('Stock quantity', 'woonoow'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Newsletter variables
|
||||||
|
$newsletter_vars = [
|
||||||
|
'{subscriber_email}' => __('Subscriber email', 'woonoow'),
|
||||||
|
'{subscriber_name}' => __('Subscriber name', 'woonoow'),
|
||||||
|
'{unsubscribe_url}' => __('Unsubscribe link', 'woonoow'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Build variables based on event ID and category
|
||||||
|
$event = self::get_event($event_id, $recipient_type);
|
||||||
|
|
||||||
|
// If event not found, try to match by just event_id
|
||||||
|
if (!$event) {
|
||||||
|
$all_events = self::get_all_events();
|
||||||
|
foreach ($all_events as $e) {
|
||||||
|
if ($e['id'] === $event_id) {
|
||||||
|
$event = $e;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start with common vars
|
||||||
|
$variables = $common;
|
||||||
|
|
||||||
|
// Add category-specific vars
|
||||||
|
if ($event) {
|
||||||
|
$category = $event['category'] ?? '';
|
||||||
|
|
||||||
|
// Add customer vars for customer-facing events
|
||||||
|
if (($event['recipient_type'] ?? '') === 'customer') {
|
||||||
|
$variables = array_merge($variables, $customer_vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add based on category
|
||||||
|
switch ($category) {
|
||||||
|
case 'orders':
|
||||||
|
$variables = array_merge($variables, $order_vars);
|
||||||
|
// Add tracking for completed/shipped events
|
||||||
|
if (in_array($event_id, ['order_completed', 'order_shipped', 'order_delivered'])) {
|
||||||
|
$variables = array_merge($variables, $shipping_vars);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'products':
|
||||||
|
$variables = array_merge($variables, $product_vars);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'marketing':
|
||||||
|
$variables = array_merge($variables, $newsletter_vars);
|
||||||
|
// Add campaign-specific for newsletter_campaign
|
||||||
|
if ($event_id === 'newsletter_campaign') {
|
||||||
|
$variables['{content}'] = __('Campaign content', 'woonoow');
|
||||||
|
$variables['{campaign_title}'] = __('Campaign title', 'woonoow');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'customers':
|
||||||
|
$variables = array_merge($variables, $customer_vars);
|
||||||
|
// Add account-specific vars
|
||||||
|
if ($event_id === 'new_customer') {
|
||||||
|
$variables['{user_temp_password}'] = __('Temporary password', 'woonoow');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add event-specific variables if defined
|
||||||
|
if (!empty($event['variables'])) {
|
||||||
|
$variables = array_merge($variables, $event['variables']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort alphabetically for easier browsing
|
||||||
|
ksort($variables);
|
||||||
|
|
||||||
|
return $variables;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user