PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.49
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.49
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / admin / layouts / wishlist-analytics.php

wishlist-analytics.php in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.49, at includes/admin/layouts/wishlist-analytics.php

369 lines 17.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Wishlist Analytics page - V3 Premium style inspired design.
4 *
5 * @package King_Addons
6 */
7
8 use King_Addons\Wishlist\Wishlist_Service;
9
10 if (!defined('ABSPATH')) {
11 exit;
12 }
13
14 // Theme mode is per-user
15 $theme_mode = get_user_meta(get_current_user_id(), 'king_addons_theme_mode', true);
16 $allowed_theme_modes = ['dark', 'light', 'auto'];
17 if (!in_array($theme_mode, $allowed_theme_modes, true)) {
18 $theme_mode = 'dark';
19 }
20
21 $is_pro = function_exists('king_addons_freemius') && king_addons_freemius()->can_use_premium_code__premium_only();
22
23 // Get date filter values
24 $date_from = isset($_GET['date_from']) ? sanitize_text_field(wp_unslash($_GET['date_from'])) : '';
25 $date_to = isset($_GET['date_to']) ? sanitize_text_field(wp_unslash($_GET['date_to'])) : '';
26
27 // Validate dates
28 if ($date_from && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_from)) {
29 $date_from = '';
30 }
31 if ($date_to && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_to)) {
32 $date_to = '';
33 }
34
35 // Handle CSV export
36 if ($is_pro && isset($_GET['action']) && 'export_csv' === $_GET['action']) {
37 if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'] ?? '')), 'king_addons_wishlist_export')) {
38 wp_die(esc_html__('Invalid request.', 'king-addons'));
39 }
40
41 $service = new Wishlist_Service();
42 $stats = $service->get_product_stats($date_from ?: null, $date_to ?: null);
43
44 header('Content-Type: text/csv; charset=utf-8');
45 header('Content-Disposition: attachment; filename=wishlist-analytics-' . gmdate('Y-m-d') . '.csv');
46
47 $output = fopen('php://output', 'w');
48 fputcsv($output, [
49 esc_html__('Product ID', 'king-addons'),
50 esc_html__('Product Name', 'king-addons'),
51 esc_html__('Wishlist Adds', 'king-addons'),
52 esc_html__('Conversions', 'king-addons'),
53 esc_html__('Revenue', 'king-addons'),
54 esc_html__('Conversion Rate', 'king-addons'),
55 esc_html__('Price', 'king-addons'),
56 esc_html__('Stock Status', 'king-addons'),
57 ]);
58
59 foreach ($stats as $row) {
60 $product = function_exists('wc_get_product') ? wc_get_product($row['product_id']) : null;
61 $conv_rate = $row['adds'] > 0 ? round(($row['conversions'] / $row['adds']) * 100, 1) : 0;
62 fputcsv($output, [
63 $row['product_id'],
64 $product ? $product->get_name() : sprintf(esc_html__('Product #%d', 'king-addons'), $row['product_id']),
65 $row['adds'],
66 $row['conversions'],
67 $row['revenue'],
68 $conv_rate . '%',
69 $product ? $product->get_price() : '',
70 $product ? ($product->is_in_stock() ? 'In Stock' : 'Out of Stock') : '',
71 ]);
72 }
73
74 fclose($output);
75 exit;
76 }
77
78 // Enqueue the shared CSS
79 $css_url = KING_ADDONS_URL . 'includes/admin/layouts/shared/admin-v3-styles.css';
80 $css_path = KING_ADDONS_PATH . 'includes/admin/layouts/shared/admin-v3-styles.css';
81 $css_version = file_exists($css_path) ? filemtime($css_path) : KING_ADDONS_VERSION;
82 ?>
83
84 <link rel="stylesheet" href="<?php echo esc_url($css_url); ?>?v=<?php echo esc_attr($css_version); ?>">
85
86 <script>
87 document.body.classList.add('ka-admin-v3');
88 (function() {
89 const mode = '<?php echo esc_js($theme_mode); ?>';
90 const mql = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
91 const isDark = mode === 'auto' ? !!(mql && mql.matches) : mode === 'dark';
92 document.body.classList.toggle('ka-v3-dark', isDark);
93 document.documentElement.classList.toggle('ka-v3-dark', isDark);
94 })();
95 </script>
96
97 <div class="ka-admin-wrap">
98 <!-- Header -->
99 <div class="ka-admin-header">
100 <div class="ka-admin-header-left">
101 <div class="ka-admin-header-icon pink">
102 <span class="dashicons dashicons-chart-bar"></span>
103 </div>
104 <div>
105 <h1 class="ka-admin-title"><?php esc_html_e('Wishlist Analytics', 'king-addons'); ?></h1>
106 <p class="ka-admin-subtitle"><?php esc_html_e('Track wishlist performance and conversions.', 'king-addons'); ?></p>
107 </div>
108 </div>
109 <div class="ka-admin-header-actions">
110 <div class="ka-v3-segmented" id="ka-v3-theme-segment" role="radiogroup" aria-label="<?php echo esc_attr(esc_html__('Theme', 'king-addons')); ?>" data-active="<?php echo esc_attr($theme_mode); ?>">
111 <span class="ka-v3-segmented-indicator" aria-hidden="true"></span>
112 <button type="button" class="ka-v3-segmented-btn" data-theme="light" aria-pressed="<?php echo $theme_mode === 'light' ? 'true' : 'false'; ?>">
113 <span class="ka-v3-segmented-icon" aria-hidden="true">☀︎</span>
114 <?php esc_html_e('Light', 'king-addons'); ?>
115 </button>
116 <button type="button" class="ka-v3-segmented-btn" data-theme="dark" aria-pressed="<?php echo $theme_mode === 'dark' ? 'true' : 'false'; ?>">
117 <span class="ka-v3-segmented-icon" aria-hidden="true"></span>
118 <?php esc_html_e('Dark', 'king-addons'); ?>
119 </button>
120 <button type="button" class="ka-v3-segmented-btn" data-theme="auto" aria-pressed="<?php echo $theme_mode === 'auto' ? 'true' : 'false'; ?>">
121 <?php esc_html_e('Auto', 'king-addons'); ?>
122 </button>
123 </div>
124 <a href="<?php echo esc_url(admin_url('admin.php?page=king-addons-wishlist')); ?>" class="ka-btn ka-btn-secondary">
125 <span class="dashicons dashicons-arrow-left-alt"></span>
126 <?php esc_html_e('Wishlist Settings', 'king-addons'); ?>
127 </a>
128 </div>
129 </div>
130
131 <?php if (!$is_pro): ?>
132 <div class="ka-pro-notice">
133 <h2><?php esc_html_e('Unlock Wishlist Analytics', 'king-addons'); ?></h2>
134 <p><?php esc_html_e('Get detailed insights into wishlist performance with conversion tracking, date filters, and CSV exports.', 'king-addons'); ?></p>
135 <a href="https://kingaddons.com/pricing/" target="_blank" class="ka-btn ka-btn-pink">
136 <?php esc_html_e('Upgrade to Pro', 'king-addons'); ?>
137 </a>
138 </div>
139 <?php return; endif; ?>
140
141 <?php
142 $service = new Wishlist_Service();
143 $stats = $service->get_product_stats($date_from ?: null, $date_to ?: null);
144 $summary = $service->get_stats_summary($date_from ?: null, $date_to ?: null);
145
146 $base_url = admin_url('admin.php?page=king-addons-wishlist-analytics');
147 $export_url = wp_nonce_url(
148 add_query_arg([
149 'action' => 'export_csv',
150 'date_from' => $date_from,
151 'date_to' => $date_to,
152 ], $base_url),
153 'king_addons_wishlist_export'
154 );
155 ?>
156
157 <!-- Stats Grid -->
158 <div class="ka-stats-grid">
159 <div class="ka-stat-card">
160 <div class="ka-stat-label"><?php esc_html_e('Total Adds', 'king-addons'); ?></div>
161 <div class="ka-stat-value"><?php echo esc_html(number_format_i18n($summary['total_adds'])); ?></div>
162 </div>
163 <div class="ka-stat-card">
164 <div class="ka-stat-label"><?php esc_html_e('Conversions', 'king-addons'); ?></div>
165 <div class="ka-stat-value"><?php echo esc_html(number_format_i18n($summary['total_conversions'])); ?></div>
166 </div>
167 <div class="ka-stat-card">
168 <div class="ka-stat-label"><?php esc_html_e('Revenue', 'king-addons'); ?></div>
169 <div class="ka-stat-value"><?php echo function_exists('wc_price') ? wp_kses_post(wc_price($summary['total_revenue'])) : esc_html('$' . number_format($summary['total_revenue'], 2)); ?></div>
170 </div>
171 <div class="ka-stat-card">
172 <div class="ka-stat-label"><?php esc_html_e('Conversion Rate', 'king-addons'); ?></div>
173 <div class="ka-stat-value <?php echo $summary['conversion_rate'] >= 5 ? 'good' : ''; ?>"><?php echo esc_html($summary['conversion_rate']); ?>%</div>
174 </div>
175 <div class="ka-stat-card">
176 <div class="ka-stat-label"><?php esc_html_e('Unique Users', 'king-addons'); ?></div>
177 <div class="ka-stat-value"><?php echo esc_html(number_format_i18n($summary['unique_users'])); ?></div>
178 </div>
179 </div>
180
181 <!-- Filter Bar -->
182 <div class="ka-filter-bar">
183 <form method="get" action="<?php echo esc_url($base_url); ?>" style="display: contents;">
184 <input type="hidden" name="page" value="king-addons-wishlist-analytics">
185 <label>
186 <?php esc_html_e('From', 'king-addons'); ?>
187 <input type="date" name="date_from" value="<?php echo esc_attr($date_from); ?>">
188 </label>
189 <label>
190 <?php esc_html_e('To', 'king-addons'); ?>
191 <input type="date" name="date_to" value="<?php echo esc_attr($date_to); ?>">
192 </label>
193 <button type="submit" class="button"><?php esc_html_e('Filter', 'king-addons'); ?></button>
194 <?php if ($date_from || $date_to): ?>
195 <a href="<?php echo esc_url($base_url); ?>" class="button"><?php esc_html_e('Reset', 'king-addons'); ?></a>
196 <?php endif; ?>
197 </form>
198 <a href="<?php echo esc_url($export_url); ?>" class="ka-btn ka-btn-pink ka-export-btn">
199 <span class="dashicons dashicons-download"></span>
200 <?php esc_html_e('Export CSV', 'king-addons'); ?>
201 </a>
202 </div>
203
204 <!-- Products Table -->
205 <div class="ka-card">
206 <div class="ka-card-header">
207 <span class="dashicons dashicons-products pink"></span>
208 <h2><?php esc_html_e('Product Performance', 'king-addons'); ?></h2>
209 </div>
210 <div class="ka-card-body" style="padding: 0;">
211 <?php if (empty($stats)): ?>
212 <div class="ka-empty">
213 <span class="dashicons dashicons-heart"></span>
214 <p><?php esc_html_e('No wishlist activity yet.', 'king-addons'); ?></p>
215 </div>
216 <?php else: ?>
217 <table class="ka-table">
218 <thead>
219 <tr>
220 <th><?php esc_html_e('Product', 'king-addons'); ?></th>
221 <th class="text-center"><?php esc_html_e('Adds', 'king-addons'); ?></th>
222 <th class="text-center"><?php esc_html_e('Conversions', 'king-addons'); ?></th>
223 <th class="text-center"><?php esc_html_e('Revenue', 'king-addons'); ?></th>
224 <th class="text-center"><?php esc_html_e('Conv. Rate', 'king-addons'); ?></th>
225 <th><?php esc_html_e('Price', 'king-addons'); ?></th>
226 <th><?php esc_html_e('Stock', 'king-addons'); ?></th>
227 </tr>
228 </thead>
229 <tbody>
230 <?php foreach ($stats as $row):
231 $product = function_exists('wc_get_product') ? wc_get_product($row['product_id']) : null;
232 $title = $product ? $product->get_name() : sprintf(esc_html__('Product #%d', 'king-addons'), $row['product_id']);
233 $link = $product ? get_edit_post_link($product->get_id()) : '';
234 $conv_rate = $row['adds'] > 0 ? round(($row['conversions'] / $row['adds']) * 100, 1) : 0;
235 ?>
236 <tr>
237 <td>
238 <div class="ka-product-cell">
239 <?php if ($product): ?>
240 <?php echo $product->get_image([40, 40]); ?>
241 <?php endif; ?>
242 <?php if ($link): ?>
243 <a href="<?php echo esc_url($link); ?>"><?php echo esc_html($title); ?></a>
244 <?php else: ?>
245 <span><?php echo esc_html($title); ?></span>
246 <?php endif; ?>
247 </div>
248 </td>
249 <td class="text-center font-bold"><?php echo esc_html($row['adds']); ?></td>
250 <td class="text-center"><?php echo esc_html($row['conversions']); ?></td>
251 <td class="text-center"><?php echo function_exists('wc_price') ? wp_kses_post(wc_price($row['revenue'])) : esc_html('$' . number_format($row['revenue'], 2)); ?></td>
252 <td class="text-center">
253 <span class="ka-badge <?php echo $conv_rate >= 5 ? 'ka-badge-success' : ($conv_rate > 0 ? 'ka-badge-warning' : 'ka-badge-neutral'); ?>">
254 <?php echo esc_html($conv_rate); ?>%
255 </span>
256 </td>
257 <td><?php echo $product ? wp_kses_post($product->get_price_html()) : '-'; ?></td>
258 <td>
259 <?php if ($product): ?>
260 <?php if ($product->is_in_stock()): ?>
261 <span class="ka-stock ka-stock-in">
262 <span class="dashicons dashicons-yes-alt"></span>
263 <?php esc_html_e('In Stock', 'king-addons'); ?>
264 </span>
265 <?php else: ?>
266 <span class="ka-stock ka-stock-out">
267 <span class="dashicons dashicons-dismiss"></span>
268 <?php esc_html_e('Out of Stock', 'king-addons'); ?>
269 </span>
270 <?php endif; ?>
271 <?php else: ?>
272 -
273 <?php endif; ?>
274 </td>
275 </tr>
276 <?php endforeach; ?>
277 </tbody>
278 </table>
279 <?php endif; ?>
280 </div>
281 </div>
282 </div>
283
284 <script>
285 (function($) {
286 'use strict';
287
288 $(document).ready(function() {
289 const ajaxUrl = '<?php echo esc_url(admin_url('admin-ajax.php')); ?>';
290 const nonce = '<?php echo esc_js(wp_create_nonce('king_addons_dashboard_ui')); ?>';
291
292 // Theme segmented control (dashboard-style)
293 const $themeSegment = $('#ka-v3-theme-segment');
294 const $themeSegmentButtons = $themeSegment.find('.ka-v3-segmented-btn');
295 const themeMql = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
296 let themeMode = ($themeSegment.attr('data-active') || 'dark').toString();
297 let themeMqlHandler = null;
298
299 function saveUISetting(key, value) {
300 $.post(ajaxUrl, {
301 action: 'king_addons_save_dashboard_ui',
302 nonce: nonce,
303 key: key,
304 value: value
305 });
306 }
307
308 function updateSegment(mode) {
309 $themeSegment.attr('data-active', mode);
310 $themeSegmentButtons.each(function() {
311 const theme = $(this).data('theme');
312 $(this).attr('aria-pressed', theme === mode ? 'true' : 'false');
313 });
314 }
315
316 function applyThemeClass(isDark) {
317 $('body').toggleClass('ka-v3-dark', isDark);
318 document.documentElement.classList.toggle('ka-v3-dark', isDark);
319 }
320
321 function setThemeMode(mode, save) {
322 themeMode = mode;
323 updateSegment(mode);
324
325 if (themeMqlHandler && themeMql) {
326 if (themeMql.removeEventListener) {
327 themeMql.removeEventListener('change', themeMqlHandler);
328 } else if (themeMql.removeListener) {
329 themeMql.removeListener(themeMqlHandler);
330 }
331 themeMqlHandler = null;
332 }
333
334 if (mode === 'auto') {
335 applyThemeClass(!!(themeMql && themeMql.matches));
336 themeMqlHandler = function(e) {
337 if (themeMode !== 'auto') {
338 return;
339 }
340 applyThemeClass(!!e.matches);
341 };
342 if (themeMql) {
343 if (themeMql.addEventListener) {
344 themeMql.addEventListener('change', themeMqlHandler);
345 } else if (themeMql.addListener) {
346 themeMql.addListener(themeMqlHandler);
347 }
348 }
349 } else {
350 applyThemeClass(mode === 'dark');
351 }
352
353 if (save) {
354 saveUISetting('theme_mode', mode);
355 }
356 }
357
358 $themeSegment.on('click', '.ka-v3-segmented-btn', function(e) {
359 e.preventDefault();
360 const mode = ($(this).data('theme') || 'dark').toString();
361 setThemeMode(mode, true);
362 });
363
364 // Ensure mode applies for Auto (listener + system state)
365 setThemeMode(themeMode, false);
366 });
367 })(jQuery);
368 </script>
369