PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.78
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.78
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.78, at includes/admin/layouts/wishlist-analytics.php

370 lines 17.3 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 <span class="ka-v3-segmented-icon" aria-hidden="true"></span>
122 <?php esc_html_e('Auto', 'king-addons'); ?>
123 </button>
124 </div>
125 <a href="<?php echo esc_url(admin_url('admin.php?page=king-addons-wishlist')); ?>" class="ka-btn ka-btn-secondary">
126 <span class="dashicons dashicons-arrow-left-alt"></span>
127 <?php esc_html_e('Wishlist Settings', 'king-addons'); ?>
128 </a>
129 </div>
130 </div>
131
132 <?php if (!$is_pro): ?>
133 <div class="ka-pro-notice">
134 <h2><?php esc_html_e('Unlock Wishlist Analytics', 'king-addons'); ?></h2>
135 <p><?php esc_html_e('Get detailed insights into wishlist performance with conversion tracking, date filters, and CSV exports.', 'king-addons'); ?></p>
136 <a href="https://kingaddons.com/pricing/" target="_blank" class="ka-btn ka-btn-pink">
137 <?php esc_html_e('Upgrade to Pro', 'king-addons'); ?>
138 </a>
139 </div>
140 <?php return; endif; ?>
141
142 <?php
143 $service = new Wishlist_Service();
144 $stats = $service->get_product_stats($date_from ?: null, $date_to ?: null);
145 $summary = $service->get_stats_summary($date_from ?: null, $date_to ?: null);
146
147 $base_url = admin_url('admin.php?page=king-addons-wishlist-analytics');
148 $export_url = wp_nonce_url(
149 add_query_arg([
150 'action' => 'export_csv',
151 'date_from' => $date_from,
152 'date_to' => $date_to,
153 ], $base_url),
154 'king_addons_wishlist_export'
155 );
156 ?>
157
158 <!-- Stats Grid -->
159 <div class="ka-stats-grid">
160 <div class="ka-stat-card">
161 <div class="ka-stat-label"><?php esc_html_e('Total Adds', 'king-addons'); ?></div>
162 <div class="ka-stat-value"><?php echo esc_html(number_format_i18n($summary['total_adds'])); ?></div>
163 </div>
164 <div class="ka-stat-card">
165 <div class="ka-stat-label"><?php esc_html_e('Conversions', 'king-addons'); ?></div>
166 <div class="ka-stat-value"><?php echo esc_html(number_format_i18n($summary['total_conversions'])); ?></div>
167 </div>
168 <div class="ka-stat-card">
169 <div class="ka-stat-label"><?php esc_html_e('Revenue', 'king-addons'); ?></div>
170 <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>
171 </div>
172 <div class="ka-stat-card">
173 <div class="ka-stat-label"><?php esc_html_e('Conversion Rate', 'king-addons'); ?></div>
174 <div class="ka-stat-value <?php echo $summary['conversion_rate'] >= 5 ? 'good' : ''; ?>"><?php echo esc_html($summary['conversion_rate']); ?>%</div>
175 </div>
176 <div class="ka-stat-card">
177 <div class="ka-stat-label"><?php esc_html_e('Unique Users', 'king-addons'); ?></div>
178 <div class="ka-stat-value"><?php echo esc_html(number_format_i18n($summary['unique_users'])); ?></div>
179 </div>
180 </div>
181
182 <!-- Filter Bar -->
183 <div class="ka-filter-bar">
184 <form method="get" action="<?php echo esc_url($base_url); ?>" style="display: contents;">
185 <input type="hidden" name="page" value="king-addons-wishlist-analytics">
186 <label>
187 <?php esc_html_e('From', 'king-addons'); ?>
188 <input type="date" name="date_from" value="<?php echo esc_attr($date_from); ?>">
189 </label>
190 <label>
191 <?php esc_html_e('To', 'king-addons'); ?>
192 <input type="date" name="date_to" value="<?php echo esc_attr($date_to); ?>">
193 </label>
194 <button type="submit" class="button"><?php esc_html_e('Filter', 'king-addons'); ?></button>
195 <?php if ($date_from || $date_to): ?>
196 <a href="<?php echo esc_url($base_url); ?>" class="button"><?php esc_html_e('Reset', 'king-addons'); ?></a>
197 <?php endif; ?>
198 </form>
199 <a href="<?php echo esc_url($export_url); ?>" class="ka-btn ka-btn-pink ka-export-btn">
200 <span class="dashicons dashicons-download"></span>
201 <?php esc_html_e('Export CSV', 'king-addons'); ?>
202 </a>
203 </div>
204
205 <!-- Products Table -->
206 <div class="ka-card">
207 <div class="ka-card-header">
208 <span class="dashicons dashicons-products pink"></span>
209 <h2><?php esc_html_e('Product Performance', 'king-addons'); ?></h2>
210 </div>
211 <div class="ka-card-body" style="padding: 0;">
212 <?php if (empty($stats)): ?>
213 <div class="ka-empty">
214 <span class="dashicons dashicons-heart"></span>
215 <p><?php esc_html_e('No wishlist activity yet.', 'king-addons'); ?></p>
216 </div>
217 <?php else: ?>
218 <table class="ka-table">
219 <thead>
220 <tr>
221 <th><?php esc_html_e('Product', 'king-addons'); ?></th>
222 <th class="text-center"><?php esc_html_e('Adds', 'king-addons'); ?></th>
223 <th class="text-center"><?php esc_html_e('Conversions', 'king-addons'); ?></th>
224 <th class="text-center"><?php esc_html_e('Revenue', 'king-addons'); ?></th>
225 <th class="text-center"><?php esc_html_e('Conv. Rate', 'king-addons'); ?></th>
226 <th><?php esc_html_e('Price', 'king-addons'); ?></th>
227 <th><?php esc_html_e('Stock', 'king-addons'); ?></th>
228 </tr>
229 </thead>
230 <tbody>
231 <?php foreach ($stats as $row):
232 $product = function_exists('wc_get_product') ? wc_get_product($row['product_id']) : null;
233 $title = $product ? $product->get_name() : sprintf(esc_html__('Product #%d', 'king-addons'), $row['product_id']);
234 $link = $product ? get_edit_post_link($product->get_id()) : '';
235 $conv_rate = $row['adds'] > 0 ? round(($row['conversions'] / $row['adds']) * 100, 1) : 0;
236 ?>
237 <tr>
238 <td>
239 <div class="ka-product-cell">
240 <?php if ($product): ?>
241 <?php echo $product->get_image([40, 40]); ?>
242 <?php endif; ?>
243 <?php if ($link): ?>
244 <a href="<?php echo esc_url($link); ?>"><?php echo esc_html($title); ?></a>
245 <?php else: ?>
246 <span><?php echo esc_html($title); ?></span>
247 <?php endif; ?>
248 </div>
249 </td>
250 <td class="text-center font-bold"><?php echo esc_html($row['adds']); ?></td>
251 <td class="text-center"><?php echo esc_html($row['conversions']); ?></td>
252 <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>
253 <td class="text-center">
254 <span class="ka-badge <?php echo $conv_rate >= 5 ? 'ka-badge-success' : ($conv_rate > 0 ? 'ka-badge-warning' : 'ka-badge-neutral'); ?>">
255 <?php echo esc_html($conv_rate); ?>%
256 </span>
257 </td>
258 <td><?php echo $product ? wp_kses_post($product->get_price_html()) : '-'; ?></td>
259 <td>
260 <?php if ($product): ?>
261 <?php if ($product->is_in_stock()): ?>
262 <span class="ka-stock ka-stock-in">
263 <span class="dashicons dashicons-yes-alt"></span>
264 <?php esc_html_e('In Stock', 'king-addons'); ?>
265 </span>
266 <?php else: ?>
267 <span class="ka-stock ka-stock-out">
268 <span class="dashicons dashicons-dismiss"></span>
269 <?php esc_html_e('Out of Stock', 'king-addons'); ?>
270 </span>
271 <?php endif; ?>
272 <?php else: ?>
273 -
274 <?php endif; ?>
275 </td>
276 </tr>
277 <?php endforeach; ?>
278 </tbody>
279 </table>
280 <?php endif; ?>
281 </div>
282 </div>
283 </div>
284
285 <script>
286 (function($) {
287 'use strict';
288
289 $(document).ready(function() {
290 const ajaxUrl = '<?php echo esc_url(admin_url('admin-ajax.php')); ?>';
291 const nonce = '<?php echo esc_js(wp_create_nonce('king_addons_dashboard_ui')); ?>';
292
293 // Theme segmented control (dashboard-style)
294 const $themeSegment = $('#ka-v3-theme-segment');
295 const $themeSegmentButtons = $themeSegment.find('.ka-v3-segmented-btn');
296 const themeMql = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
297 let themeMode = ($themeSegment.attr('data-active') || 'dark').toString();
298 let themeMqlHandler = null;
299
300 function saveUISetting(key, value) {
301 $.post(ajaxUrl, {
302 action: 'king_addons_save_dashboard_ui',
303 nonce: nonce,
304 key: key,
305 value: value
306 });
307 }
308
309 function updateSegment(mode) {
310 $themeSegment.attr('data-active', mode);
311 $themeSegmentButtons.each(function() {
312 const theme = $(this).data('theme');
313 $(this).attr('aria-pressed', theme === mode ? 'true' : 'false');
314 });
315 }
316
317 function applyThemeClass(isDark) {
318 $('body').toggleClass('ka-v3-dark', isDark);
319 document.documentElement.classList.toggle('ka-v3-dark', isDark);
320 }
321
322 function setThemeMode(mode, save) {
323 themeMode = mode;
324 updateSegment(mode);
325
326 if (themeMqlHandler && themeMql) {
327 if (themeMql.removeEventListener) {
328 themeMql.removeEventListener('change', themeMqlHandler);
329 } else if (themeMql.removeListener) {
330 themeMql.removeListener(themeMqlHandler);
331 }
332 themeMqlHandler = null;
333 }
334
335 if (mode === 'auto') {
336 applyThemeClass(!!(themeMql && themeMql.matches));
337 themeMqlHandler = function(e) {
338 if (themeMode !== 'auto') {
339 return;
340 }
341 applyThemeClass(!!e.matches);
342 };
343 if (themeMql) {
344 if (themeMql.addEventListener) {
345 themeMql.addEventListener('change', themeMqlHandler);
346 } else if (themeMql.addListener) {
347 themeMql.addListener(themeMqlHandler);
348 }
349 }
350 } else {
351 applyThemeClass(mode === 'dark');
352 }
353
354 if (save) {
355 saveUISetting('theme_mode', mode);
356 }
357 }
358
359 $themeSegment.on('click', '.ka-v3-segmented-btn', function(e) {
360 e.preventDefault();
361 const mode = ($(this).data('theme') || 'dark').toString();
362 setThemeMode(mode, true);
363 });
364
365 // Ensure mode applies for Auto (listener + system state)
366 setThemeMode(themeMode, false);
367 });
368 })(jQuery);
369 </script>
370