WooCommerce 库存通知系统

为 WooCommerce 商店添加了一个全面的库存通知系统,让客户可以注册补货提醒。当产品补货时,订阅的客户会自动收到定制的电子邮件通知。

### 主要功能
* **自动通知** – 产品补货时自动发送电子邮件通知
* **降价警报** – 产品降价时可选通知
* **可变产品支持** – 适用于简单产品和所有产品变体
* **可自定义电子邮件** – 完全自定义通知电子邮件模板
* **我的帐户集成** – 客户可以从他们的帐户管理他们的订阅
* **详细统计数据** – 跟踪您最想要的缺货产品
* **AJAX 订阅表单** – 流畅的用户体验,无需重新加载页面
* **短代码支持** – 使用简单的短代码在任何地方添加通知表单
* **可自定义样式** – 将表单与您的主题设计相匹配
* **GDPR 就绪** – 客户可以管理和删除他们的订阅

相关文章: WooCommerce自定义订单状态

<?php
/**
* Plugin Name: BDFG Stock Notifier for WooCommerce
* Plugin URI: https://beiduofengou.net/2025/01/22/stock-notifier-for-woocommerce/
* Description: Adds a powerful notification system to your WooCommerce store that lets customers sign up for back-in-stock alerts. When products return to stock, subscribed customers automatically receive customized email notifications.
* Version: 2.4.0
* Author: beiduofengou
* Author URI: https://beiduofengou.net
* Text Domain: bdfg-stock-notifier
* Domain Path: /languages
* Requires at least: 5.6
* Requires PHP: 7.4
* WC requires at least: 5.0
* WC tested up to: 8.5
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

// Define plugin constants
define('BDFG_STOCK_NOTIFIER_VERSION', '2.4.0');
define('BDFG_STOCK_NOTIFIER_FILE', __FILE__);
define('BDFG_STOCK_NOTIFIER_PATH', plugin_dir_path(__FILE__));
define('BDFG_STOCK_NOTIFIER_URL', plugin_dir_url(__FILE__));
define('BDFG_STOCK_NOTIFIER_BASENAME', plugin_basename(__FILE__));

// Load text domain
function bdfg_stock_notifier_load_textdomain() {
load_plugin_textdomain('bdfg-stock-notifier', false, dirname(plugin_basename(__FILE__)) . '/languages/');
}
add_action('plugins_loaded', 'bdfg_stock_notifier_load_textdomain');

// Check if WooCommerce is installed
function bdfg_stock_notifier_check_woocommerce() {
if (!class_exists('WooCommerce')) {
add_action('admin_notices', 'bdfg_stock_notifier_woocommerce_missing_notice');
return false;
}
return true;
}

// WooCommerce missing notice
function bdfg_stock_notifier_woocommerce_missing_notice() {
?>
<div class="notice notice-error">
<p><?php _e('BDFG Stock Notifier requires WooCommerce to be installed and active.', 'bdfg-stock-notifier'); ?></p>
</div>
<?php
}

/**
* Create database tables for stock notifications
*/
function bdfg_stock_notifier_create_tables() {
global $wpdb;

$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';

$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
email varchar(100) NOT NULL,
product_id bigint(20) NOT NULL,
variation_id bigint(20) DEFAULT 0,
date_created datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
status varchar(20) DEFAULT 'pending' NOT NULL,
user_id bigint(20) DEFAULT 0,
notify_price_drop tinyint(1) DEFAULT 0,
PRIMARY KEY (id),
KEY email (email),
KEY product_id (product_id),
KEY variation_id (variation_id),
KEY user_id (user_id)
) $charset_collate;";

require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}

// Plugin activation hook
register_activation_hook(__FILE__, 'bdfg_stock_notifier_activate');
function bdfg_stock_notifier_activate() {
if (bdfg_stock_notifier_check_woocommerce()) {
bdfg_stock_notifier_create_tables();

// Set default options
$default_options = array(
'enable_stock_notifications' => 'yes',
'enable_price_drop' => 'no',
'notification_email_subject' => __('Product Back in Stock - {product_name}', 'bdfg-stock-notifier'),
'notification_email_template' => __("Hello,\n\nGood news! The product you were interested in is back in stock:\n\n{product_name}\n\nYou can purchase it now at: {product_url}\n\nBest regards,\n{site_name}", 'bdfg-stock-notifier'),
'display_position' => 'woocommerce_single_product_summary',
'display_priority' => 30,
'button_color' => '#0066cc',
'button_text_color' => '#ffffff',
);

add_option('bdfg_stock_notifier_settings', $default_options);

// Create logs directory
$upload_dir = wp_upload_dir();
$logs_dir = trailingslashit($upload_dir['basedir']) . 'bdfg-stock-notifier-logs';
if (!file_exists($logs_dir)) {
wp_mkdir_p($logs_dir);
// Add an index.php file to prevent directory listing
@file_put_contents($logs_dir . '/index.php', "<?php\n// Silence is golden.");
}
}
}

// Plugin uninstall hook
register_uninstall_hook(__FILE__, 'bdfg_stock_notifier_uninstall');
function bdfg_stock_notifier_uninstall() {
global $wpdb;

// Let's delete the database table
$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';
$wpdb->query("DROP TABLE IF EXISTS $table_name");

// Delete plugin options
delete_option('bdfg_stock_notifier_settings');

// Clean up logs
$upload_dir = wp_upload_dir();
$logs_dir = trailingslashit($upload_dir['basedir']) . 'bdfg-stock-notifier-logs';
if (file_exists($logs_dir)) {
foreach (glob($logs_dir . '/*.*') as $file) {
@unlink($file);
}
@rmdir($logs_dir);
}
}

// Core plugin class
class BDFG_Stock_Notifier {
private static $instance = null;
private $settings;

public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}

private function __construct() {
if (!bdfg_stock_notifier_check_woocommerce()) {
return;
}

$this->settings = get_option('bdfg_stock_notifier_settings', array());

// Initialize hooks
add_action('init', array($this, 'init_hooks'));

// Admin settings
if (is_admin()) {
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'register_settings'));
add_filter('plugin_action_links_' . BDFG_STOCK_NOTIFIER_BASENAME, array($this, 'add_plugin_action_links'));
}
}

public function init_hooks() {
// Frontend hooks
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));

// Add notification form based on admin settings
$position = isset($this->settings['display_position']) ? $this->settings['display_position'] : 'woocommerce_single_product_summary';
$priority = isset($this->settings['display_priority']) ? intval($this->settings['display_priority']) : 30;
add_action($position, array($this, 'display_stock_notification_form'), $priority);

// AJAX handlers
add_action('wp_ajax_bdfg_stock_notifier_subscribe', array($this, 'handle_subscription_ajax'));
add_action('wp_ajax_nopriv_bdfg_stock_notifier_subscribe', array($this, 'handle_subscription_ajax'));

// Product stock status change notifications
add_action('woocommerce_product_set_stock_status', array($this, 'product_stock_status_changed'), 10, 3);
add_action('woocommerce_variation_set_stock_status', array($this, 'variation_stock_status_changed'), 10, 3);

// Price change tracking
add_action('woocommerce_product_set_price', array($this, 'track_price_change'), 10, 2);
add_action('woocommerce_variation_set_price', array($this, 'track_variation_price_change'), 10, 2);

// Add shortcode
add_shortcode('bdfg_stock_notifier', array($this, 'shortcode_handler'));

// User account integration
add_action('woocommerce_account_stock-notifications_endpoint', array($this, 'account_stock_notifications_content'));
add_filter('woocommerce_account_menu_items', array($this, 'add_account_menu_item'), 40);
add_filter('woocommerce_get_query_vars', array($this, 'add_account_endpoint'));

// Register custom REST API endpoints
add_action('rest_api_init', array($this, 'register_rest_endpoints'));
}

/**
* Register and load frontend assets
*/
public function enqueue_scripts() {
if (is_product() || has_shortcode(get_post_field('post_content', get_the_ID()), 'bdfg_stock_notifier')) {
wp_enqueue_style('bdfg-stock-notifier', BDFG_STOCK_NOTIFIER_URL . 'assets/css/frontend.css', array(), BDFG_STOCK_NOTIFIER_VERSION);
wp_enqueue_script('bdfg-stock-notifier', BDFG_STOCK_NOTIFIER_URL . 'assets/js/frontend.js', array('jquery'), BDFG_STOCK_NOTIFIER_VERSION, true);

// Custom button styling
$button_color = isset($this->settings['button_color']) ? sanitize_hex_color($this->settings['button_color']) : '#0066cc';
$button_text_color = isset($this->settings['button_text_color']) ? sanitize_hex_color($this->settings['button_text_color']) : '#ffffff';

$custom_css = "
.bdfg-stock-notification-submit {
background-color: {$button_color} !important;
color: {$button_text_color} !important;
}
.bdfg-stock-notification-submit:hover {
background-color: " . $this->adjust_brightness($button_color, -10) . " !important;
}
";
wp_add_inline_style('bdfg-stock-notifier', $custom_css);

wp_localize_script('bdfg-stock-notifier', 'bdfgStockNotifier', array(
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bdfg_stock_notifier_nonce'),
'i18n' => array(
'subscribe' => __('Notify Me When Available', 'bdfg-stock-notifier'),
'subscribed' => __('We\'ll notify you when back in stock', 'bdfg-stock-notifier'),
'select_options' => __('Please select product options first', 'bdfg-stock-notifier'),
'email_invalid' => __('Please enter a valid email address', 'bdfg-stock-notifier'),
'error' => __('Something went wrong. Please try again.', 'bdfg-stock-notifier')
),
'settings' => array(
'enable_price_drop' => isset($this->settings['enable_price_drop']) ? $this->settings['enable_price_drop'] : 'no'
)
));
}
}

/**
* Utility function to darken/lighten colors
*/
private function adjust_brightness($hex, $steps) {
$steps = max(-255, min(255, $steps));

$hex = str_replace('#', '', $hex);
if (strlen($hex) == 3) {
$hex = str_repeat(substr($hex, 0, 1), 2) . str_repeat(substr($hex, 1, 1), 2) . str_repeat(substr($hex, 2, 1), 2);
}

$r = hexdec(substr($hex, 0, 2));
$g = hexdec(substr($hex, 2, 2));
$b = hexdec(substr($hex, 4, 2));

$r = max(0, min(255, $r + $steps));
$g = max(0, min(255, $g + $steps));
$b = max(0, min(255, $b + $steps));

return '#' . dechex($r) . dechex($g) . dechex($b);
}

/**
* Display notification form shortcode handler
*/
public function shortcode_handler($atts) {
$atts = shortcode_atts(array(
'product_id' => 0,
'variation_id' => 0,
'button_text' => '',
'title' => '',
), $atts, 'bdfg_stock_notifier');

$product_id = absint($atts['product_id']);

// If no product ID specified, use current product
if (!$product_id && is_product()) {
global $product;
$product_id = $product->get_id();
}

if (!$product_id) {
return '<p>' . __('Product ID is required for the stock notifier form.', 'bdfg-stock-notifier') . '</p>';
}

$product = wc_get_product($product_id);
if (!$product) {
return '<p>' . __('Invalid product specified.', 'bdfg-stock-notifier') . '</p>';
}

// Start output buffering
ob_start();
$this->display_stock_notification_form($product, $atts);
return ob_get_clean();
}

/**
* Display notification form on product pages
*/
public function display_stock_notification_form($product = null, $options = array()) {
// Check if settings is enabled
if (empty($this->settings['enable_stock_notifications']) || $this->settings['enable_stock_notifications'] != 'yes') {
return;
}

// If product is not provided (happens when called from action hook)
if (!$product || !is_object($product)) {
global $product;
}

if (!$product || !is_a($product, 'WC_Product')) {
return;
}

$product_id = $product->get_id();
$product_type = $product->get_type();

// Don't show for in-stock products
if ($product->is_in_stock() && $product->get_stock_status() != 'outofstock') {
return;
}

// Get options
$button_text = isset($options['button_text']) && !empty($options['button_text'])
? sanitize_text_field($options['button_text'])
: __('Notify Me When Available', 'bdfg-stock-notifier');

$title = isset($options['title']) && !empty($options['title'])
? sanitize_text_field($options['title'])
: __('Out of Stock', 'bdfg-stock-notifier');

$enable_price_drop = isset($this->settings['enable_price_drop']) && $this->settings['enable_price_drop'] == 'yes';

echo '<div class="bdfg-stock-notification-container">';
echo '<h4>' . esc_html($title) . '</h4>';
echo '<p>' . __('This product is currently out of stock. Enter your email to be notified when it becomes available again.', 'bdfg-stock-notifier') . '</p>';

echo '<form class="bdfg-stock-notification-form" data-product-id="' . esc_attr($product_id) . '" data-product-type="' . esc_attr($product_type) . '">';

echo '<input type="email" name="bdfg_email" class="bdfg-stock-notification-email" placeholder="' . esc_attr__('Your Email', 'bdfg-stock-notifier') . '" required>';

// Price drop notification option
if ($enable_price_drop) {
echo '<div class="bdfg-price-drop-option">';
echo '<label>';
echo '<input type="checkbox" name="bdfg_notify_price_drop" value="1" checked> ';
echo esc_html__('Also notify me if this product goes on sale', 'bdfg-stock-notifier');
echo '</label>';
echo '</div>';
}

echo '<div class="bdfg-subscription-response"></div>';
echo '<button type="submit" class="bdfg-stock-notification-submit button alt">' . esc_html($button_text) . '</button>';

// Add branding (can be disabled in settings)
if (!isset($this->settings['remove_branding']) || $this->settings['remove_branding'] != 'yes') {
echo '<div class="bdfg-notifier-branding">';
echo '<small>Powered by <a href="https://beiduofengou.net" target="_blank">BDFG Stock Notifier</a></small>';
echo '</div>';
}

echo '</form>';

echo '</div>';
}

/**
* Handle AJAX subscription requests
*/
public function handle_subscription_ajax() {
check_ajax_referer('bdfg_stock_notifier_nonce', 'nonce');

$email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
$product_id = isset($_POST['product_id']) ? absint($_POST['product_id']) : 0;
$variation_id = isset($_POST['variation_id']) ? absint($_POST['variation_id']) : 0;
$notify_price_drop = isset($_POST['notify_price_drop']) ? (bool)$_POST['notify_price_drop'] : false;

if (!is_email($email)) {
wp_send_json_error(array('message' => __('Please enter a valid email address.', 'bdfg-stock-notifier')));
exit;
}

if (!$product_id) {
wp_send_json_error(array('message' => __('Invalid product.', 'bdfg-stock-notifier')));
exit;
}

$product = wc_get_product($product_id);
if (!$product) {
wp_send_json_error(array('message' => __('Product not found.', 'bdfg-stock-notifier')));
exit;
}

// For variable products, check if variation is selected
if ($product->get_type() == 'variable' && !$variation_id) {
wp_send_json_error(array('message' => __('Please select product options before requesting notification.', 'bdfg-stock-notifier')));
exit;
}

// Only subscribe for out of stock products
$check_id = $variation_id ? $variation_id : $product_id;
$check_product = wc_get_product($check_id);

if ($check_product && $check_product->is_in_stock()) {
wp_send_json_error(array(
'message' => __('This product is already in stock.', 'bdfg-stock-notifier'),
'in_stock' => true
));
exit;
}

// Get user ID if logged in
$user_id = get_current_user_id();

// Save subscription to database
$result = $this->save_subscription($email, $product_id, $variation_id, $user_id, $notify_price_drop);

if ($result === true) {
// Log the subscription
$this->log(sprintf(
'New subscription: Email: %s, Product ID: %d, Variation ID: %d, User ID: %d',
$email, $product_id, $variation_id, $user_id
));

do_action('bdfg_stock_notifier_after_subscription', $email, $product_id, $variation_id, $user_id);

wp_send_json_success(array('message' => __('You will be notified when this product is back in stock.', 'bdfg-stock-notifier')));
} else {
wp_send_json_error(array('message' => $result));
}

exit;
}

/**
* Save subscription to database
*/
private function save_subscription($email, $product_id, $variation_id = 0, $user_id = 0, $notify_price_drop = false) {
global $wpdb;

$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';

// Check if already subscribed
$existing = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM $table_name
WHERE email = %s AND product_id = %d AND variation_id = %d AND status = 'pending'",
$email, $product_id, $variation_id
));

if ($existing) {
return __('You are already subscribed to this product.', 'bdfg-stock-notifier');
}

// Add new subscription
$result = $wpdb->insert(
$table_name,
array(
'email' => $email,
'product_id' => $product_id,
'variation_id' => $variation_id,
'date_created' => current_time('mysql'),
'status' => 'pending',
'user_id' => $user_id,
'notify_price_drop' => $notify_price_drop ? 1 : 0
),
array('%s', '%d', '%d', '%s', '%s', '%d', '%d')
);

return $result ? true : __('Failed to save your subscription. Please try again.', 'bdfg-stock-notifier');
}

/**
* Handle product stock status change
*/
public function product_stock_status_changed($product_id, $stock_status, $product) {
if ($stock_status === 'instock') {
$this->send_stock_notifications($product_id, 0);
}
}

/**
* Handle variation stock status change
*/
public function variation_stock_status_changed($variation_id, $stock_status, $product) {
if ($stock_status === 'instock') {
$parent_id = $product->get_parent_id();
$this->send_stock_notifications($parent_id, $variation_id);
}
}

/**
* Track product price changes
*/
public function track_price_change($product, $price) {
if (isset($this->settings['enable_price_drop']) && $this->settings['enable_price_drop'] == 'yes') {
$product_id = $product->get_id();
$old_price = $product->get_meta('_bdfg_previous_price', true);

if ($old_price && $price < $old_price) {
// Price has dropped, notify subscribers
$this->send_price_drop_notifications($product_id, 0, $price, $old_price);
}

// Store current price for future reference
$product->update_meta_data('_bdfg_previous_price', $price);
$product->save_meta_data();
}
}

/**
* Track variation price changes
*/
public function track_variation_price_change($variation, $price) {
if (isset($this->settings['enable_price_drop']) && $this->settings['enable_price_drop'] == 'yes') {
$variation_id = $variation->get_id();
$parent_id = $variation->get_parent_id();
$old_price = $variation->get_meta('_bdfg_previous_price', true);

if ($old_price && $price < $old_price) {
// Price has dropped, notify subscribers
$this->send_price_drop_notifications($parent_id, $variation_id, $price, $old_price);
}

// Store current price for future reference
$variation->update_meta_data('_bdfg_previous_price', $price);
$variation->save_meta_data();
}
}

/**
* Send price drop notifications
*/
private function send_price_drop_notifications($product_id, $variation_id, $new_price, $old_price) {
global $wpdb;

$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';

// Get subscribers who opted for price drop notifications
if ($variation_id > 0) {
$subscriptions = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $table_name
WHERE product_id = %d AND variation_id = %d AND notify_price_drop = 1",
$product_id, $variation_id
));
} else {
$subscriptions = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $table_name
WHERE product_id = %d AND variation_id = 0 AND notify_price_drop = 1",
$product_id
));
}

if (empty($subscriptions)) {
return;
}

$product = wc_get_product($variation_id > 0 ? $variation_id : $product_id);
if (!$product) {
return;
}

$product_name = $product->get_name();
$product_url = get_permalink($product_id);
$discount_percent = round((($old_price - $new_price) / $old_price) * 100);

// Add variation URL parameter if needed
if ($variation_id > 0) {
$product_url = add_query_arg('variation_id', $variation_id, $product_url);
}

$formatted_old_price = wc_price($old_price);
$formatted_new_price = wc_price($new_price);

// Get email template
$subject = __('Price Drop Alert - {product_name} now {discount_percent}% off!', 'bdfg-stock-notifier');
$message = __("Hello,\n\nGood news! The price for {product_name} has dropped!\n\nOld Price: {old_price}\nNew Price: {new_price}\nYou save: {discount_percent}%\n\nGrab this deal now at: {product_url}\n\nBest regards,\n{site_name}", 'bdfg-stock-notifier');

// Replace placeholders
$subject = str_replace(
array('{product_name}', '{discount_percent}'),
array($product_name, $discount_percent),
$subject
);

$message = str_replace(
array('{product_name}', '{product_url}', '{site_name}', '{old_price}', '{new_price}', '{discount_percent}'),
array($product_name, $product_url, get_bloginfo('name'), $formatted_old_price, $formatted_new_price, $discount_percent),
$message
);

// Set email headers
$headers = array();
$from_name = get_bloginfo('name');
$from_email = get_option('woocommerce_email_from_address', get_option('admin_email'));
if ($from_email) {
$headers[] = 'From: ' . $from_name . ' <' . $from_email . '>';
}
$headers[] = 'Content-Type: text/html; charset=UTF-8';

// Send emails
foreach ($subscriptions as $subscription) {
wp_mail($subscription->email, $subject, nl2br($message), $headers);
}

$this->log(sprintf(
'Price drop notifications sent: Product ID: %d, Variation ID: %d, Old Price: %s, New Price: %s, Subscribers: %d',
$product_id, $variation_id, $formatted_old_price, $formatted_new_price, count($subscriptions)
));
}

/**
* Send stock notifications to subscribers
*/
private function send_stock_notifications($product_id, $variation_id) {
global $wpdb;

$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';

// Get pending subscriptions
if ($variation_id > 0) {
// For variation products
$subscriptions = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $table_name
WHERE product_id = %d AND variation_id = %d AND status = 'pending'",
$product_id, $variation_id
));
} else {
// For simple products
$subscriptions = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $table_name
WHERE product_id = %d AND variation_id = 0 AND status = 'pending'",
$product_id
));
}

if (empty($subscriptions)) {
return;
}

$product = wc_get_product($variation_id > 0 ? $variation_id : $product_id);
if (!$product) {
return;
}

$product_name = $product->get_name();
$product_url = get_permalink($product_id);

// Add variation URL parameter if needed
if ($variation_id > 0) {
$product_url = add_query_arg('variation_id', $variation_id, $product_url);
}

// Add UTM parameters for analytics
$product_url = add_query_arg(array(
'utm_source' => 'stock-notifier',
'utm_medium' => 'email',
'utm_campaign' => 'back-in-stock'
), $product_url);

// Get email templates
$subject = isset($this->settings['notification_email_subject'])
? $this->settings['notification_email_subject']
: __('Product Back in Stock - {product_name}', 'bdfg-stock-notifier');

$message = isset($this->settings['notification_email_template'])
? $this->settings['notification_email_template']
: __("Hello,\n\nGood news! The product you were interested in is back in stock:\n\n{product_name}\n\nYou can purchase it now at: {product_url}\n\nBest regards,\n{site_name}", 'bdfg-stock-notifier');

// Replace placeholders
$subject = str_replace(
array('{product_name}', '{site_name}'),
array($product_name, get_bloginfo('name')),
$subject
);

$message = str_replace(
array('{product_name}', '{product_url}', '{site_name}'),
array($product_name, $product_url, get_bloginfo('name')),
$message
);

// Convert plain text to HTML
$html_message = nl2br($message);

// Add product image if available
$thumbnail_id = $product->get_image_id();
if ($thumbnail_id) {
$image_url = wp_get_attachment_image_url($thumbnail_id, 'thumbnail');
if ($image_url) {
$image_html = '<div style="margin-bottom: 20px;"><img src="' . esc_url($image_url) . '" alt="' . esc_attr($product_name) . '" style="max-width: 200px; height: auto;"></div>';
$html_message = $image_html . $html_message;
}
}

// Set email headers
$headers = array();
$from_name = get_bloginfo('name');
$from_email = get_option('woocommerce_email_from_address', get_option('admin_email'));
if ($from_email) {
$headers[] = 'From: ' . $from_name . ' <' . $from_email . '>';
}
$headers[] = 'Content-Type: text/html; charset=UTF-8';

// Batch processing to prevent server overload
$batch_size = 20;
$batches = array_chunk($subscriptions, $batch_size);
$success_count = 0;
$fail_count = 0;

foreach ($batches as $batch) {
foreach ($batch as $subscription) {
$email_sent = wp_mail($subscription->email, $subject, $html_message, $headers);

// Update subscription status
$wpdb->update(
$table_name,
array('status' => $email_sent ? 'completed' : 'failed'),
array('id' => $subscription->id),
array('%s'),
array('%d')
);

if ($email_sent) {
$success_count++;
} else {
$fail_count++;
}
}

// Small delay between batches
if (count($batches) > 1) {
usleep(500000); // 0.5 seconds
}
}

// Log the results
$this->log(sprintf(
'Stock notification emails sent: Product ID: %d, Variation ID: %d, Success: %d, Failed: %d',
$product_id, $variation_id, $success_count, $fail_count
));

do_action('bdfg_stock_notifier_emails_sent', $product_id, $variation_id, $success_count, $fail_count);
}

/**
* Log important events
*/
private function log($message) {
if (!isset($this->settings['enable_logs']) || $this->settings['enable_logs'] != 'yes') {
return;
}

$upload_dir = wp_upload_dir();
$log_dir = trailingslashit($upload_dir['basedir']) . 'bdfg-stock-notifier-logs';

if (!file_exists($log_dir)) {
wp_mkdir_p($log_dir);
}

$log_file = $log_dir . '/' . date('Y-m-d') . '.log';
$timestamp = date('Y-m-d H:i:s');

error_log("[{$timestamp}] {$message}\n", 3, $log_file);
}

/**
* Add admin menu items
*/
public function add_admin_menu() {
add_submenu_page(
'woocommerce',
__('BDFG Stock Notifier', 'bdfg-stock-notifier'),
__('Stock Notifier', 'bdfg-stock-notifier'),
'manage_woocommerce',
'bdfg-stock-notifier',
array($this, 'render_admin_page')
);

// Add help page
add_submenu_page(
'bdfg-stock-notifier',
__('BDFG Stock Notifier Help', 'bdfg-stock-notifier'),
__('Help & Support', 'bdfg-stock-notifier'),
'manage_woocommerce',
'bdfg-stock-notifier-help',
array($this, 'render_help_page')
);
}

/**
* Add plugin action links
*/
public function add_plugin_action_links($links) {
$plugin_links = array(
'<a href="' . admin_url('admin.php?page=bdfg-stock-notifier') . '">' . __('Settings', 'bdfg-stock-notifier') . '</a>',
);

return array_merge($plugin_links, $links);
}

/**
* Register plugin settings
*/
public function register_settings() {
register_setting('bdfg_stock_notifier_settings', 'bdfg_stock_notifier_settings');

// General settings section
add_settings_section(
'bdfg_stock_notifier_general',
__('General Settings', 'bdfg-stock-notifier'),
array($this, 'render_section_general'),
'bdfg_stock_notifier_settings'
);

add_settings_field(
'enable_stock_notifications',
__('Enable Stock Notifications', 'bdfg-stock-notifier'),
array($this, 'render_field_enable'),
'bdfg_stock_notifier_settings',
'bdfg_stock_notifier_general'
);

add_settings_field(
'enable_price_drop',
__('Enable Price Drop Notifications', 'bdfg-stock-notifier'),
array($this, 'render_field_price_drop'),
'bdfg_stock_notifier_settings',
'bdfg_stock_notifier_general'
);

add_settings_field(
'enable_logs',
__('Enable Logs', 'bdfg-stock-notifier'),
array($this, 'render_field_logs'),
'bdfg_stock_notifier_settings',
'bdfg_stock_notifier_general'
);

add_settings_field(
'remove_branding',
__('Remove Branding', 'bdfg-stock-notifier'),
array($this, 'render_field_branding'),
'bdfg_stock_notifier_settings',
'bdfg_stock_notifier_general'
);

// Display settings section
add_settings_section(
'bdfg_stock_notifier_display',
__('Display Settings', 'bdfg-stock-notifier'),
array($this, 'render_section_display'),
'bdfg_stock_notifier_settings'
);

add_settings_field(
'display_position',
__('Form Position', 'bdfg-stock-notifier'),
array($this, 'render_field_position'),
'bdfg_stock_notifier_settings',
'bdfg_stock_notifier_display'
);

add_settings_field(
'display_priority',
__('Display Priority', 'bdfg-stock-notifier'),
array($this, 'render_field_priority'),
'bdfg_stock_notifier_settings',
'bdfg_stock_notifier_display'
);

add_settings_field(
'button_color',
__('Button Color', 'bdfg-stock-notifier'),
array($this, 'render_field_button_color'),
'bdfg_stock_notifier_settings',
'bdfg_stock_notifier_display'
);

add_settings_field(
'button_text_color',
__('Button Text Color', 'bdfg-stock-notifier'),
array($this, 'render_field_button_text_color'),
'bdfg_stock_notifier_settings',
'bdfg_stock_notifier_display'
);

// Email settings section
add_settings_section(
'bdfg_stock_notifier_email',
__('Email Settings', 'bdfg-stock-notifier'),
array($this, 'render_section_email'),
'bdfg_stock_notifier_settings'
);

add_settings_field(
'notification_email_subject',
__('Email Subject', 'bdfg-stock-notifier'),
array($this, 'render_field_email_subject'),
'bdfg_stock_notifier_settings',
'bdfg_stock_notifier_email'
);

add_settings_field(
'notification_email_template',
__('Email Template', 'bdfg-stock-notifier'),
array($this, 'render_field_email_template'),
'bdfg_stock_notifier_settings',
'bdfg_stock_notifier_email'
);
}

/**
* Render admin page
*/
public function render_admin_page() {
// Handle tab navigation
$active_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'subscribers';

?>
<div class="wrap">
<h1><?php _e('BDFG Stock Notifier for WooCommerce', 'bdfg-stock-notifier'); ?></h1>

<div class="bdfg-admin-header">
<div class="bdfg-admin-logo">
<img src="<?php echo BDFG_STOCK_NOTIFIER_URL . 'assets/img/logo.png'; ?>" alt="BDFG Stock Notifier">
</div>
<div class="bdfg-admin-version">
<?php echo sprintf(__('Version %s', 'bdfg-stock-notifier'), BDFG_STOCK_NOTIFIER_VERSION); ?>
</div>
</div>

<h2 class="nav-tab-wrapper">
<a href="<?php echo admin_url('admin.php?page=bdfg-stock-notifier&tab=subscribers'); ?>"
class="nav-tab <?php echo $active_tab == 'subscribers' ? 'nav-tab-active' : ''; ?>">
<?php _e('Subscribers', 'bdfg-stock-notifier'); ?>
</a>
<a href="<?php echo admin_url('admin.php?page=bdfg-stock-notifier&tab=settings'); ?>"
class="nav-tab <?php echo $active_tab == 'settings' ? 'nav-tab-active' : ''; ?>">
<?php _e('Settings', 'bdfg-stock-notifier'); ?>
</a>
<a href="<?php echo admin_url('admin.php?page=bdfg-stock-notifier&tab=stats'); ?>"
class="nav-tab <?php echo $active_tab == 'stats' ? 'nav-tab-active' : ''; ?>">
<?php _e('Statistics', 'bdfg-stock-notifier'); ?>
</a>
<a href="<?php echo admin_url('admin.php?page=bdfg-stock-notifier-help'); ?>"
class="nav-tab">
<?php _e('Help', 'bdfg-stock-notifier'); ?>
</a>
</h2>

<?php
switch ($active_tab) {
case 'settings':
$this->render_settings_page();
break;
case 'stats':
$this->render_stats_page();
break;
default:
$this->render_subscribers_page();
break;
}
?>
</div>
<?php
}

/**
* Render help page
*/
public function render_help_page() {
?>
<div class="wrap">
<h1><?php _e('BDFG Stock Notifier Help & Support', 'bdfg-stock-notifier'); ?></h1>

<div class="bdfg-admin-header">
<div class="bdfg-admin-logo">
<img src="<?php echo BDFG_STOCK_NOTIFIER_URL . 'assets/img/logo.png'; ?>" alt="BDFG Stock Notifier">
</div>
<div class="bdfg-admin-version">
<?php echo sprintf(__('Version %s', 'bdfg-stock-notifier'), BDFG_STOCK_NOTIFIER_VERSION); ?>
</div>
</div>

<div class="bdfg-help-wrapper">
<div class="bdfg-help-section">
<h2><?php _e('How to Use', 'bdfg-stock-notifier'); ?></h2>
<p><?php _e('The BDFG Stock Notifier plugin automatically adds a subscription form to out-of-stock products on your WooCommerce store. Here\'s how to get the most out of it:', 'bdfg-stock-notifier'); ?></p>

<h3><?php _e('Basic Setup', 'bdfg-stock-notifier'); ?></h3>
<ol>
<li><?php _e('Go to WooCommerce > Stock Notifier > Settings', 'bdfg-stock-notifier'); ?></li>
<li><?php _e('Enable stock notifications', 'bdfg-stock-notifier'); ?></li>
<li><?php _e('Configure email templates and display settings', 'bdfg-stock-notifier'); ?></li>
</ol>

<h3><?php _e('Using Shortcode', 'bdfg-stock-notifier'); ?></h3>
<p><?php _e('You can add the stock notification form anywhere using the shortcode:', 'bdfg-stock-notifier'); ?></p>
<code>[bdfg_stock_notifier product_id="123"]</code>

<p><?php _e('Optional parameters:', 'bdfg-stock-notifier'); ?></p>
<ul>
<li><code>product_id</code> - <?php _e('The ID of the product (required unless used on a product page)', 'bdfg-stock-notifier'); ?></li>
<li><code>variation_id</code> - <?php _e('Specific variation ID for variable products', 'bdfg-stock-notifier'); ?></li>
<li><code>button_text</code> - <?php _e('Custom text for the submit button', 'bdfg-stock-notifier'); ?></li>
<li><code>title</code> - <?php _e('Custom title for the form', 'bdfg-stock-notifier'); ?></li>
</ul>
</div>

<div class="bdfg-help-section">
<h2><?php _e('FAQ', 'bdfg-stock-notifier'); ?></h2>

<div class="bdfg-faq-item">
<h3><?php _e('How do email notifications work?', 'bdfg-stock-notifier'); ?></h3>
<p><?php _e('When a product comes back in stock (status changes from "out of stock" to "in stock"), all customers who subscribed to notifications for that product will receive an email automatically.', 'bdfg-stock-notifier'); ?></p>
</div>

<div class="bdfg-faq-item">
<h3><?php _e('Can I customize the notification email?', 'bdfg-stock-notifier'); ?></h3>
<p><?php _e('Yes! Go to the Settings tab and modify the email subject and template. You can use these placeholders: {product_name}, {product_url}, and {site_name}.', 'bdfg-stock-notifier'); ?></p>
</div>

<div class="bdfg-faq-item">
<h3><?php _e('What is the price drop notification feature?', 'bdfg-stock-notifier'); ?></h3>
<p><?php _e('This allows subscribers to also receive notifications when a product\'s price is reduced, not just when it comes back in stock. Enable this feature in the Settings tab.', 'bdfg-stock-notifier'); ?></p>
</div>

<div class="bdfg-faq-item">
<h3><?php _e('Where can I view my subscribers?', 'bdfg-stock-notifier'); ?></h3>
<p><?php _e('You can view and manage all subscribers from the Subscribers tab.', 'bdfg-stock-notifier'); ?></p>
</div>
</div>

<div class="bdfg-help-section">
<h2><?php _e('Support', 'bdfg-stock-notifier'); ?></h2>
<p><?php _e('If you need assistance with the plugin, please visit our support page:', 'bdfg-stock-notifier'); ?></p>
<p><a href="https://beiduofengou.net/support" target="_blank" class="button button-primary"><?php _e('Get Support', 'bdfg-stock-notifier'); ?></a></p>
</div>
</div>
</div>
<?php
}

/**
* Render settings page
*/
private function render_settings_page() {
?>
<form method="post" action="options.php">
<?php
settings_fields('bdfg_stock_notifier_settings');
do_settings_sections('bdfg_stock_notifier_settings');
submit_button();
?>
</form>
<?php
}

/**
* Render subscribers page with filters and search
*/
private function render_subscribers_page() {
global $wpdb;

// Process bulk actions
if (isset($_POST['bdfg_action']) && isset($_POST['subscription_ids']) && is_array($_POST['subscription_ids'])) {
check_admin_referer('bdfg_bulk_action', 'bdfg_nonce');

$action = sanitize_text_field($_POST['bdfg_action']);
$ids = array_map('absint', $_POST['subscription_ids']);

if (!empty($ids)) {
$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';
$id_list = implode(',', $ids);

if ($action === 'delete') {
$wpdb->query("DELETE FROM $table_name WHERE id IN ($id_list)");
echo '<div class="notice notice-success"><p>' . __('Selected subscriptions deleted successfully.', 'bdfg-stock-notifier') . '</p></div>';
} elseif ($action === 'mark_completed') {
$wpdb->query("UPDATE $table_name SET status = 'completed' WHERE id IN ($id_list)");
echo '<div class="notice notice-success"><p>' . __('Subscriptions marked as completed.', 'bdfg-stock-notifier') . '</p></div>';
} elseif ($action === 'export_csv' && !empty($ids)) {
$this->export_subscribers_csv($ids);
}
}
}

// Handle search and filters
$search = isset($_GET['subscription_search']) ? sanitize_text_field($_GET['subscription_search']) : '';
$status = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : '';
$product_id = isset($_GET['product_id']) ? absint($_GET['product_id']) : 0;

// Prepare pagination
$per_page = 20;
$current_page = isset($_GET['paged']) ? max(1, absint($_GET['paged'])) : 1;
$offset = ($current_page - 1) * $per_page;

// Build query
$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';
$query = "SELECT * FROM $table_name WHERE 1=1";
$count_query = "SELECT COUNT(*) FROM $table_name WHERE 1=1";

$query_args = array();

if (!empty($search)) {
$query .= " AND email LIKE %s";
$count_query .= " AND email LIKE %s";
$query_args[] = '%' . $wpdb->esc_like($search) . '%';
}

if (!empty($status)) {
$query .= " AND status = %s";
$count_query .= " AND status = %s";
$query_args[] = $status;
}

if (!empty($product_id)) {
$query .= " AND product_id = %d";
$count_query .= " AND product_id = %d";
$query_args[] = $product_id;
}

// Add order and limit
$query .= " ORDER BY date_created DESC LIMIT %d OFFSET %d";
$query_args[] = $per_page;
$query_args[] = $offset;

// Execute queries
$total_items = $wpdb->get_var($wpdb->prepare($count_query, $query_args));
$subscriptions = $wpdb->get_results($wpdb->prepare($query, $query_args));

$total_pages = ceil($total_items / $per_page);

// Display subscribers list
?>
<div class="bdfg-subscribers-wrapper">
<div class="bdfg-subscribers-filters">
<form method="get">
<input type="hidden" name="page" value="bdfg-stock-notifier">
<input type="hidden" name="tab" value="subscribers">

<div class="bdfg-filters-row">
<div class="bdfg-filter-item">
<input type="text" name="subscription_search" placeholder="<?php _e('Search email...', 'bdfg-stock-notifier'); ?>" value="<?php echo esc_attr($search); ?>">
</div>

<div class="bdfg-filter-item">
<select name="status">
<option value=""><?php _e('All Statuses', 'bdfg-stock-notifier'); ?></option>
<option value="pending" <?php selected($status, 'pending'); ?>><?php _e('Pending', 'bdfg-stock-notifier'); ?></option>
<option value="completed" <?php selected($status, 'completed'); ?>><?php _e('Completed', 'bdfg-stock-notifier'); ?></option>
<option value="failed" <?php selected($status, 'failed'); ?>><?php _e('Failed', 'bdfg-stock-notifier'); ?></option>
</select>
</div>

<div class="bdfg-filter-item">
<input type="submit" class="button" value="<?php _e('Filter', 'bdfg-stock-notifier'); ?>">
<?php if (!empty($search) || !empty($status) || !empty($product_id)): ?>
<a href="<?php echo admin_url('admin.php?page=bdfg-stock-notifier&tab=subscribers'); ?>" class="button"><?php _e('Reset', 'bdfg-stock-notifier'); ?></a>
<?php endif; ?>
</div>
</div>
</form>
</div>

<div class="bdfg-subscribers-actions">
<div class="tablenav top">
<div class="alignleft actions">
<form method="post">
<?php wp_nonce_field('bdfg_bulk_action', 'bdfg_nonce'); ?>
<select name="bdfg_action">
<option value=""><?php _e('Bulk Actions', 'bdfg-stock-notifier'); ?></option>
<option value="delete"><?php _e('Delete', 'bdfg-stock-notifier'); ?></option>
<option value="mark_completed"><?php _e('Mark as Completed', 'bdfg-stock-notifier'); ?></option>
<option value="export_csv"><?php _e('Export as CSV', 'bdfg-stock-notifier'); ?></option>
</select>
<input type="submit" class="button action" value="<?php _e('Apply', 'bdfg-stock-notifier'); ?>">
</form>
</div>

<div class="tablenav-pages">
<?php if ($total_pages > 1): ?>
<span class="displaying-num"><?php echo sprintf(_n('%s item', '%s items', $total_items, 'bdfg-stock-notifier'), number_format_i18n($total_items)); ?></span>
<?php
$paginate_args = array(
'base' => add_query_arg('paged', '%#%'),
'format' => '',
'prev_text' => '&laquo;',
'next_text' => '&raquo;',
'total' => $total_pages,
'current' => $current_page,
'add_args' => array(
'subscription_search' => $search,
'status' => $status,
'product_id' => $product_id,
)
);
echo paginate_links($paginate_args);
?>
<?php endif; ?>
</div>
</div>
</div>

<form method="post">
<?php wp_nonce_field('bdfg_bulk_action', 'bdfg_nonce'); ?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th scope="col" class="manage-column column-cb check-column">
<input type="checkbox" id="cb-select-all-1">
</th>
<th scope="col" class="manage-column"><?php _e('Email', 'bdfg-stock-notifier'); ?></th>
<th scope="col" class="manage-column"><?php _e('Product', 'bdfg-stock-notifier'); ?></th>
<th scope="col" class="manage-column"><?php _e('Date', 'bdfg-stock-notifier'); ?></th>
<th scope="col" class="manage-column"><?php _e('Status', 'bdfg-stock-notifier'); ?></th>
<th scope="col" class="manage-column"><?php _e('Price Drop', 'bdfg-stock-notifier'); ?></th>
</tr>
</thead>

<tbody>
<?php if (empty($subscriptions)): ?>
<tr>
<td colspan="6"><?php _e('No subscriptions found.', 'bdfg-stock-notifier'); ?></td>
</tr>
<?php else: ?>
<?php foreach ($subscriptions as $subscription): ?>
<tr>
<th scope="row" class="check-column">
<input type="checkbox" name="subscription_ids[]" value="<?php echo esc_attr($subscription->id); ?>">
</th>
<td>
<?php echo esc_html($subscription->email); ?>
<?php if ($subscription->user_id > 0): ?>
<span class="bdfg-user-badge"><?php _e('Registered User', 'bdfg-stock-notifier'); ?></span>
<?php endif; ?>
</td>
<td>
<?php
$product = wc_get_product($subscription->product_id);
if ($product) {
$product_name = $product->get_name();

if ($subscription->variation_id > 0) {
$variation = wc_get_product($subscription->variation_id);
if ($variation) {
$product_name .= ' - ' . implode(', ', $variation->get_variation_attributes());
}
}

echo '<a href="' . esc_url(get_edit_post_link($subscription->product_id)) . '">' . esc_html($product_name) . '</a>';
} else {
echo __('Product not found', 'bdfg-stock-notifier');
}
?>
</td>
<td><?php echo date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($subscription->date_created)); ?></td>
<td>
<?php
$status_labels = array(
'pending' => __('Pending', 'bdfg-stock-notifier'),
'completed' => __('Completed', 'bdfg-stock-notifier'),
'failed' => __('Failed', 'bdfg-stock-notifier')
);

$status_class = 'bdfg-status-' . $subscription->status;
echo '<span class="bdfg-status ' . esc_attr($status_class) . '">' . (isset($status_labels[$subscription->status]) ? $status_labels[$subscription->status] : $subscription->status) . '</span>';
?>
</td>
<td>
<?php echo $subscription->notify_price_drop ? __('Yes', 'bdfg-stock-notifier') : __('No', 'bdfg-stock-notifier'); ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</form>

<div class="tablenav bottom">
<?php if ($total_pages > 1): ?>
<div class="tablenav-pages">
<span class="displaying-num"><?php echo sprintf(_n('%s item', '%s items', $total_items, 'bdfg-stock-notifier'), number_format_i18n($total_items)); ?></span>
<?php echo paginate_links($paginate_args); ?>
</div>
<?php endif; ?>
</div>
</div>
<?php
}

/**
* Export subscribers to CSV
*/
private function export_subscribers_csv($ids) {
global $wpdb;

$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';
$id_list = implode(',', array_map('absint', $ids));

$subscriptions = $wpdb->get_results("SELECT * FROM $table_name WHERE id IN ($id_list) ORDER BY date_created DESC");

if (empty($subscriptions)) {
return;
}

// Set headers for CSV download
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=bdfg-stock-notifier-export-' . date('Y-m-d') . '.csv');

// Create a file pointer
$output = fopen('php://output', 'w');

// Set column headers
fputcsv($output, array(
__('ID', 'bdfg-stock-notifier'),
__('Email', 'bdfg-stock-notifier'),
__('Product ID', 'bdfg-stock-notifier'),
__('Product Name', 'bdfg-stock-notifier'),
__('Variation ID', 'bdfg-stock-notifier'),
__('Date Created', 'bdfg-stock-notifier'),
__('Status', 'bdfg-stock-notifier'),
__('User ID', 'bdfg-stock-notifier'),
__('Price Drop Notifications', 'bdfg-stock-notifier')
));

// Output each subscription as a CSV line
foreach ($subscriptions as $subscription) {
$product = wc_get_product($subscription->product_id);
$product_name = $product ? $product->get_name() : __('Product not found', 'bdfg-stock-notifier');

fputcsv($output, array(
$subscription->id,
$subscription->email,
$subscription->product_id,
$product_name,
$subscription->variation_id,
$subscription->date_created,
$subscription->status,
$subscription->user_id,
$subscription->notify_price_drop ? __('Yes', 'bdfg-stock-notifier') : __('No', 'bdfg-stock-notifier')
));
}

exit;
}

/**
* Render statistics page
*/
private function render_stats_page() {
global $wpdb;

$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';

// Get basic statistics
$total_subscribers = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
$pending_notifications = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'pending'");
$completed_notifications = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'completed'");
$failed_notifications = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'failed'");

// Get top subscribed products
$top_products = $wpdb->get_results(
"SELECT product_id, COUNT(*) as subscription_count
FROM $table_name
GROUP BY product_id
ORDER BY subscription_count DESC
LIMIT 10"
);

// Get recent activity
$recent_activity = $wpdb->get_results(
"SELECT * FROM $table_name
ORDER BY date_created DESC
LIMIT 15"
);

// Get monthly stats
$monthly_stats = $wpdb->get_results(
"SELECT DATE_FORMAT(date_created, '%Y-%m') as month, COUNT(*) as count
FROM $table_name
GROUP BY month
ORDER BY month DESC
LIMIT 12"
);

?>
<div class="bdfg-stats-wrapper">
<div class="bdfg-stats-header">
<h2><?php _e('Stock Notification Statistics', 'bdfg-stock-notifier'); ?></h2>
<p><?php _e('Overview of your stock notification subscriptions and activity.', 'bdfg-stock-notifier'); ?></p>
</div>

<div class="bdfg-stats-summary">
<div class="bdfg-stats-card">
<h3><?php _e('Total Subscribers', 'bdfg-stock-notifier'); ?></h3>
<div class="bdfg-stats-number"><?php echo number_format_i18n($total_subscribers); ?></div>
</div>

<div class="bdfg-stats-card">
<h3><?php _e('Pending Notifications', 'bdfg-stock-notifier'); ?></h3>
<div class="bdfg-stats-number"><?php echo number_format_i18n($pending_notifications); ?></div>
</div>

<div class="bdfg-stats-card">
<h3><?php _e('Sent Notifications', 'bdfg-stock-notifier'); ?></h3>
<div class="bdfg-stats-number"><?php echo number_format_i18n($completed_notifications); ?></div>
</div>

<div class="bdfg-stats-card">
<h3><?php _e('Failed Notifications', 'bdfg-stock-notifier'); ?></h3>
<div class="bdfg-stats-number"><?php echo number_format_i18n($failed_notifications); ?></div>
</div>
</div>

<div class="bdfg-stats-content">
<div class="bdfg-stats-column">
<h3><?php _e('Most Wanted Products', 'bdfg-stock-notifier'); ?></h3>

<table class="widefat">
<thead>
<tr>
<th><?php _e('Product', 'bdfg-stock-notifier'); ?></th>
<th><?php _e('Subscriptions', 'bdfg-stock-notifier'); ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($top_products)): ?>
<tr>
<td colspan="2"><?php _e('No data available', 'bdfg-stock-notifier'); ?></td>
</tr>
<?php else: ?>
<?php foreach ($top_products as $product_stats): ?>
<tr>
<td>
<?php
$product = wc_get_product($product_stats->product_id);
if ($product) {
echo '<a href="' . esc_url(get_edit_post_link($product_stats->product_id)) . '">' . esc_html($product->get_name()) . '</a>';
} else {
echo __('Product not found', 'bdfg-stock-notifier') . ' (ID: ' . $product_stats->product_id . ')';
}
?>
</td>
<td><?php echo number_format_i18n($product_stats->subscription_count); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>

<div class="bdfg-stats-column">
<h3><?php _e('Monthly Subscriptions', 'bdfg-stock-notifier'); ?></h3>

<table class="widefat">
<thead>
<tr>
<th><?php _e('Month', 'bdfg-stock-notifier'); ?></th>
<th><?php _e('Subscriptions', 'bdfg-stock-notifier'); ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($monthly_stats)): ?>
<tr>
<td colspan="2"><?php _e('No data available', 'bdfg-stock-notifier'); ?></td>
</tr>
<?php else: ?>
<?php foreach ($monthly_stats as $month_stat): ?>
<tr>
<td>
<?php
$date = new DateTime($month_stat->month . '-01');
echo $date->format('F Y');
?>
</td>
<td><?php echo number_format_i18n($month_stat->count); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php
}

/**
* Register REST API endpoints
*/
public function register_rest_endpoints() {
register_rest_route('bdfg-stock-notifier/v1', '/subscriptions', array(
'methods' => 'GET',
'callback' => array($this, 'api_get_subscriptions'),
'permission_callback' => function() {
return current_user_can('manage_woocommerce');
}
));

register_rest_route('bdfg-stock-notifier/v1', '/stats', array(
'methods' => 'GET',
'callback' => array($this, 'api_get_stats'),
'permission_callback' => function() {
return current_user_can('manage_woocommerce');
}
));
}

/**
* API endpoint to get subscriptions
*/
public function api_get_subscriptions($request) {
global $wpdb;

$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';
$per_page = isset($request['per_page']) ? absint($request['per_page']) : 20;
$page = isset($request['page']) ? absint($request['page']) : 1;
$offset = ($page - 1) * $per_page;

$subscriptions = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $table_name ORDER BY date_created DESC LIMIT %d OFFSET %d",
$per_page, $offset
));

$total_items = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");

foreach ($subscriptions as &$subscription) {
$product = wc_get_product($subscription->product_id);
$subscription->product_name = $product ? $product->get_name() : __('Product not found', 'bdfg-stock-notifier');

if ($subscription->variation_id > 0) {
$variation = wc_get_product($subscription->variation_id);
if ($variation) {
$subscription->variation_name = implode(', ', $variation->get_variation_attributes());
}
}
}

return array(
'subscriptions' => $subscriptions,
'total' => (int) $total_items,
'pages' => ceil($total_items / $per_page)
);
}

/**
* API endpoint to get stats
*/
public function api_get_stats() {
global $wpdb;

$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';

$stats = array(
'total' => $wpdb->get_var("SELECT COUNT(*) FROM $table_name"),
'pending' => $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'pending'"),
'completed' => $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'completed'"),
'failed' => $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'failed'"),
);

return $stats;
}

/**
* Add endpoint for My Account area
*/
public function add_account_endpoint($query_vars) {
$query_vars['stock-notifications'] = 'stock-notifications';
return $query_vars;
}

/**
* Add menu item to My Account menu
*/
public function add_account_menu_item($items) {
// Insert after orders
$new_items = array();

foreach ($items as $key => $value) {
$new_items[$key] = $value;

if ($key === 'orders') {
$new_items['stock-notifications'] = __('Stock Notifications', 'bdfg-stock-notifier');
}
}

return $new_items;
}

/**
* Content for My Account stock notifications page
*/
public function account_stock_notifications_content() {
global $wpdb;

$user_id = get_current_user_id();
$user = get_user_by('id', $user_id);

if (!$user) {
return;
}

$table_name = $wpdb->prefix . 'bdfg_stock_subscriptions';

// Process unsubscribe requests
if (isset($_POST['bdfg_unsubscribe']) && isset($_POST['subscription_id']) && wp_verify_nonce($_POST['bdfg_account_nonce'], 'bdfg_unsubscribe')) {
$subscription_id = absint($_POST['subscription_id']);

// Verify this subscription belongs to the user
$subscription = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $table_name WHERE id = %d AND (email = %s OR user_id = %d)",
$subscription_id, $user->user_email, $user_id
));

if ($subscription) {
$wpdb->delete(
$table_name,
array('id' => $subscription_id),
array('%d')
);

echo '<div class="woocommerce-message">' . __('You have been unsubscribed successfully.', 'bdfg-stock-notifier') . '</div>';
}
}

// Get user's subscriptions
$subscriptions = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $table_name WHERE email = %s OR user_id = %d ORDER BY date_created DESC",
$user->user_email, $user_id
));

?>
<h2><?php _e('My Stock Notifications', 'bdfg-stock-notifier'); ?></h2>

<?php if (empty($subscriptions)): ?>
<p><?php _e('You are not currently subscribed to any out-of-stock product notifications.', 'bdfg-stock-notifier'); ?></p>
<?php else: ?>
<p><?php _e('You will receive an email notification when these products are back in stock.', 'bdfg-stock-notifier'); ?></p>

<table class="woocommerce-orders-table woocommerce-MyAccount-orders shop_table shop_table_responsive">
<thead>
<tr>
<th><?php _e('Product', 'bdfg-stock-notifier'); ?></th>
<th><?php _e('Date', 'bdfg-stock-notifier'); ?></th>
<th><?php _e('Status', 'bdfg-stock-notifier'); ?></th>
<th><?php _e('Actions', 'bdfg-stock-notifier'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($subscriptions as $subscription): ?>
<tr>
<td>
<?php
$product = wc_get_product($subscription->product_id);
if ($product) {
$product_name = $product->get_name();

if ($subscription->variation_id > 0) {
$variation = wc_get_product($subscription->variation_id);
if ($variation) {
$product_name .= ' - ' . implode(', ', $variation->get_variation_attributes());
}
}

echo '<a href="' . esc_url(get_permalink($subscription->product_id)) . '">' . esc_html($product_name) . '</a>';
} else {
echo __('Product no longer available', 'bdfg-stock-notifier');
}
?>
</td>
<td>
<?php echo date_i18n(get_option('date_format'), strtotime($subscription->date_created)); ?>
</td>
<td>
<?php
$status_labels = array(
'pending' => __('Waiting for stock', 'bdfg-stock-notifier'),
'completed' => __('Notification sent', 'bdfg-stock-notifier'),
'failed' => __('Failed', 'bdfg-stock-notifier')
);

echo isset($status_labels[$subscription->status]) ? $status_labels[$subscription->status] : $subscription->status;
?>
</td>
<td>
<form method="post">
<?php wp_nonce_field('bdfg_unsubscribe', 'bdfg_account_nonce'); ?>
<input type="hidden" name="subscription_id" value="<?php echo esc_attr($subscription->id); ?>">
<button type="submit" name="bdfg_unsubscribe" class="button"><?php _e('Unsubscribe', 'bdfg-stock-notifier'); ?></button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php
}

/**
* Settings section renderers
*/
public function render_section_general() {
echo '<p>' . __('Configure the general settings for stock notifications.', 'bdfg-stock-notifier') . '</p>';
}

public function render_section_display() {
echo '<p>' . __('Configure how the notification form appears on product pages.', 'bdfg-stock-notifier') . '</p>';
}

public function render_section_email() {
echo '<p>' . __('Configure the email template for stock notifications.', 'bdfg-stock-notifier') . '</p>';
echo '<p>' . __('Available placeholders: {product_name}, {product_url}, {site_name}', 'bdfg-stock-notifier') . '</p>';
}

/**
* Settings field renderers
*/
public function render_field_enable() {
$options = get_option('bdfg_stock_notifier_settings');
$value = isset($options['enable_stock_notifications']) ? $options['enable_stock_notifications'] : 'yes';
?>
<select name="bdfg_stock_notifier_settings[enable_stock_notifications]">
<option value="yes" <?php selected($value, 'yes'); ?>><?php _e('Yes', 'bdfg-stock-notifier'); ?></option>
<option value="no" <?php selected($value, 'no'); ?>><?php _e('No', 'bdfg-stock-notifier'); ?></option>
</select>
<p class="description"><?php _e('Enable or disable stock notification functionality.', 'bdfg-stock-notifier'); ?></p>
<?php
}

public function render_field_price_drop() {
$options = get_option('bdfg_stock_notifier_settings');
$value = isset($options['enable_price_drop']) ? $options['enable_price_drop'] : 'no';
?>
<select name="bdfg_stock_notifier_settings[enable_price_drop]">
<option value="yes" <?php selected($value, 'yes'); ?>><?php _e('Yes', 'bdfg-stock-notifier'); ?></option>
<option value="no" <?php selected($value, 'no'); ?>><?php _e('No', 'bdfg-stock-notifier'); ?></option>
</select>
<p class="description"><?php _e('Allow customers to receive notifications when product prices drop.', 'bdfg-stock-notifier'); ?></p>
<?php
}

public function render_field_logs() {
$options = get_option('bdfg_stock_notifier_settings');
$value = isset($options['enable_logs']) ? $options['enable_logs'] : 'no';
?>
<select name="bdfg_stock_notifier_settings[enable_logs]">
<option value="yes" <?php selected($value, 'yes'); ?>><?php _e('Yes', 'bdfg-stock-notifier'); ?></option>
<option value="no" <?php selected($value, 'no'); ?>><?php _e('No', 'bdfg-stock-notifier'); ?></option>
</select>
<p class="description">
<?php _e('Enable logging of subscription and notification events.', 'bdfg-stock-notifier'); ?>
<?php
$upload_dir = wp_upload_dir();
$logs_dir = trailingslashit($upload_dir['basedir']) . 'bdfg-stock-notifier-logs';
echo sprintf(__('Logs are stored in: %s', 'bdfg-stock-notifier'), '<code>' . $logs_dir . '</code>');
?>
</p>
<?php
}

public function render_field_branding() {
$options = get_option('bdfg_stock_notifier_settings');
$value = isset($options['remove_branding']) ? $options['remove_branding'] : 'no';
?>
<select name="bdfg_stock_notifier_settings[remove_branding]">
<option value="no" <?php selected($value, 'no'); ?>><?php _e('Show branding', 'bdfg-stock-notifier'); ?></option>
<option value="yes" <?php selected($value, 'yes'); ?>><?php _e('Remove branding', 'bdfg-stock-notifier'); ?></option>
</select>
<p class="description"><?php _e('Show or hide "Powered by BDFG Stock Notifier" text on the notification form.', 'bdfg-stock-notifier'); ?></p>
<?php
}

public function render_field_position() {
$options = get_option('bdfg_stock_notifier_settings');
$value = isset($options['display_position']) ? $options['display_position'] : 'woocommerce_single_product_summary';
?>
<select name="bdfg_stock_notifier_settings[display_position]">
<option value="woocommerce_single_product_summary" <?php selected($value, 'woocommerce_single_product_summary'); ?>><?php _e('After product summary', 'bdfg-stock-notifier'); ?></option>
<option value="woocommerce_product_meta_end" <?php selected($value, 'woocommerce_product_meta_end'); ?>><?php _e('After product meta', 'bdfg-stock-notifier'); ?></option>
<option value="woocommerce_after_add_to_cart_form" <?php selected($value, 'woocommerce_after_add_to_cart_form'); ?>><?php _e('After add to cart form', 'bdfg-stock-notifier'); ?></option>
<option value="woocommerce_after_single_product_summary" <?php selected($value, 'woocommerce_after_single_product_summary'); ?>><?php _e('After product', 'bdfg-stock-notifier'); ?></option>
<option value="woocommerce_before_add_to_cart_form" <?php selected($value, 'woocommerce_before_add_to_cart_form'); ?>><?php _e('Before add to cart form', 'bdfg-stock-notifier'); ?></option>
</select>
<p class="description"><?php _e('Choose where to display the notification form on product pages.', 'bdfg-stock-notifier'); ?></p>
<?php
}

public function render_field_priority() {
$options = get_option('bdfg_stock_notifier_settings');
$value = isset($options['display_priority']) ? absint($options['display_priority']) : 30;
?>
<input type="number" name="bdfg_stock_notifier_settings[display_priority]" value="<?php echo esc_attr($value); ?>" min="1" max="100" step="1">
<p class="description"><?php _e('Display priority within the selected position (lower numbers show first).', 'bdfg-stock-notifier'); ?></p>
<?php
}

public function render_field_button_color() {
$options = get_option('bdfg_stock_notifier_settings');
$value = isset($options['button_color']) ? $options['button_color'] : '#0066cc';
?>
<input type="color" name="bdfg_stock_notifier_settings[button_color]" value="<?php echo esc_attr($value); ?>">
<p class="description"><?php _e('Choose the notification button color.', 'bdfg-stock-notifier'); ?></p>
<?php
}

public function render_field_button_text_color() {
$options = get_option('bdfg_stock_notifier_settings');
$value = isset($options['button_text_color']) ? $options['button_text_color'] : '#ffffff';
?>
<input type="color" name="bdfg_stock_notifier_settings[button_text_color]" value="<?php echo esc_attr($value); ?>">
<p class="description"><?php _e('Choose the notification button text color.', 'bdfg-stock-notifier'); ?></p>
<?php
}

public function render_field_email_subject() {
$options = get_option('bdfg_stock_notifier_settings');
$value = isset($options['notification_email_subject']) ? $options['notification_email_subject'] : __('Product Back in Stock - {product_name}', 'bdfg-stock-notifier');
?>
<input type="text" name="bdfg_stock_notifier_settings[notification_email_subject]" value="<?php echo esc_attr($value); ?>" class="regular-text">
<p class="description"><?php _e('The email subject line. Placeholders: {product_name}, {site_name}', 'bdfg-stock-notifier'); ?></p>
<?php
}

public function render_field_email_template() {
$options = get_option('bdfg_stock_notifier_settings');
$value = isset($options['notification_email_template']) ? $options['notification_email_template'] : __("Hello,\n\nGood news! The product you were interested in is back in stock:\n\n{product_name}\n\nYou can purchase it now at: {product_url}\n\nBest regards,\n{site_name}", 'bdfg-stock-notifier');
?>
<textarea name="bdfg_stock_notifier_settings[notification_email_template]" rows="10" class="large-text"><?php echo esc_textarea($value); ?></textarea>
<p class="description"><?php _e('The email message template. Placeholders: {product_name}, {product_url}, {site_name}', 'bdfg-stock-notifier'); ?></p>
<?php
}
}

// Initialize plugin
function bdfg_stock_notifier_init() {
// Check if WooCommerce is already installed
if (bdfg_stock_notifier_check_woocommerce()) {
BDFG_Stock_Notifier::instance();
}
}
add_action('plugins_loaded', 'bdfg_stock_notifier_init');

// Register activation hook for endpoint creation
function bdfg_stock_notifier_add_endpoint() {
add_rewrite_endpoint('stock-notifications', EP_ROOT | EP_PAGES);
flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'bdfg_stock_notifier_add_endpoint');

// Load translation file from languages directory
function bdfg_stock_notifier_load_plugin_textdomain() {
load_plugin_textdomain('bdfg-stock-notifier', false, dirname(plugin_basename(__FILE__)) . '/languages/');
}
add_action('plugins_loaded', 'bdfg_stock_notifier_load_plugin_textdomain');

assets/css/frontend.css

/**
* BDFG Stock Notifier for WooCommerce - Frontend Styles
*
* @package BDFG_Stock_Notifier
* @version 2.4.0
*/

.bdfg-stock-notification-container {
margin: 20px 0;
padding: 15px;
border: 1px solid #e5e5e5;
background-color: #f8f8f8;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}

.bdfg-stock-notification-container:hover {
border-color: #d5d5d5;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

.bdfg-stock-notification-container h4 {
margin-top: 0;
margin-bottom: 10px;
font-size: 18px;
font-weight: 600;
color: #333;
}

.bdfg-stock-notification-form {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 15px;
}

.bdfg-stock-notification-email {
flex: 1;
min-width: 250px;
padding: 10px 15px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
height: 42px;
line-height: 1.2;
}

.bdfg-stock-notification-submit {
cursor: pointer;
padding: 10px 20px;
font-weight: 600;
border-radius: 4px;
border: none;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.2s ease;
}

.bdfg-subscription-response {
width: 100%;
font-size: 14px;
margin: 5px 0;
padding: 5px 0;
font-weight: 500;
}

.bdfg-subscription-response.success {
color: #3c763d;
}

.bdfg-subscription-response.error {
color: #a94442;
}

.bdfg-price-drop-option {
width: 100%;
margin: 5px 0;
display: flex;
align-items: center;
}

.bdfg-price-drop-option label {
display: flex;
align-items: center;
font-size: 14px;
cursor: pointer;
}

.bdfg-price-drop-option input[type="checkbox"] {
margin-right: 5px;
}

.bdfg-notifier-branding {
width: 100%;
text-align: right;
font-size: 11px;
margin-top: 10px;
opacity: 0.7;
}

.bdfg-notifier-branding a {
color: inherit;
text-decoration: none;
}

.bdfg-notifier-branding a:hover {
text-decoration: underline;
}

/* Account Page Styles */
.woocommerce-account .bdfg-status {
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
display: inline-block;
}

.woocommerce-account .bdfg-status-pending {
background: #f8ecd1;
color: #d19919;
}

.woocommerce-account .bdfg-status-completed {
background: #e0f5e9;
color: #2e9a61;
}

.woocommerce-account .bdfg-status-failed {
background: #fce8e8;
color: #d93025;
}

/* Responsive styles */
@media (max-width: 768px) {
.bdfg-stock-notification-form {
flex-direction: column;
}

.bdfg-stock-notification-email,
.bdfg-stock-notification-submit {
width: 100%;
}
}

assets/js/frontend.js

相关文章: WooCommerce 产品过滤器

/**
* BDFG Stock Notifier for WooCommerce - Frontend Scripts
*
* @package BDFG_Stock_Notifier
* @version 2.4.0
* @author beiduofengou
*/

jQuery(document).ready(function($) {

// Stock notification form submission
$('.bdfg-stock-notification-form').on('submit', function(e) {
e.preventDefault();

var form = $(this);
var emailField = form.find('.bdfg-stock-notification-email');
var responseDiv = form.find('.bdfg-subscription-response');
var submitButton = form.find('.bdfg-stock-notification-submit');

// Validate email
var email = emailField.val().trim();
var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

if (!emailRegex.test(email)) {
responseDiv.text(bdfgStockNotifier.i18n.email_invalid)
.removeClass('success')
.addClass('error')
.show();
return;
}

// Get product info
var productId = form.data('product-id');
var productType = form.data('product-type');
var variationId = 0;

// For variable products, get the selected variation ID
if (productType === 'variable') {
variationId = $('input[name="variation_id"]').val();
if (!variationId || variationId == 0) {
responseDiv.text(bdfgStockNotifier.i18n.select_options)
.removeClass('success')
.addClass('error')
.show();
return;
}
}

// Get price drop notification preference
var notifyPriceDrop = form.find('input[name="bdfg_notify_price_drop"]').is(':checked') ? 1 : 0;

// Disable submit button and show loading state
submitButton.prop('disabled', true).text('...');
responseDiv.removeClass('success error').empty();

// Send AJAX request
$.ajax({
url: bdfgStockNotifier.ajaxUrl,
type: 'POST',
data: {
action: 'bdfg_stock_notifier_subscribe',
email: email,
product_id: productId,
variation_id: variationId,
notify_price_drop: notifyPriceDrop,
nonce: bdfgStockNotifier.nonce
},
success: function(response) {
if (response.success) {
responseDiv.text(response.data.message)
.removeClass('error')
.addClass('success')
.show();

// Update button text and disable inputs
submitButton.text(bdfgStockNotifier.i18n.subscribed).prop('disabled', true);
emailField.prop('disabled', true);
form.find('input[name="bdfg_notify_price_drop"]').prop('disabled', true);

// Track event if Google Analytics exists
if (typeof ga === 'function') {
ga('send', {
hitType: 'event',
eventCategory: 'Stock Notifications',
eventAction: 'Subscribe',
eventLabel: 'Product: ' + productId
});
}
} else {
responseDiv.text(response.data.message)
.removeClass('success')
.addClass('error')
.show();

// If product is already in stock, refresh page
if (response.data && response.data.in_stock) {
setTimeout(function() {
window.location.reload();
}, 1500);
}

submitButton.prop('disabled', false).text(bdfgStockNotifier.i18n.subscribe);
}
},
error: function() {
responseDiv.text(bdfgStockNotifier.i18n.error)
.removeClass('success')
.addClass('error')
.show();
submitButton.prop('disabled', false).text(bdfgStockNotifier.i18n.subscribe);
}
});
});

// For variable products, listen for variation selection changes
$(document).on('found_variation', function(event, variation) {
var container = $('.bdfg-stock-notification-container');

if (variation.is_in_stock) {
container.slideUp(200);
} else {
container.slideDown(200);
}
});

$(document).on('reset_data', function() {
var container = $('.bdfg-stock-notification-container');

// Reset form and display
container.find('.bdfg-subscription-response').empty();
container.find('.bdfg-stock-notification-email').prop('disabled', false).val('');
container.find('input[name="bdfg_notify_price_drop"]').prop('disabled', false).prop('checked', true);
container.find('.bdfg-stock-notification-submit').prop('disabled', false).text(bdfgStockNotifier.i18n.subscribe);
});

// Improve usability with keyboard navigation
$('.bdfg-stock-notification-email').on('keypress', function(e) {
if (e.which === 13) { // Enter key
e.preventDefault();
$(this).closest('form').submit();
}
});
});

assets/css/admin.css

/**
* BDFG Stock Notifier for WooCommerce - Admin Styles
*
* @package BDFG_Stock_Notifier
* @version 2.4.0
*/

.bdfg-admin-header {
display: flex;
align-items: center;
margin: 20px 0;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
}

.bdfg-admin-logo {
margin-right: 15px;
}

.bdfg-admin-logo img {
max-width: 200px;
height: auto;
}

.bdfg-admin-version {
background: #f0f0f0;
padding: 3px 8px;
border-radius: 4px;
font-size: 12px;
}

/* Subscribers table styles */
.bdfg-subscribers-wrapper {
margin-top: 20px;
}

.bdfg-subscribers-filters {
margin-bottom: 20px;
padding: 15px;
background: #fff;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
}

.bdfg-filters-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: flex-end;
}

.bdfg-filter-item {
flex: 1;
min-width: 200px;
max-width: 300px;
}

.bdfg-filter-item input[type="text"],
.bdfg-filter-item select {
width: 100%;
}

/* Status indicators */
.bdfg-status {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
line-height: 1.4;
}

.bdfg-status-pending {
background-color: #f8ecd1;
color: #bc8c12;
}

.bdfg-status-completed {
background-color: #e0f5e9;
color: #2e9a61;
}

.bdfg-status-failed {
background-color: #fce8e8;
color: #d93025;
}

.bdfg-user-badge {
display: inline-block;
margin-left: 5px;
padding: 1px 5px;
font-size: 11px;
background: #e2e2e2;
border-radius: 3px;
vertical-align: middle;
}

/* Statistics page styles */
.bdfg-stats-wrapper {
margin-top: 20px;
}

.bdfg-stats-header {
margin-bottom: 20px;
}

.bdfg-stats-summary {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-bottom: 30px;
}

.bdfg-stats-card {
flex: 1;
min-width: 200px;
padding: 20px;
background: white;
border: 1px solid #e5e5e5;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
text-align: center;
}

.bdfg-stats-card h3 {
margin-top: 0;
margin-bottom: 15px;
font-size: 16px;
color: #666;
}

.bdfg-stats-number {
font-size: 32px;
font-weight: 700;
color: #333;
}

.bdfg-stats-content {
display: flex;
flex-wrap: wrap;
gap: 30px;
}

.bdfg-stats-column {
flex: 1;
min-width: 300px;
}

.bdfg-stats-column h3 {
margin-top: 0;
margin-bottom: 15px;
}

/* Help page styles */
.bdfg-help-wrapper {
margin-top: 20px;
}

.bdfg-help-section {
margin-bottom: 30px;
padding: 20px;
background: white;
border: 1px solid #e5e5e5;
border-radius: 4px;
}

.bdfg-help-section h2 {
margin-top: 0;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}

.bdfg-faq-item {
margin-bottom: 20px;
}

.bdfg-faq-item h3 {
font-size: 16px;
margin-bottom: 5px;
}

/* Responsive fixes */
@media (max-width: 782px) {
.bdfg-stats-card {
min-width: 100%;
}

.bdfg-stats-column {
min-width: 100%;
}
}

 

相关文章: WooCommerce 客户分析报告插件

Leave a Comment