refactor(Core): optimize admin panel and refactor
This commit is contained in:
103
admin/admin.php
103
admin/admin.php
@@ -25,6 +25,86 @@ $dashboardController = new DashboardController($eventRepository, $ruleRepository
|
|||||||
$settingsController = new SettingsController();
|
$settingsController = new SettingsController();
|
||||||
$adminController = new AdminController($ruleRepository, $upsellRepository, $bannerRepository);
|
$adminController = new AdminController($ruleRepository, $upsellRepository, $bannerRepository);
|
||||||
|
|
||||||
|
function sodino_admin_notice_key() {
|
||||||
|
return 'sodino_admin_notice_' . get_current_user_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
function sodino_old_input_key() {
|
||||||
|
return 'sodino_old_input_' . get_current_user_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
function sodino_get_admin_notice($delete = false) {
|
||||||
|
$notice = get_transient(sodino_admin_notice_key());
|
||||||
|
|
||||||
|
if (!$notice) {
|
||||||
|
$notice = get_transient('sodino_admin_notice');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($notice && $delete) {
|
||||||
|
delete_transient(sodino_admin_notice_key());
|
||||||
|
delete_transient('sodino_admin_notice');
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_array($notice) ? $notice : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sodino_get_old_input($delete = false) {
|
||||||
|
$old = get_transient(sodino_old_input_key());
|
||||||
|
|
||||||
|
if (is_array($old) && isset($old['_sodino_page'])) {
|
||||||
|
$current_page = isset($_GET['page']) ? sanitize_key($_GET['page']) : '';
|
||||||
|
if ($current_page !== sanitize_key($old['_sodino_page'])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($old && $delete) {
|
||||||
|
delete_transient(sodino_old_input_key());
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_array($old) ? $old : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function sodino_old_input($key, $default = '') {
|
||||||
|
static $old = null;
|
||||||
|
|
||||||
|
if ($old === null) {
|
||||||
|
$old = sodino_get_old_input(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($key, '.') === false) {
|
||||||
|
return array_key_exists($key, $old) ? $old[$key] : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $old;
|
||||||
|
foreach (explode('.', $key) as $part) {
|
||||||
|
if (!is_array($value) || !array_key_exists($part, $value)) {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
$value = $value[$part];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sodino_render_admin_notice() {
|
||||||
|
$notice = sodino_get_admin_notice(true);
|
||||||
|
if (!$notice) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $notice['type'] ?? 'success';
|
||||||
|
$class = $type === 'error' ? 'sodino-form-notice sodino-form-notice-error' : 'sodino-form-notice sodino-form-notice-success';
|
||||||
|
$title = $type === 'error' ? __('خطای فرم', 'sodino') : __('عملیات موفق', 'sodino');
|
||||||
|
|
||||||
|
printf(
|
||||||
|
'<div class="%s" role="alert"><strong>%s</strong><p>%s</p></div>',
|
||||||
|
esc_attr($class),
|
||||||
|
esc_html($title),
|
||||||
|
esc_html($notice['message'] ?? '')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add admin menu
|
* Add admin menu
|
||||||
*/
|
*/
|
||||||
@@ -102,6 +182,15 @@ add_action('admin_menu', function() use ($adminController) {
|
|||||||
[$adminController, 'addBannerPage']
|
[$adminController, 'addBannerPage']
|
||||||
);
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
'sodino-dashboard',
|
||||||
|
__('قیمت رقبا (بهزودی)', 'sodino'),
|
||||||
|
__('قیمت رقبا (بهزودی)', 'sodino'),
|
||||||
|
'manage_options',
|
||||||
|
'sodino-competitor-price',
|
||||||
|
[$adminController, 'competitorPricePage']
|
||||||
|
);
|
||||||
|
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'sodino-dashboard',
|
'sodino-dashboard',
|
||||||
__('تنظیمات', 'sodino'),
|
__('تنظیمات', 'sodino'),
|
||||||
@@ -125,16 +214,12 @@ add_action('admin_enqueue_scripts', function($hook) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enqueue Tailwind via CDN
|
|
||||||
wp_enqueue_script('sodino-tailwind', 'https://cdn.tailwindcss.com', [], SODINO_VERSION);
|
|
||||||
|
|
||||||
// Admin CSS
|
// Admin CSS
|
||||||
wp_enqueue_style('sodino-admin', plugin_dir_url(__FILE__) . 'css/admin.css', [], SODINO_VERSION);
|
wp_enqueue_style('sodino-admin', plugin_dir_url(__FILE__) . 'css/admin.css', [], SODINO_VERSION);
|
||||||
|
|
||||||
// Dashboard specific scripts
|
// Dashboard specific scripts
|
||||||
if (strpos($hook, 'sodino-dashboard') !== false || strpos($hook, 'sodino_page_sodino-dashboard') !== false) {
|
if (strpos($hook, 'sodino-dashboard') !== false || strpos($hook, 'sodino_page_sodino-dashboard') !== false) {
|
||||||
wp_enqueue_script('sodino-chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', [], null, true);
|
wp_enqueue_script('sodino-dashboard-js', plugin_dir_url(__FILE__) . 'js/dashboard.js', [], SODINO_VERSION, true);
|
||||||
wp_enqueue_script('sodino-dashboard-js', plugin_dir_url(__FILE__) . 'js/dashboard.js', ['sodino-chart-js'], SODINO_VERSION, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upsell specific scripts
|
// Upsell specific scripts
|
||||||
@@ -185,7 +270,12 @@ add_action('admin_init', function() use ($ruleController, $settingsController, $
|
|||||||
* Show admin notices
|
* Show admin notices
|
||||||
*/
|
*/
|
||||||
add_action('admin_notices', function() {
|
add_action('admin_notices', function() {
|
||||||
$notice = get_transient('sodino_admin_notice');
|
$page = isset($_GET['page']) ? sanitize_key($_GET['page']) : '';
|
||||||
|
if (strpos($page, 'sodino') === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$notice = sodino_get_admin_notice(true);
|
||||||
|
|
||||||
if ($notice) {
|
if ($notice) {
|
||||||
$class = $notice['type'] === 'error' ? 'notice-error' : 'notice-success';
|
$class = $notice['type'] === 'error' ? 'notice-error' : 'notice-success';
|
||||||
@@ -194,6 +284,5 @@ add_action('admin_notices', function() {
|
|||||||
esc_attr($class),
|
esc_attr($class),
|
||||||
esc_html($notice['message'])
|
esc_html($notice['message'])
|
||||||
);
|
);
|
||||||
delete_transient('sodino_admin_notice');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ class Sodino_Rules_List_Table extends WP_List_Table {
|
|||||||
return [
|
return [
|
||||||
'cb' => '<input type="checkbox" />',
|
'cb' => '<input type="checkbox" />',
|
||||||
'name' => __('عنوان قانون', 'sodino'),
|
'name' => __('عنوان قانون', 'sodino'),
|
||||||
'condition_type' => __('نوع کاربر', 'sodino'),
|
'condition_type' => __('شرط', 'sodino'),
|
||||||
'action_value' => __('درصد تخفیف', 'sodino'),
|
'action_value' => __('عملیات', 'sodino'),
|
||||||
'enabled' => __('وضعیت', 'sodino'),
|
'enabled' => __('وضعیت', 'sodino'),
|
||||||
'actions' => __('عملیات', 'sodino'),
|
'actions' => __('عملیات', 'sodino'),
|
||||||
];
|
];
|
||||||
@@ -69,15 +69,30 @@ class Sodino_Rules_List_Table extends WP_List_Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function column_condition_type($item) {
|
public function column_condition_type($item) {
|
||||||
$value = __('کاربر جدید', 'sodino');
|
$labels = [
|
||||||
if ($item->condition_value === 'returning') {
|
'user_type' => __('نوع کاربر', 'sodino'),
|
||||||
$value = __('کاربر بازگشتی', 'sodino');
|
'product_category' => __('دستهبندی محصول', 'sodino'),
|
||||||
}
|
'product_ids' => __('محصولات خاص', 'sodino'),
|
||||||
return esc_html($value);
|
'cart_total_min' => __('حداقل مبلغ سبد', 'sodino'),
|
||||||
|
'cart_total_max' => __('حداکثر مبلغ سبد', 'sodino'),
|
||||||
|
'cart_item_count_min' => __('حداقل تعداد سبد', 'sodino'),
|
||||||
|
'cart_item_count_max' => __('حداکثر تعداد سبد', 'sodino'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$type = $labels[$item->condition_type] ?? $item->condition_type;
|
||||||
|
return esc_html(sprintf('%s: %s', $type, $item->condition_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function column_action_value($item) {
|
public function column_action_value($item) {
|
||||||
return sprintf('%s %%', esc_html($item->action_value));
|
$labels = [
|
||||||
|
'discount_percent' => __('درصد تخفیف', 'sodino'),
|
||||||
|
'discount_fixed' => __('تخفیف ثابت', 'sodino'),
|
||||||
|
'set_price' => __('قیمت ثابت', 'sodino'),
|
||||||
|
'free_shipping' => __('ارسال رایگان', 'sodino'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$type = $labels[$item->action_type] ?? $item->action_type;
|
||||||
|
return esc_html(sprintf('%s: %s', $type, $item->action_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function column_enabled($item) {
|
public function column_enabled($item) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ function sodino_admin_layout($current_page, $content_callback) {
|
|||||||
<?php include SODINO_PLUGIN_DIR . 'admin/components/header.php'; ?>
|
<?php include SODINO_PLUGIN_DIR . 'admin/components/header.php'; ?>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<?php if (function_exists('sodino_render_admin_notice')) { sodino_render_admin_notice(); } ?>
|
||||||
<div class="flex gap-8">
|
<div class="flex gap-8">
|
||||||
<?php include SODINO_PLUGIN_DIR . 'admin/components/sidebar.php'; ?>
|
<?php include SODINO_PLUGIN_DIR . 'admin/components/sidebar.php'; ?>
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ $menu_items = [
|
|||||||
'sodino-add-upsell' => __('افزودن آپسل', 'sodino'),
|
'sodino-add-upsell' => __('افزودن آپسل', 'sodino'),
|
||||||
'sodino-banners' => __('بنرهای هوشمند', 'sodino'),
|
'sodino-banners' => __('بنرهای هوشمند', 'sodino'),
|
||||||
'sodino-add-banner' => __('افزودن بنر', 'sodino'),
|
'sodino-add-banner' => __('افزودن بنر', 'sodino'),
|
||||||
|
'sodino-competitor-price' => __('قیمت رقبا (بهزودی)', 'sodino'),
|
||||||
'sodino-settings' => __('تنظیمات', 'sodino'),
|
'sodino-settings' => __('تنظیمات', 'sodino'),
|
||||||
];
|
];
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -385,3 +385,383 @@
|
|||||||
#sodino-app .wp-list-table .column-title {
|
#sodino-app .wp-list-table .column-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#sodino-app .sodino-form-notice {
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 16px 18px;
|
||||||
|
border: 1px solid;
|
||||||
|
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .sodino-form-notice strong {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .sodino-form-notice p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .sodino-form-notice-error {
|
||||||
|
background: #fff1f2;
|
||||||
|
border-color: #fecdd3;
|
||||||
|
color: #9f1239;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .sodino-form-notice-success {
|
||||||
|
background: #ecfdf3;
|
||||||
|
border-color: #bbf7d0;
|
||||||
|
color: #166534;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Local utility layer for the Sodino admin. This replaces the previous Tailwind CDN dependency. */
|
||||||
|
#sodino-app {
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 28px 18px 48px;
|
||||||
|
background: #f4f7fb;
|
||||||
|
color: #172033;
|
||||||
|
font-family: Tahoma, Arial, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app *,
|
||||||
|
#sodino-app *::before,
|
||||||
|
#sodino-app *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app h1,
|
||||||
|
#sodino-app h2,
|
||||||
|
#sodino-app h3,
|
||||||
|
#sodino-app h4,
|
||||||
|
#sodino-app p {
|
||||||
|
margin-top: 0;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app input,
|
||||||
|
#sodino-app select,
|
||||||
|
#sodino-app textarea,
|
||||||
|
#sodino-app button {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .min-h-screen { min-height: 100vh; }
|
||||||
|
#sodino-app .max-w-7xl { max-width: 1280px; }
|
||||||
|
#sodino-app .mx-auto { margin-left: auto; margin-right: auto; }
|
||||||
|
#sodino-app .min-w-0 { min-width: 0; }
|
||||||
|
#sodino-app .min-w-full { min-width: 100%; }
|
||||||
|
#sodino-app .w-full { width: 100%; }
|
||||||
|
#sodino-app .w-64 { width: 16rem; }
|
||||||
|
#sodino-app .w-4 { width: 1rem; }
|
||||||
|
#sodino-app .w-5 { width: 1.25rem; }
|
||||||
|
#sodino-app .h-4 { height: 1rem; }
|
||||||
|
#sodino-app .h-5 { height: 1.25rem; }
|
||||||
|
#sodino-app .h-64 { height: 16rem; }
|
||||||
|
#sodino-app .h-full { height: 100%; }
|
||||||
|
#sodino-app .block { display: block; }
|
||||||
|
#sodino-app .hidden { display: none; }
|
||||||
|
#sodino-app .inline-flex { display: inline-flex; }
|
||||||
|
#sodino-app .flex { display: flex; }
|
||||||
|
#sodino-app .grid { display: grid; }
|
||||||
|
#sodino-app .flex-1 { flex: 1 1 0%; }
|
||||||
|
#sodino-app .flex-col { flex-direction: column; }
|
||||||
|
#sodino-app .flex-shrink-0 { flex-shrink: 0; }
|
||||||
|
#sodino-app .items-center { align-items: center; }
|
||||||
|
#sodino-app .justify-between { justify-content: space-between; }
|
||||||
|
#sodino-app .justify-center { justify-content: center; }
|
||||||
|
#sodino-app .justify-end { justify-content: flex-end; }
|
||||||
|
#sodino-app .gap-2 { gap: 0.5rem; }
|
||||||
|
#sodino-app .gap-3 { gap: 0.75rem; }
|
||||||
|
#sodino-app .gap-4 { gap: 1rem; }
|
||||||
|
#sodino-app .gap-6 { gap: 1.5rem; }
|
||||||
|
#sodino-app .gap-8 { gap: 2rem; }
|
||||||
|
#sodino-app .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
|
||||||
|
#sodino-app .grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
#sodino-app .relative { position: relative; }
|
||||||
|
#sodino-app .absolute { position: absolute; }
|
||||||
|
#sodino-app .z-10 { z-index: 10; }
|
||||||
|
#sodino-app .overflow-hidden { overflow: hidden; }
|
||||||
|
#sodino-app .overflow-x-auto { overflow-x: auto; }
|
||||||
|
#sodino-app .cursor-pointer { cursor: pointer; }
|
||||||
|
|
||||||
|
#sodino-app .p-4 { padding: 1rem; }
|
||||||
|
#sodino-app .p-5 { padding: 1.25rem; }
|
||||||
|
#sodino-app .p-6 { padding: 1.5rem; }
|
||||||
|
#sodino-app .px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }
|
||||||
|
#sodino-app .px-4 { padding-left: 1rem; padding-right: 1rem; }
|
||||||
|
#sodino-app .px-5 { padding-left: 1.25rem; padding-right: 1.25rem; }
|
||||||
|
#sodino-app .px-6 { padding-left: 1.5rem; padding-right: 1.5rem; }
|
||||||
|
#sodino-app .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }
|
||||||
|
#sodino-app .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
|
||||||
|
#sodino-app .py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
|
||||||
|
#sodino-app .py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
|
||||||
|
#sodino-app .py-8 { padding-top: 2rem; padding-bottom: 2rem; }
|
||||||
|
#sodino-app .pb-6 { padding-bottom: 1.5rem; }
|
||||||
|
#sodino-app .pt-6 { padding-top: 1.5rem; }
|
||||||
|
#sodino-app .mt-1 { margin-top: 0.25rem; }
|
||||||
|
#sodino-app .mt-2 { margin-top: 0.5rem; }
|
||||||
|
#sodino-app .mt-8 { margin-top: 2rem; }
|
||||||
|
#sodino-app .mb-2 { margin-bottom: 0.5rem; }
|
||||||
|
#sodino-app .mb-4 { margin-bottom: 1rem; }
|
||||||
|
#sodino-app .mb-6 { margin-bottom: 1.5rem; }
|
||||||
|
#sodino-app .mb-8 { margin-bottom: 2rem; }
|
||||||
|
#sodino-app .mr-2 { margin-right: 0.5rem; }
|
||||||
|
#sodino-app .-ml-1 { margin-left: -0.25rem; }
|
||||||
|
#sodino-app .space-y-2 > * + * { margin-top: 0.5rem; }
|
||||||
|
#sodino-app .space-y-3 > * + * { margin-top: 0.75rem; }
|
||||||
|
#sodino-app .space-y-4 > * + * { margin-top: 1rem; }
|
||||||
|
#sodino-app .space-y-6 > * + * { margin-top: 1.5rem; }
|
||||||
|
#sodino-app .space-y-8 > * + * { margin-top: 2rem; }
|
||||||
|
|
||||||
|
#sodino-app .rounded { border-radius: 4px; }
|
||||||
|
#sodino-app .rounded-md,
|
||||||
|
#sodino-app .rounded-lg,
|
||||||
|
#sodino-app .rounded-xl,
|
||||||
|
#sodino-app .rounded-2xl,
|
||||||
|
#sodino-app .sd-sidebar,
|
||||||
|
#sodino-app .sd-card,
|
||||||
|
#sodino-app .sd-chart-card {
|
||||||
|
border-radius: 8px !important;
|
||||||
|
}
|
||||||
|
#sodino-app .rounded-full { border-radius: 9999px; }
|
||||||
|
|
||||||
|
#sodino-app .border { border: 1px solid #d8e0ea; }
|
||||||
|
#sodino-app .border-b { border-bottom: 1px solid #d8e0ea; }
|
||||||
|
#sodino-app .border-t { border-top: 1px solid #d8e0ea; }
|
||||||
|
#sodino-app .border-r-2 { border-right: 2px solid currentColor; }
|
||||||
|
#sodino-app .border-transparent { border-color: transparent; }
|
||||||
|
#sodino-app .border-gray-200 { border-color: #d8e0ea; }
|
||||||
|
#sodino-app .border-gray-300 { border-color: #c7d2df; }
|
||||||
|
#sodino-app .border-blue-700 { border-color: #1769aa; }
|
||||||
|
#sodino-app .divide-y > * + * { border-top: 1px solid #d8e0ea; }
|
||||||
|
#sodino-app .divide-gray-200 > * + * { border-color: #d8e0ea; }
|
||||||
|
|
||||||
|
#sodino-app .bg-white {
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
#sodino-app .bg-gray-50 {
|
||||||
|
background: #f6f8fb !important;
|
||||||
|
}
|
||||||
|
#sodino-app .bg-gray-100 {
|
||||||
|
background: #eef3f8;
|
||||||
|
}
|
||||||
|
#sodino-app .bg-gray-300 {
|
||||||
|
background: #c7d2df;
|
||||||
|
}
|
||||||
|
#sodino-app .bg-blue-50 {
|
||||||
|
background: #e8f3fb;
|
||||||
|
}
|
||||||
|
#sodino-app .bg-blue-600,
|
||||||
|
#sodino-app .from-blue-600 {
|
||||||
|
background: #1769aa;
|
||||||
|
}
|
||||||
|
#sodino-app .to-blue-700 {
|
||||||
|
background: #115487;
|
||||||
|
}
|
||||||
|
#sodino-app .bg-gradient-to-br {
|
||||||
|
background: linear-gradient(135deg, #1769aa, #115487);
|
||||||
|
}
|
||||||
|
#sodino-app .bg-green-50 {
|
||||||
|
background: #e9f8ef;
|
||||||
|
}
|
||||||
|
#sodino-app .bg-yellow-50 {
|
||||||
|
background: #fff7df;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .text-xs { font-size: 0.75rem; line-height: 1rem; }
|
||||||
|
#sodino-app .text-sm { font-size: 0.875rem; line-height: 1.5; }
|
||||||
|
#sodino-app .text-lg { font-size: 1.125rem; line-height: 1.6; }
|
||||||
|
#sodino-app .text-xl { font-size: 1.25rem; line-height: 1.5; }
|
||||||
|
#sodino-app .text-2xl { font-size: 1.5rem; line-height: 1.35; }
|
||||||
|
#sodino-app .text-3xl { font-size: 1.875rem; line-height: 1.25; }
|
||||||
|
#sodino-app .font-medium { font-weight: 500; }
|
||||||
|
#sodino-app .font-semibold { font-weight: 600; }
|
||||||
|
#sodino-app .font-bold { font-weight: 700; }
|
||||||
|
#sodino-app .text-right { text-align: right; }
|
||||||
|
#sodino-app .text-white { color: #ffffff; }
|
||||||
|
#sodino-app .text-gray-500 { color: #64748b !important; }
|
||||||
|
#sodino-app .text-gray-600 { color: #475569 !important; }
|
||||||
|
#sodino-app .text-gray-700 { color: #334155 !important; }
|
||||||
|
#sodino-app .text-gray-900 { color: #172033 !important; }
|
||||||
|
#sodino-app .text-blue-600 { color: #1769aa; }
|
||||||
|
#sodino-app .text-blue-700 { color: #115487; }
|
||||||
|
#sodino-app .text-green-600 { color: #198754; }
|
||||||
|
#sodino-app .text-green-700 { color: #146c43; }
|
||||||
|
#sodino-app .text-red-500 { color: #dc3545; }
|
||||||
|
#sodino-app .text-red-600 { color: #c82333; }
|
||||||
|
#sodino-app .text-yellow-700 { color: #946200; }
|
||||||
|
#sodino-app .opacity-90 { opacity: 0.9; }
|
||||||
|
|
||||||
|
#sodino-app .shadow-sm,
|
||||||
|
#sodino-app .shadow-lg,
|
||||||
|
#sodino-app .bg-white.rounded-lg.shadow-sm.border,
|
||||||
|
#sodino-app .bg-white.rounded-2xl.border {
|
||||||
|
box-shadow: 0 10px 26px rgba(15, 23, 42, 0.06) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .transition-colors {
|
||||||
|
transition-property: color, background-color, border-color, box-shadow, transform;
|
||||||
|
}
|
||||||
|
#sodino-app .duration-200 {
|
||||||
|
transition-duration: 160ms;
|
||||||
|
}
|
||||||
|
#sodino-app .hover\:bg-gray-50:hover {
|
||||||
|
background: #eef3f8;
|
||||||
|
}
|
||||||
|
#sodino-app .hover\:bg-blue-700:hover {
|
||||||
|
background: #115487;
|
||||||
|
}
|
||||||
|
#sodino-app .hover\:text-gray-900:hover {
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
#sodino-app .hover\:border-blue-300:hover {
|
||||||
|
border-color: #7db8df;
|
||||||
|
}
|
||||||
|
#sodino-app .focus\:outline-none:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
#sodino-app .focus\:border-blue-500:focus {
|
||||||
|
border-color: #2488d1;
|
||||||
|
}
|
||||||
|
#sodino-app .focus\:ring-2:focus {
|
||||||
|
box-shadow: 0 0 0 3px rgba(36, 136, 209, 0.18);
|
||||||
|
}
|
||||||
|
#sodino-app .focus\:ring-blue-100:focus,
|
||||||
|
#sodino-app .focus\:ring-blue-500:focus {
|
||||||
|
box-shadow: 0 0 0 3px rgba(36, 136, 209, 0.18);
|
||||||
|
}
|
||||||
|
#sodino-app .focus\:ring-offset-2:focus {
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .bg-white.border-b.border-gray-200 {
|
||||||
|
border: 1px solid #d8e0ea;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 10px 26px rgba(15, 23, 42, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app aside .bg-white,
|
||||||
|
#sodino-app main > .bg-white,
|
||||||
|
#sodino-app .grid > .bg-white,
|
||||||
|
#sodino-app .bg-white.rounded-lg.shadow-sm.border,
|
||||||
|
#sodino-app .bg-white.rounded-2xl.border {
|
||||||
|
border: 1px solid #d8e0ea;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app aside {
|
||||||
|
position: sticky;
|
||||||
|
top: 42px;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app nav.space-y-2 a {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
padding: 0.7rem 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app nav.space-y-2 a.bg-blue-50,
|
||||||
|
#sodino-app nav.space-y-2 a:hover {
|
||||||
|
background: #e8f3fb;
|
||||||
|
color: #115487 !important;
|
||||||
|
border-color: #b9d9ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app input[type="text"],
|
||||||
|
#sodino-app input[type="number"],
|
||||||
|
#sodino-app input[type="url"],
|
||||||
|
#sodino-app input[type="datetime-local"],
|
||||||
|
#sodino-app input[type="date"],
|
||||||
|
#sodino-app select,
|
||||||
|
#sodino-app textarea {
|
||||||
|
min-height: 42px;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
border: 1px solid #c7d2df !important;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #172033;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app input[type="checkbox"] {
|
||||||
|
border-radius: 4px;
|
||||||
|
border-color: #9fb0c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app button,
|
||||||
|
#sodino-app .inline-flex[href],
|
||||||
|
#sodino-app input[type="submit"] {
|
||||||
|
border-radius: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .wp-list-table {
|
||||||
|
border: 1px solid #d8e0ea;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 10px 26px rgba(15, 23, 42, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .wp-list-table thead th {
|
||||||
|
background: #eef3f8;
|
||||||
|
color: #334155 !important;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app .wp-list-table tbody tr:hover {
|
||||||
|
background: #f6f8fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
#sodino-app .sm\:px-6 { padding-left: 1.5rem; padding-right: 1.5rem; }
|
||||||
|
#sodino-app .sm\:flex-row { flex-direction: row; }
|
||||||
|
#sodino-app .sm\:items-center { align-items: center; }
|
||||||
|
#sodino-app .sm\:justify-between { justify-content: space-between; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
#sodino-app .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
#sodino-app .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||||
|
#sodino-app .md\:col-span-2 { grid-column: span 2 / span 2; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
#sodino-app .lg\:px-8 { padding-left: 2rem; padding-right: 2rem; }
|
||||||
|
#sodino-app .lg\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
#sodino-app .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||||
|
#sodino-app .lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||||
|
#sodino-app .lg\:grid-cols-\[280px_1fr\] { grid-template-columns: 280px minmax(0, 1fr); }
|
||||||
|
#sodino-app .lg\:col-span-2 { grid-column: span 2 / span 2; }
|
||||||
|
#sodino-app .lg\:flex-row { flex-direction: row; }
|
||||||
|
#sodino-app .lg\:items-center { align-items: center; }
|
||||||
|
#sodino-app .lg\:justify-between { justify-content: space-between; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
#sodino-app {
|
||||||
|
padding: 18px 10px 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app > .max-w-7xl > .flex,
|
||||||
|
#sodino-app .max-w-7xl > .flex {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sodino-app aside,
|
||||||
|
#sodino-app .w-64 {
|
||||||
|
width: 100%;
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,165 +11,198 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof Chart === 'undefined') {
|
function toNumbers(values) {
|
||||||
|
return (values || []).map(function (value) {
|
||||||
|
const number = Number(value);
|
||||||
|
return Number.isFinite(number) ? number : 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fitCanvas(canvas) {
|
||||||
|
const ratio = window.devicePixelRatio || 1;
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
canvas.width = Math.max(1, Math.floor(rect.width * ratio));
|
||||||
|
canvas.height = Math.max(1, Math.floor(rect.height * ratio));
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
|
||||||
|
return { ctx, width: rect.width, height: rect.height };
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGrid(ctx, area, lines) {
|
||||||
|
ctx.strokeStyle = 'rgba(148, 163, 184, 0.22)';
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
|
||||||
|
for (let i = 0; i <= lines; i++) {
|
||||||
|
const y = area.top + (area.height / lines) * i;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(area.left, y);
|
||||||
|
ctx.lineTo(area.left + area.width, y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawLineChart(canvas, series) {
|
||||||
|
if (!canvas) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const salesChartContext = document.getElementById('sodinoSalesChart');
|
const fitted = fitCanvas(canvas);
|
||||||
const discountChartContext = document.getElementById('sodinoDiscountChart');
|
const ctx = fitted.ctx;
|
||||||
const ruleChartContext = document.getElementById('sodinoRuleChart');
|
const width = fitted.width;
|
||||||
|
const height = fitted.height;
|
||||||
|
const area = { left: 18, top: 18, width: width - 36, height: height - 42 };
|
||||||
|
const allValues = series.reduce(function (values, item) {
|
||||||
|
return values.concat(item.values);
|
||||||
|
}, []);
|
||||||
|
const max = Math.max(1, ...allValues);
|
||||||
|
|
||||||
if (salesChartContext && dashboardData.salesChart) {
|
ctx.clearRect(0, 0, width, height);
|
||||||
new Chart(salesChartContext.getContext('2d'), {
|
drawGrid(ctx, area, 4);
|
||||||
type: 'line',
|
|
||||||
data: {
|
series.forEach(function (item) {
|
||||||
labels: dashboardData.salesChart.labels,
|
const values = item.values;
|
||||||
datasets: [
|
if (!values.length) {
|
||||||
{
|
return;
|
||||||
label: dashboardData.translations.afterApplying,
|
}
|
||||||
data: dashboardData.salesChart.after,
|
|
||||||
borderColor: '#0ea5e9',
|
ctx.strokeStyle = item.color;
|
||||||
backgroundColor: 'rgba(14, 165, 233, 0.15)',
|
ctx.fillStyle = item.fill;
|
||||||
fill: true,
|
ctx.lineWidth = 2.5;
|
||||||
tension: 0.35,
|
ctx.beginPath();
|
||||||
pointRadius: 3,
|
|
||||||
},
|
values.forEach(function (value, index) {
|
||||||
{
|
const x = area.left + (values.length === 1 ? area.width / 2 : (area.width / (values.length - 1)) * index);
|
||||||
label: dashboardData.translations.beforeApplying,
|
const y = area.top + area.height - ((value / max) * area.height);
|
||||||
data: dashboardData.salesChart.before,
|
if (index === 0) {
|
||||||
borderColor: '#ef4444',
|
ctx.moveTo(x, y);
|
||||||
backgroundColor: 'rgba(239, 68, 68, 0.15)',
|
} else {
|
||||||
fill: true,
|
ctx.lineTo(x, y);
|
||||||
tension: 0.35,
|
}
|
||||||
pointRadius: 3,
|
});
|
||||||
},
|
|
||||||
],
|
ctx.stroke();
|
||||||
},
|
|
||||||
options: {
|
const lastX = area.left + area.width;
|
||||||
responsive: true,
|
ctx.lineTo(lastX, area.top + area.height);
|
||||||
plugins: {
|
ctx.lineTo(area.left, area.top + area.height);
|
||||||
legend: {
|
ctx.closePath();
|
||||||
labels: {
|
ctx.fill();
|
||||||
color: '#334155',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
ticks: {
|
|
||||||
color: '#475569',
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
color: 'rgba(148, 163, 184, 0.15)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
ticks: {
|
|
||||||
color: '#475569',
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
color: 'rgba(148, 163, 184, 0.15)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discountChartContext && dashboardData.summary) {
|
function drawVerticalBars(canvas, labels, values, colors) {
|
||||||
new Chart(discountChartContext.getContext('2d'), {
|
if (!canvas) {
|
||||||
type: 'bar',
|
return;
|
||||||
data: {
|
}
|
||||||
labels: [dashboardData.translations.totalDiscount, dashboardData.translations.totalRevenue],
|
|
||||||
datasets: [
|
const fitted = fitCanvas(canvas);
|
||||||
{
|
const ctx = fitted.ctx;
|
||||||
label: dashboardData.translations.discountEffect,
|
const width = fitted.width;
|
||||||
data: [dashboardData.summary.total_discount, dashboardData.summary.total_revenue],
|
const height = fitted.height;
|
||||||
backgroundColor: ['#fbbf24', '#0ea5e9'],
|
const area = { left: 24, top: 18, width: width - 48, height: height - 52 };
|
||||||
borderRadius: 999,
|
const max = Math.max(1, ...values);
|
||||||
borderSkipped: false,
|
const gap = 18;
|
||||||
},
|
const barWidth = Math.max(18, (area.width - gap * (values.length - 1)) / Math.max(1, values.length));
|
||||||
],
|
|
||||||
},
|
ctx.clearRect(0, 0, width, height);
|
||||||
options: {
|
drawGrid(ctx, area, 4);
|
||||||
responsive: true,
|
ctx.font = '12px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
|
||||||
plugins: {
|
ctx.textAlign = 'center';
|
||||||
legend: {
|
ctx.fillStyle = '#475569';
|
||||||
display: false,
|
|
||||||
},
|
values.forEach(function (value, index) {
|
||||||
},
|
const x = area.left + index * (barWidth + gap);
|
||||||
scales: {
|
const barHeight = (value / max) * area.height;
|
||||||
x: {
|
const y = area.top + area.height - barHeight;
|
||||||
ticks: {
|
|
||||||
color: '#475569',
|
ctx.fillStyle = colors[index % colors.length];
|
||||||
},
|
roundRect(ctx, x, y, barWidth, barHeight, 8);
|
||||||
grid: {
|
ctx.fill();
|
||||||
display: false,
|
|
||||||
},
|
ctx.fillStyle = '#475569';
|
||||||
},
|
ctx.fillText(labels[index] || '', x + barWidth / 2, height - 16);
|
||||||
y: {
|
|
||||||
ticks: {
|
|
||||||
color: '#475569',
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
color: 'rgba(148, 163, 184, 0.15)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ruleChartContext && dashboardData.rulePerformance) {
|
function drawHorizontalBars(canvas, labels, revenue, discount) {
|
||||||
new Chart(ruleChartContext.getContext('2d'), {
|
if (!canvas) {
|
||||||
type: 'bar',
|
return;
|
||||||
data: {
|
}
|
||||||
labels: dashboardData.rulePerformance.names,
|
|
||||||
datasets: [
|
const fitted = fitCanvas(canvas);
|
||||||
{
|
const ctx = fitted.ctx;
|
||||||
label: dashboardData.translations.ruleRevenue,
|
const width = fitted.width;
|
||||||
data: dashboardData.rulePerformance.revenue,
|
const height = fitted.height;
|
||||||
backgroundColor: '#0ea5e9',
|
const area = { left: 18, top: 18, width: width - 36, height: height - 36 };
|
||||||
borderRadius: 999,
|
const rows = Math.max(1, labels.length);
|
||||||
},
|
const max = Math.max(1, ...revenue, ...discount);
|
||||||
{
|
const rowHeight = area.height / rows;
|
||||||
label: dashboardData.translations.ruleDiscount,
|
|
||||||
data: dashboardData.rulePerformance.discount,
|
ctx.clearRect(0, 0, width, height);
|
||||||
backgroundColor: '#f59e0b',
|
ctx.font = '12px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
|
||||||
borderRadius: 999,
|
ctx.textAlign = 'right';
|
||||||
},
|
|
||||||
],
|
labels.forEach(function (label, index) {
|
||||||
},
|
const y = area.top + rowHeight * index + 10;
|
||||||
options: {
|
const labelWidth = Math.min(110, area.width * 0.35);
|
||||||
indexAxis: 'y',
|
const chartLeft = area.left;
|
||||||
responsive: true,
|
const chartWidth = area.width - labelWidth - 12;
|
||||||
plugins: {
|
const revenueWidth = (Number(revenue[index] || 0) / max) * chartWidth;
|
||||||
legend: {
|
const discountWidth = (Number(discount[index] || 0) / max) * chartWidth;
|
||||||
position: 'bottom',
|
|
||||||
labels: {
|
ctx.fillStyle = '#475569';
|
||||||
color: '#475569',
|
ctx.fillText(String(label || '').slice(0, 18), width - 18, y + 14);
|
||||||
},
|
|
||||||
},
|
ctx.fillStyle = '#0ea5e9';
|
||||||
},
|
roundRect(ctx, chartLeft, y, revenueWidth, 8, 6);
|
||||||
scales: {
|
ctx.fill();
|
||||||
x: {
|
|
||||||
ticks: {
|
ctx.fillStyle = '#f59e0b';
|
||||||
color: '#475569',
|
roundRect(ctx, chartLeft, y + 13, discountWidth, 8, 6);
|
||||||
},
|
ctx.fill();
|
||||||
grid: {
|
|
||||||
color: 'rgba(148, 163, 184, 0.15)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
ticks: {
|
|
||||||
color: '#475569',
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function roundRect(ctx, x, y, width, height, radius) {
|
||||||
|
const r = Math.min(radius, Math.abs(width) / 2, Math.abs(height) / 2);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + r, y);
|
||||||
|
ctx.lineTo(x + width - r, y);
|
||||||
|
ctx.quadraticCurveTo(x + width, y, x + width, y + r);
|
||||||
|
ctx.lineTo(x + width, y + height - r);
|
||||||
|
ctx.quadraticCurveTo(x + width, y + height, x + width - r, y + height);
|
||||||
|
ctx.lineTo(x + r, y + height);
|
||||||
|
ctx.quadraticCurveTo(x, y + height, x, y + height - r);
|
||||||
|
ctx.lineTo(x, y + r);
|
||||||
|
ctx.quadraticCurveTo(x, y, x + r, y);
|
||||||
|
ctx.closePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
const sales = dashboardData.salesChart || {};
|
||||||
|
drawLineChart(document.getElementById('sodinoSalesChart'), [
|
||||||
|
{ values: toNumbers(sales.after), color: '#0ea5e9', fill: 'rgba(14, 165, 233, 0.12)' },
|
||||||
|
{ values: toNumbers(sales.before), color: '#ef4444', fill: 'rgba(239, 68, 68, 0.10)' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const summary = dashboardData.summary || {};
|
||||||
|
drawVerticalBars(
|
||||||
|
document.getElementById('sodinoDiscountChart'),
|
||||||
|
[dashboardData.translations.totalDiscount, dashboardData.translations.totalRevenue],
|
||||||
|
toNumbers([summary.total_discount, summary.total_revenue]),
|
||||||
|
['#f59e0b', '#0ea5e9']
|
||||||
|
);
|
||||||
|
|
||||||
|
const rules = dashboardData.rulePerformance || {};
|
||||||
|
drawHorizontalBars(
|
||||||
|
document.getElementById('sodinoRuleChart'),
|
||||||
|
rules.names || [],
|
||||||
|
toNumbers(rules.revenue),
|
||||||
|
toNumbers(rules.discount)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render();
|
||||||
|
window.addEventListener('resize', render);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ if (!defined('ABSPATH')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-banner');
|
$current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-banner');
|
||||||
|
$form_content_type = function_exists('sodino_old_input') ? sodino_old_input('content_type', $banner->content_type) : $banner->content_type;
|
||||||
|
$form_display_type = function_exists('sodino_old_input') ? sodino_old_input('display_type', $banner->display_type) : $banner->display_type;
|
||||||
?>
|
?>
|
||||||
<div id="sodino-app" class="min-h-screen bg-gray-50" dir="rtl">
|
<div id="sodino-app" class="min-h-screen bg-gray-50" dir="rtl">
|
||||||
<div class="bg-white border-b border-gray-200 shadow-sm">
|
<div class="bg-white border-b border-gray-200 shadow-sm">
|
||||||
@@ -22,6 +24,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-banner');
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<?php if (function_exists('sodino_render_admin_notice')) { sodino_render_admin_notice(); } ?>
|
||||||
<div class="grid gap-8 lg:grid-cols-[280px_1fr]">
|
<div class="grid gap-8 lg:grid-cols-[280px_1fr]">
|
||||||
<aside class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm">
|
<aside class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm">
|
||||||
<h2 class="text-lg font-semibold text-gray-900 mb-4"><?php _e('منوی سودینو', 'sodino'); ?></h2>
|
<h2 class="text-lg font-semibold text-gray-900 mb-4"><?php _e('منوی سودینو', 'sodino'); ?></h2>
|
||||||
@@ -32,9 +35,24 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-banner');
|
|||||||
<a href="<?php echo admin_url('admin.php?page=sodino-rules'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-rules' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-rules'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-rules' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('قوانین', 'sodino'); ?>
|
<?php _e('قوانین', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-rule'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-add-rule' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن قانون', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-upsells'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-upsells' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('آپسل (پیشنهاد فروش)', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن آپسل', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-banners' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-banners' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('بنرهای هوشمند', 'sodino'); ?>
|
<?php _e('بنرهای هوشمند', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-banner'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-add-banner' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن بنر', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-settings'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-settings' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-settings'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-settings' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('تنظیمات', 'sodino'); ?>
|
<?php _e('تنظیمات', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
@@ -53,7 +71,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-banner');
|
|||||||
<div class="grid gap-6 lg:grid-cols-2">
|
<div class="grid gap-6 lg:grid-cols-2">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="title"><?php _e('عنوان بنر', 'sodino'); ?></label>
|
<label class="block text-sm font-medium text-gray-700 mb-2" for="title"><?php _e('عنوان بنر', 'sodino'); ?></label>
|
||||||
<input type="text" name="title" id="title" value="<?php echo esc_attr($banner->title); ?>" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100" required>
|
<input type="text" name="title" id="title" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('title', $banner->title) : $banner->title); ?>" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -62,7 +80,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-banner');
|
|||||||
<?php $content_types = ['image' => __('تصویر', 'sodino'), 'html' => __('HTML', 'sodino'), 'shortcode' => __('شورتکد', 'sodino')]; ?>
|
<?php $content_types = ['image' => __('تصویر', 'sodino'), 'html' => __('HTML', 'sodino'), 'shortcode' => __('شورتکد', 'sodino')]; ?>
|
||||||
<?php foreach ($content_types as $value => $label) : ?>
|
<?php foreach ($content_types as $value => $label) : ?>
|
||||||
<label class="flex items-center gap-3 rounded-2xl border border-gray-300 bg-white px-4 py-3 cursor-pointer hover:border-blue-300">
|
<label class="flex items-center gap-3 rounded-2xl border border-gray-300 bg-white px-4 py-3 cursor-pointer hover:border-blue-300">
|
||||||
<input type="radio" name="content_type" value="<?php echo esc_attr($value); ?>" <?php checked($banner->content_type, $value); ?> class="h-4 w-4 text-blue-600 sodino-banner-content-type">
|
<input type="radio" name="content_type" value="<?php echo esc_attr($value); ?>" <?php checked($form_content_type, $value); ?> class="h-4 w-4 text-blue-600 sodino-banner-content-type">
|
||||||
<span class="text-sm text-gray-700"><?php echo esc_html($label); ?></span>
|
<span class="text-sm text-gray-700"><?php echo esc_html($label); ?></span>
|
||||||
</label>
|
</label>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
@@ -74,25 +92,25 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-banner');
|
|||||||
<div class="sodino-banner-content-group" data-type="image">
|
<div class="sodino-banner-content-group" data-type="image">
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="content_value_image"><?php _e('آدرس تصویر بنر', 'sodino'); ?></label>
|
<label class="block text-sm font-medium text-gray-700 mb-2" for="content_value_image"><?php _e('آدرس تصویر بنر', 'sodino'); ?></label>
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<input type="text" name="content_value" id="content_value_image" value="<?php echo esc_attr($banner->content_type === 'image' ? $banner->content_value : ''); ?>" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100">
|
<input type="text" name="content_values[image]" id="content_value_image" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('content_values.image', $banner->content_type === 'image' ? $banner->content_value : '') : ($banner->content_type === 'image' ? $banner->content_value : '')); ?>" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100">
|
||||||
<button type="button" id="sodino-banner-image-upload" class="inline-flex items-center rounded-2xl border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50"><?php _e('انتخاب تصویر', 'sodino'); ?></button>
|
<button type="button" id="sodino-banner-image-upload" class="inline-flex items-center rounded-2xl border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50"><?php _e('انتخاب تصویر', 'sodino'); ?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sodino-banner-content-group" data-type="html">
|
<div class="sodino-banner-content-group" data-type="html">
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="content_value_html"><?php _e('محتوای HTML بنر', 'sodino'); ?></label>
|
<label class="block text-sm font-medium text-gray-700 mb-2" for="content_value_html"><?php _e('محتوای HTML بنر', 'sodino'); ?></label>
|
||||||
<textarea name="content_value" id="content_value_html" rows="6" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100"><?php echo esc_textarea($banner->content_type === 'html' ? $banner->content_value : ''); ?></textarea>
|
<textarea name="content_values[html]" id="content_value_html" rows="6" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100"><?php echo esc_textarea(function_exists('sodino_old_input') ? sodino_old_input('content_values.html', $banner->content_type === 'html' ? $banner->content_value : '') : ($banner->content_type === 'html' ? $banner->content_value : '')); ?></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sodino-banner-content-group" data-type="shortcode">
|
<div class="sodino-banner-content-group" data-type="shortcode">
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="content_value_shortcode"><?php _e('شورتکد بنر', 'sodino'); ?></label>
|
<label class="block text-sm font-medium text-gray-700 mb-2" for="content_value_shortcode"><?php _e('شورتکد بنر', 'sodino'); ?></label>
|
||||||
<textarea name="content_value" id="content_value_shortcode" rows="4" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100"><?php echo esc_textarea($banner->content_type === 'shortcode' ? $banner->content_value : ''); ?></textarea>
|
<textarea name="content_values[shortcode]" id="content_value_shortcode" rows="4" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100"><?php echo esc_textarea(function_exists('sodino_old_input') ? sodino_old_input('content_values.shortcode', $banner->content_type === 'shortcode' ? $banner->content_value : '') : ($banner->content_type === 'shortcode' ? $banner->content_value : '')); ?></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="link_url"><?php _e('لینک بنر (اختیاری)', 'sodino'); ?></label>
|
<label class="block text-sm font-medium text-gray-700 mb-2" for="link_url"><?php _e('لینک بنر (اختیاری)', 'sodino'); ?></label>
|
||||||
<input type="url" name="link_url" id="link_url" value="<?php echo esc_attr($banner->link_url); ?>" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100" placeholder="https://">
|
<input type="url" name="link_url" id="link_url" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('link_url', $banner->link_url) : $banner->link_url); ?>" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100" placeholder="https://">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -106,11 +124,12 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-banner');
|
|||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="position"><?php _e('محل نمایش', 'sodino'); ?></label>
|
<label class="block text-sm font-medium text-gray-700 mb-2" for="position"><?php _e('محل نمایش', 'sodino'); ?></label>
|
||||||
<select name="position" id="position" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100">
|
<select name="position" id="position" class="w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-100">
|
||||||
<option value="top" <?php selected($banner->position, 'top'); ?>><?php _e('بالای سایت', 'sodino'); ?></option>
|
<?php $form_position = function_exists('sodino_old_input') ? sodino_old_input('position', $banner->position) : $banner->position; ?>
|
||||||
<option value="middle" <?php selected($banner->position, 'middle'); ?>><?php _e('وسط محتوا', 'sodino'); ?></option>
|
<option value="top" <?php selected($form_position, 'top'); ?>><?php _e('بالای سایت', 'sodino'); ?></option>
|
||||||
<option value="bottom" <?php selected($banner->position, 'bottom'); ?>><?php _e('پایین', 'sodino'); ?></option>
|
<option value="middle" <?php selected($form_position, 'middle'); ?>><?php _e('وسط محتوا', 'sodino'); ?></option>
|
||||||
<option value="product_page" <?php selected($banner->position, 'product_page'); ?>><?php _e('صفحه محصول', 'sodino'); ?></option>
|
<option value="bottom" <?php selected($form_position, 'bottom'); ?>><?php _e('پایین', 'sodino'); ?></option>
|
||||||
<option value="cart" <?php selected($banner->position, 'cart'); ?>><?php _e('سبد خرید', 'sodino'); ?></option>
|
<option value="product_page" <?php selected($form_position, 'product_page'); ?>><?php _e('صفحه محصول', 'sodino'); ?></option>
|
||||||
|
<option value="cart" <?php selected($form_position, 'cart'); ?>><?php _e('سبد خرید', 'sodino'); ?></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -119,7 +138,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-banner');
|
|||||||
<?php $display_types = ['inline' => __('داخل صفحه', 'sodino'), 'popup' => __('پاپآپ', 'sodino'), 'floating_bar' => __('نوار شناور', 'sodino')]; ?>
|
<?php $display_types = ['inline' => __('داخل صفحه', 'sodino'), 'popup' => __('پاپآپ', 'sodino'), 'floating_bar' => __('نوار شناور', 'sodino')]; ?>
|
||||||
<?php foreach ($display_types as $value => $label) : ?>
|
<?php foreach ($display_types as $value => $label) : ?>
|
||||||
<label class="flex items-center gap-3 rounded-2xl border border-gray-300 bg-white px-4 py-3 cursor-pointer hover:border-blue-300">
|
<label class="flex items-center gap-3 rounded-2xl border border-gray-300 bg-white px-4 py-3 cursor-pointer hover:border-blue-300">
|
||||||
<input type="radio" name="display_type" value="<?php echo esc_attr($value); ?>" <?php checked($banner->display_type, $value); ?> class="h-4 w-4 text-blue-600">
|
<input type="radio" name="display_type" value="<?php echo esc_attr($value); ?>" <?php checked($form_display_type, $value); ?> class="h-4 w-4 text-blue-600">
|
||||||
<span class="text-sm text-gray-700"><?php echo esc_html($label); ?></span>
|
<span class="text-sm text-gray-700"><?php echo esc_html($label); ?></span>
|
||||||
</label>
|
</label>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-banners');
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<?php if (function_exists('sodino_render_admin_notice')) { sodino_render_admin_notice(); } ?>
|
||||||
<div class="grid gap-8 lg:grid-cols-[280px_1fr]">
|
<div class="grid gap-8 lg:grid-cols-[280px_1fr]">
|
||||||
<aside class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm">
|
<aside class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm">
|
||||||
<h2 class="text-lg font-semibold text-gray-900 mb-4"><?php _e('منوی سودینو', 'sodino'); ?></h2>
|
<h2 class="text-lg font-semibold text-gray-900 mb-4"><?php _e('منوی سودینو', 'sodino'); ?></h2>
|
||||||
@@ -32,9 +33,24 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-banners');
|
|||||||
<a href="<?php echo admin_url('admin.php?page=sodino-rules'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-rules' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-rules'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-rules' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('قوانین', 'sodino'); ?>
|
<?php _e('قوانین', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-rule'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-add-rule' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن قانون', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-upsells'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-upsells' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('آپسل (پیشنهاد فروش)', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن آپسل', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-banners' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-banners' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('بنرهای هوشمند', 'sodino'); ?>
|
<?php _e('بنرهای هوشمند', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-banner'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-add-banner' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن بنر', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-settings'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-settings' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-settings'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-settings' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('تنظیمات', 'sodino'); ?>
|
<?php _e('تنظیمات', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-competitor-price');
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<?php if (function_exists('sodino_render_admin_notice')) { sodino_render_admin_notice(); } ?>
|
||||||
<div class="flex gap-8">
|
<div class="flex gap-8">
|
||||||
<aside class="w-64 flex-shrink-0">
|
<aside class="w-64 flex-shrink-0">
|
||||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||||
@@ -41,6 +42,12 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-competitor-price');
|
|||||||
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('افزودن آپسل', 'sodino'); ?>
|
<?php _e('افزودن آپسل', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-banners' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('بنرهای هوشمند', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-banner'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-banner' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن بنر', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-dashboard');
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<?php if (function_exists('sodino_render_admin_notice')) { sodino_render_admin_notice(); } ?>
|
||||||
<div class="flex gap-8">
|
<div class="flex gap-8">
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<aside class="w-64 flex-shrink-0">
|
<aside class="w-64 flex-shrink-0">
|
||||||
@@ -48,6 +49,12 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-dashboard');
|
|||||||
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('افزودن آپسل', 'sodino'); ?>
|
<?php _e('افزودن آپسل', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-banners' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('بنرهای هوشمند', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-banner'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-banner' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن بنر', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ if (!defined('ABSPATH')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-rule');
|
$current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-rule');
|
||||||
|
$form_condition_type = function_exists('sodino_old_input') ? sodino_old_input('condition_type', $rule->condition_type) : $rule->condition_type;
|
||||||
|
$form_action_type = function_exists('sodino_old_input') ? sodino_old_input('action_type', $rule->action_type) : $rule->action_type;
|
||||||
?>
|
?>
|
||||||
<div id="sodino-app" class="min-h-screen bg-gray-50" dir="rtl">
|
<div id="sodino-app" class="min-h-screen bg-gray-50" dir="rtl">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
@@ -22,6 +24,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-rule');
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<?php if (function_exists('sodino_render_admin_notice')) { sodino_render_admin_notice(); } ?>
|
||||||
<div class="flex gap-8">
|
<div class="flex gap-8">
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<aside class="w-64 flex-shrink-0">
|
<aside class="w-64 flex-shrink-0">
|
||||||
@@ -43,6 +46,12 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-rule');
|
|||||||
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('افزودن آپسل', 'sodino'); ?>
|
<?php _e('افزودن آپسل', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-banners' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('بنرهای هوشمند', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-banner'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-banner' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن بنر', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
@@ -74,27 +83,27 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-rule');
|
|||||||
<!-- Form -->
|
<!-- Form -->
|
||||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<?php wp_nonce_field('gheymatyar_save_rule', 'gheymatyar_rule_nonce'); ?>
|
<?php wp_nonce_field('sodino_save_rule', 'sodino_rule_nonce'); ?>
|
||||||
|
|
||||||
<div class="grid gap-6 md:grid-cols-2">
|
<div class="grid gap-6 md:grid-cols-2">
|
||||||
<!-- Rule Name -->
|
<!-- Rule Name -->
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
<label for="name" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('عنوان قانون', 'sodino'); ?></label>
|
<label for="name" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('عنوان قانون', 'sodino'); ?></label>
|
||||||
<input type="text" name="name" id="name" value="<?php echo esc_attr($rule->name); ?>" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" required>
|
<input type="text" name="name" id="name" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('name', $rule->name) : $rule->name); ?>" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" required>
|
||||||
<p class="mt-2 text-sm text-gray-500"><?php _e('نامی کوتاه و قابل فهم برای قانون انتخاب کنید.', 'sodino'); ?></p>
|
<p class="mt-2 text-sm text-gray-500"><?php _e('نامی کوتاه و قابل فهم برای قانون انتخاب کنید.', 'sodino'); ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Priority -->
|
<!-- Priority -->
|
||||||
<div>
|
<div>
|
||||||
<label for="priority" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('اولویت', 'sodino'); ?></label>
|
<label for="priority" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('اولویت', 'sodino'); ?></label>
|
||||||
<input type="number" name="priority" id="priority" value="<?php echo esc_attr($rule->priority ?: 10); ?>" min="1" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
<input type="number" name="priority" id="priority" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('priority', $rule->priority ?: 10) : ($rule->priority ?: 10)); ?>" min="1" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
||||||
<p class="mt-2 text-sm text-gray-500"><?php _e('عدد بالاتر، اولویت بیشتری دارد.', 'sodino'); ?></p>
|
<p class="mt-2 text-sm text-gray-500"><?php _e('عدد بالاتر، اولویت بیشتری دارد.', 'sodino'); ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Usage Limit -->
|
<!-- Usage Limit -->
|
||||||
<div>
|
<div>
|
||||||
<label for="usage_limit" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('محدودیت تعداد استفاده', 'sodino'); ?></label>
|
<label for="usage_limit" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('محدودیت تعداد استفاده', 'sodino'); ?></label>
|
||||||
<input type="number" name="usage_limit" id="usage_limit" value="<?php echo esc_attr($rule->usage_limit ?? 0); ?>" min="0" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
<input type="number" name="usage_limit" id="usage_limit" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('usage_limit', $rule->usage_limit ?? 0) : ($rule->usage_limit ?? 0)); ?>" min="0" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
||||||
<p class="mt-2 text-sm text-gray-500"><?php _e('0 به معنی بدون محدودیت است.', 'sodino'); ?></p>
|
<p class="mt-2 text-sm text-gray-500"><?php _e('0 به معنی بدون محدودیت است.', 'sodino'); ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -113,9 +122,9 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-rule');
|
|||||||
<div>
|
<div>
|
||||||
<label for="condition_type" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('نوع شرط', 'sodino'); ?></label>
|
<label for="condition_type" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('نوع شرط', 'sodino'); ?></label>
|
||||||
<select name="condition_type" id="condition_type" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" required>
|
<select name="condition_type" id="condition_type" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" required>
|
||||||
<option value="user_type" <?php selected($rule->condition_type, 'user_type'); ?>><?php _e('نوع کاربر', 'sodino'); ?></option>
|
<option value="user_type" <?php selected($form_condition_type, 'user_type'); ?>><?php _e('نوع کاربر', 'sodino'); ?></option>
|
||||||
<option value="product_category" <?php selected($rule->condition_type, 'product_category'); ?>><?php _e('دستهبندی محصول', 'sodino'); ?></option>
|
<option value="product_category" <?php selected($form_condition_type, 'product_category'); ?>><?php _e('دستهبندی محصول', 'sodino'); ?></option>
|
||||||
<option value="product_ids" <?php selected($rule->condition_type, 'product_ids'); ?>><?php _e('محصولات خاص', 'sodino'); ?></option>
|
<option value="product_ids" <?php selected($form_condition_type, 'product_ids'); ?>><?php _e('محصولات خاص', 'sodino'); ?></option>
|
||||||
</select>
|
</select>
|
||||||
<p class="mt-2 text-sm text-gray-500"><?php _e('نوع شرطی که باید قبل از اعمال قانون بررسی شود.', 'sodino'); ?></p>
|
<p class="mt-2 text-sm text-gray-500"><?php _e('نوع شرطی که باید قبل از اعمال قانون بررسی شود.', 'sodino'); ?></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -123,7 +132,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-rule');
|
|||||||
<!-- Condition Value -->
|
<!-- Condition Value -->
|
||||||
<div>
|
<div>
|
||||||
<label for="condition_value" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('مقدار شرط', 'sodino'); ?></label>
|
<label for="condition_value" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('مقدار شرط', 'sodino'); ?></label>
|
||||||
<input type="text" name="condition_value" id="condition_value" value="<?php echo esc_attr($rule->condition_value); ?>" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" required>
|
<input type="text" name="condition_value" id="condition_value" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('condition_value', $rule->condition_value) : $rule->condition_value); ?>" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" required>
|
||||||
<p class="mt-2 text-sm text-gray-500"><?php _e('برای کاربر جدید/بازگشتی: new یا returning. برای دستهبندی: شناسه دستهبندی، برای محصولات: شناسه محصول.', 'sodino'); ?></p>
|
<p class="mt-2 text-sm text-gray-500"><?php _e('برای کاربر جدید/بازگشتی: new یا returning. برای دستهبندی: شناسه دستهبندی، برای محصولات: شناسه محصول.', 'sodino'); ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -131,16 +140,16 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-rule');
|
|||||||
<div>
|
<div>
|
||||||
<label for="action_type" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('نوع عملیات', 'sodino'); ?></label>
|
<label for="action_type" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('نوع عملیات', 'sodino'); ?></label>
|
||||||
<select name="action_type" id="action_type" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" required>
|
<select name="action_type" id="action_type" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" required>
|
||||||
<option value="discount_percent" <?php selected($rule->action_type, 'discount_percent'); ?>><?php _e('درصد تخفیف', 'sodino'); ?></option>
|
<option value="discount_percent" <?php selected($form_action_type, 'discount_percent'); ?>><?php _e('درصد تخفیف', 'sodino'); ?></option>
|
||||||
<option value="discount_fixed" <?php selected($rule->action_type, 'discount_fixed'); ?>><?php _e('تخفیف ثابت', 'sodino'); ?></option>
|
<option value="discount_fixed" <?php selected($form_action_type, 'discount_fixed'); ?>><?php _e('تخفیف ثابت', 'sodino'); ?></option>
|
||||||
<option value="free_shipping" <?php selected($rule->action_type, 'free_shipping'); ?>><?php _e('ارسال رایگان', 'sodino'); ?></option>
|
<option value="free_shipping" <?php selected($form_action_type, 'free_shipping'); ?>><?php _e('ارسال رایگان', 'sodino'); ?></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Action Value -->
|
<!-- Action Value -->
|
||||||
<div>
|
<div>
|
||||||
<label for="action_value" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('مقدار عملیات', 'sodino'); ?></label>
|
<label for="action_value" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('مقدار عملیات', 'sodino'); ?></label>
|
||||||
<input type="number" name="action_value" id="action_value" value="<?php echo esc_attr($rule->action_value); ?>" min="0" step="0.01" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
<input type="number" name="action_value" id="action_value" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('action_value', $rule->action_value) : $rule->action_value); ?>" min="0" step="0.01" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
||||||
<p class="mt-2 text-sm text-gray-500"><?php _e('برای درصد یا مقدار ثابت وارد کنید.', 'sodino'); ?></p>
|
<p class="mt-2 text-sm text-gray-500"><?php _e('برای درصد یا مقدار ثابت وارد کنید.', 'sodino'); ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-rules');
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<?php if (function_exists('sodino_render_admin_notice')) { sodino_render_admin_notice(); } ?>
|
||||||
<div class="flex gap-8">
|
<div class="flex gap-8">
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<aside class="w-64 flex-shrink-0">
|
<aside class="w-64 flex-shrink-0">
|
||||||
@@ -43,11 +44,17 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-rules');
|
|||||||
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('افزودن آپسل', 'sodino'); ?>
|
<?php _e('افزودن آپسل', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-banners' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('بنرهای هوشمند', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-banner'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-banner' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن بنر', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-settings'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-settings' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-settings'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-settings' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('تنظیمات', 'sودینو'); ?>
|
<?php _e('تنظیمات', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,18 +5,20 @@ if (!defined('ABSPATH')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-upsell');
|
$current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-upsell');
|
||||||
|
$form_trigger_type = function_exists('sodino_old_input') ? sodino_old_input('trigger_type', $upsell->trigger_type) : $upsell->trigger_type;
|
||||||
|
$form_discount_type = function_exists('sodino_old_input') ? sodino_old_input('discount_type', $upsell->discount_type) : $upsell->discount_type;
|
||||||
$trigger_product = null;
|
$trigger_product = null;
|
||||||
$trigger_product_label = '';
|
$trigger_product_label = '';
|
||||||
$target_product = null;
|
$target_product = null;
|
||||||
$target_product_label = '';
|
$target_product_label = '';
|
||||||
|
|
||||||
if ($upsell->trigger_type === 'product' && $upsell->trigger_value) {
|
if ($form_trigger_type === 'product' && (function_exists('sodino_old_input') ? sodino_old_input('trigger_values.product', $upsell->trigger_value) : $upsell->trigger_value)) {
|
||||||
$trigger_product = wc_get_product(intval($upsell->trigger_value));
|
$trigger_product = wc_get_product(intval(function_exists('sodino_old_input') ? sodino_old_input('trigger_values.product', $upsell->trigger_value) : $upsell->trigger_value));
|
||||||
$trigger_product_label = $trigger_product ? $trigger_product->get_name() : '';
|
$trigger_product_label = $trigger_product ? $trigger_product->get_name() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($upsell->target_product_id) {
|
if (function_exists('sodino_old_input') ? sodino_old_input('target_product_id', $upsell->target_product_id) : $upsell->target_product_id) {
|
||||||
$target_product = wc_get_product($upsell->target_product_id);
|
$target_product = wc_get_product(function_exists('sodino_old_input') ? sodino_old_input('target_product_id', $upsell->target_product_id) : $upsell->target_product_id);
|
||||||
$target_product_label = $target_product ? $target_product->get_name() : '';
|
$target_product_label = $target_product ? $target_product->get_name() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +42,7 @@ $product_categories = get_terms([
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<?php if (function_exists('sodino_render_admin_notice')) { sodino_render_admin_notice(); } ?>
|
||||||
<div class="flex gap-8">
|
<div class="flex gap-8">
|
||||||
<aside class="w-64 flex-shrink-0">
|
<aside class="w-64 flex-shrink-0">
|
||||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||||
@@ -60,6 +63,12 @@ $product_categories = get_terms([
|
|||||||
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('افزودن آپسل', 'sodino'); ?>
|
<?php _e('افزودن آپسل', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-banners' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('بنرهای هوشمند', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-banner'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-banner' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن بنر', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
@@ -90,30 +99,30 @@ $product_categories = get_terms([
|
|||||||
<div class="grid gap-6 md:grid-cols-2">
|
<div class="grid gap-6 md:grid-cols-2">
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
<label for="title" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('عنوان', 'sodino'); ?></label>
|
<label for="title" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('عنوان', 'sodino'); ?></label>
|
||||||
<input type="text" name="title" id="title" value="<?php echo esc_attr($upsell->title); ?>" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" required>
|
<input type="text" name="title" id="title" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('title', $upsell->title) : $upsell->title); ?>" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2"><?php _e('نوع شرط', 'sodino'); ?></label>
|
<label class="block text-sm font-medium text-gray-700 mb-2"><?php _e('نوع شرط', 'sodino'); ?></label>
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<label class="flex items-center gap-3 rounded-lg border border-gray-200 bg-white px-4 py-3 cursor-pointer hover:border-blue-300">
|
<label class="flex items-center gap-3 rounded-lg border border-gray-200 bg-white px-4 py-3 cursor-pointer hover:border-blue-300">
|
||||||
<input type="radio" name="trigger_type" value="product" class="trigger-type" <?php checked($upsell->trigger_type, 'product'); ?>>
|
<input type="radio" name="trigger_type" value="product" class="trigger-type" <?php checked($form_trigger_type, 'product'); ?>>
|
||||||
<span><?php _e('محصول خاص', 'sodino'); ?></span>
|
<span><?php _e('محصول خاص', 'sodino'); ?></span>
|
||||||
</label>
|
</label>
|
||||||
<label class="flex items-center gap-3 rounded-lg border border-gray-200 bg-white px-4 py-3 cursor-pointer hover:border-blue-300">
|
<label class="flex items-center gap-3 rounded-lg border border-gray-200 bg-white px-4 py-3 cursor-pointer hover:border-blue-300">
|
||||||
<input type="radio" name="trigger_type" value="category" class="trigger-type" <?php checked($upsell->trigger_type, 'category'); ?>>
|
<input type="radio" name="trigger_type" value="category" class="trigger-type" <?php checked($form_trigger_type, 'category'); ?>>
|
||||||
<span><?php _e('دستهبندی', 'sodino'); ?></span>
|
<span><?php _e('دستهبندی', 'sodino'); ?></span>
|
||||||
</label>
|
</label>
|
||||||
<label class="flex items-center gap-3 rounded-lg border border-gray-200 bg-white px-4 py-3 cursor-pointer hover:border-blue-300">
|
<label class="flex items-center gap-3 rounded-lg border border-gray-200 bg-white px-4 py-3 cursor-pointer hover:border-blue-300">
|
||||||
<input type="radio" name="trigger_type" value="cart_total" class="trigger-type" <?php checked($upsell->trigger_type, 'cart_total'); ?>>
|
<input type="radio" name="trigger_type" value="cart_total" class="trigger-type" <?php checked($form_trigger_type, 'cart_total'); ?>>
|
||||||
<span><?php _e('مبلغ سبد خرید', 'sodino'); ?></span>
|
<span><?php _e('مبلغ سبد خرید', 'sodino'); ?></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input type="hidden" name="trigger_value" id="trigger_value" value="<?php echo esc_attr($upsell->trigger_value); ?>">
|
<input type="hidden" name="trigger_values[product]" id="trigger_value" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('trigger_values.product', $form_trigger_type === 'product' ? $upsell->trigger_value : '') : ($form_trigger_type === 'product' ? $upsell->trigger_value : '')); ?>">
|
||||||
|
|
||||||
<div id="trigger-product-wrapper" class="md:col-span-2 <?php echo $upsell->trigger_type === 'product' ? '' : 'hidden'; ?>">
|
<div id="trigger-product-wrapper" class="md:col-span-2 <?php echo $form_trigger_type === 'product' ? '' : 'hidden'; ?>">
|
||||||
<label for="trigger_product_search" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('محصول فعالساز', 'sodino'); ?></label>
|
<label for="trigger_product_search" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('محصول فعالساز', 'sodino'); ?></label>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input type="text" id="trigger_product_search" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" placeholder="<?php _e('جستجوی محصول...', 'sodino'); ?>" value="<?php echo esc_attr($trigger_product_label); ?>">
|
<input type="text" id="trigger_product_search" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" placeholder="<?php _e('جستجوی محصول...', 'sodino'); ?>" value="<?php echo esc_attr($trigger_product_label); ?>">
|
||||||
@@ -122,25 +131,25 @@ $product_categories = get_terms([
|
|||||||
<p class="mt-2 text-sm text-gray-500"><?php _e('محصولی که باعث نمایش پیشنهاد آپسل میشود را انتخاب کنید.', 'sodino'); ?></p>
|
<p class="mt-2 text-sm text-gray-500"><?php _e('محصولی که باعث نمایش پیشنهاد آپسل میشود را انتخاب کنید.', 'sodino'); ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="trigger-category-wrapper" class="md:col-span-2 <?php echo $upsell->trigger_type === 'category' ? '' : 'hidden'; ?>">
|
<div id="trigger-category-wrapper" class="md:col-span-2 <?php echo $form_trigger_type === 'category' ? '' : 'hidden'; ?>">
|
||||||
<label for="trigger_value_category" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('دستهبندی', 'sodino'); ?></label>
|
<label for="trigger_value_category" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('دستهبندی', 'sodino'); ?></label>
|
||||||
<select name="trigger_value" id="trigger_value_category" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
<select name="trigger_values[category]" id="trigger_value_category" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
||||||
<option value=""><?php _e('دستهبندی را انتخاب کنید', 'sodino'); ?></option>
|
<option value=""><?php _e('دستهبندی را انتخاب کنید', 'sodino'); ?></option>
|
||||||
<?php foreach ($product_categories as $category) : ?>
|
<?php foreach ($product_categories as $category) : ?>
|
||||||
<option value="<?php echo esc_attr($category->term_id); ?>" <?php selected($upsell->trigger_type, 'category'); selected($upsell->trigger_value, $category->term_id); ?>><?php echo esc_html($category->name); ?></option>
|
<option value="<?php echo esc_attr($category->term_id); ?>" <?php selected((string) (function_exists('sodino_old_input') ? sodino_old_input('trigger_values.category', $form_trigger_type === 'category' ? $upsell->trigger_value : '') : ($form_trigger_type === 'category' ? $upsell->trigger_value : '')), (string) $category->term_id); ?>><?php echo esc_html($category->name); ?></option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="trigger-amount-wrapper" class="md:col-span-2 <?php echo $upsell->trigger_type === 'cart_total' ? '' : 'hidden'; ?>">
|
<div id="trigger-amount-wrapper" class="md:col-span-2 <?php echo $form_trigger_type === 'cart_total' ? '' : 'hidden'; ?>">
|
||||||
<label for="trigger_value_amount" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('مبلغ سبد خرید', 'sodino'); ?></label>
|
<label for="trigger_value_amount" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('مبلغ سبد خرید', 'sodino'); ?></label>
|
||||||
<input type="number" name="trigger_value" id="trigger_value_amount" value="<?php echo esc_attr($upsell->trigger_type === 'cart_total' ? $upsell->trigger_value : ''); ?>" min="0" step="0.01" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
<input type="number" name="trigger_values[cart_total]" id="trigger_value_amount" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('trigger_values.cart_total', $form_trigger_type === 'cart_total' ? $upsell->trigger_value : '') : ($form_trigger_type === 'cart_total' ? $upsell->trigger_value : '')); ?>" min="0" step="0.01" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
<label for="target_product_search" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('محصول پیشنهادی', 'sodino'); ?></label>
|
<label for="target_product_search" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('محصول پیشنهادی', 'sodino'); ?></label>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input type="hidden" name="target_product_id" id="target_product_id" value="<?php echo esc_attr($upsell->target_product_id); ?>">
|
<input type="hidden" name="target_product_id" id="target_product_id" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('target_product_id', $upsell->target_product_id) : $upsell->target_product_id); ?>">
|
||||||
<input type="text" id="target_product_search" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" placeholder="<?php _e('جستجوی محصول پیشنهادی...', 'sodino'); ?>" value="<?php echo esc_attr($target_product_label); ?>">
|
<input type="text" id="target_product_search" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100" placeholder="<?php _e('جستجوی محصول پیشنهادی...', 'sodino'); ?>" value="<?php echo esc_attr($target_product_label); ?>">
|
||||||
<div id="target_product_results" class="absolute z-10 mt-1 w-full rounded-xl border border-gray-200 bg-white shadow-lg hidden"></div>
|
<div id="target_product_results" class="absolute z-10 mt-1 w-full rounded-xl border border-gray-200 bg-white shadow-lg hidden"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,20 +159,20 @@ $product_categories = get_terms([
|
|||||||
<div>
|
<div>
|
||||||
<label for="discount_type" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('نوع تخفیف', 'sodino'); ?></label>
|
<label for="discount_type" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('نوع تخفیف', 'sodino'); ?></label>
|
||||||
<select name="discount_type" id="discount_type" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
<select name="discount_type" id="discount_type" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
||||||
<option value="percentage" <?php selected($upsell->discount_type, 'percentage'); ?>><?php _e('درصدی', 'sodino'); ?></option>
|
<option value="percentage" <?php selected($form_discount_type, 'percentage'); ?>><?php _e('درصدی', 'sodino'); ?></option>
|
||||||
<option value="fixed" <?php selected($upsell->discount_type, 'fixed'); ?>><?php _e('مبلغ ثابت', 'sodino'); ?></option>
|
<option value="fixed" <?php selected($form_discount_type, 'fixed'); ?>><?php _e('مبلغ ثابت', 'sodino'); ?></option>
|
||||||
<option value="none" <?php selected($upsell->discount_type, 'none'); ?>><?php _e('بدون تخفیف', 'sodino'); ?></option>
|
<option value="none" <?php selected($form_discount_type, 'none'); ?>><?php _e('بدون تخفیف', 'sodino'); ?></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="discount_value" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('مقدار تخفیف', 'sodino'); ?></label>
|
<label for="discount_value" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('مقدار تخفیف', 'sodino'); ?></label>
|
||||||
<input type="number" name="discount_value" id="discount_value" value="<?php echo esc_attr($upsell->discount_value); ?>" min="0" step="0.01" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
<input type="number" name="discount_value" id="discount_value" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('discount_value', $upsell->discount_value) : $upsell->discount_value); ?>" min="0" step="0.01" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="priority" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('اولویت', 'sodino'); ?></label>
|
<label for="priority" class="block text-sm font-medium text-gray-700 mb-2"><?php _e('اولویت', 'sodino'); ?></label>
|
||||||
<input type="number" name="priority" id="priority" value="<?php echo esc_attr($upsell->priority); ?>" min="1" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
<input type="number" name="priority" id="priority" value="<?php echo esc_attr(function_exists('sodino_old_input') ? sodino_old_input('priority', $upsell->priority) : $upsell->priority); ?>" min="1" class="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-700 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-100">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-upsells');
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
|
<?php if (function_exists('sodino_render_admin_notice')) { sodino_render_admin_notice(); } ?>
|
||||||
<div class="flex gap-8">
|
<div class="flex gap-8">
|
||||||
<aside class="w-64 flex-shrink-0">
|
<aside class="w-64 flex-shrink-0">
|
||||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||||
@@ -41,6 +42,12 @@ $current_page = sanitize_text_field($_GET['page'] ?? 'sodino-upsells');
|
|||||||
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-upsell'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-upsell' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('افزودن آپسل', 'sodino'); ?>
|
<?php _e('افزودن آپسل', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-banners' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('بنرهای هوشمند', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
|
<a href="<?php echo admin_url('admin.php?page=sodino-add-banner'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-add-banner' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
|
<?php _e('افزودن بنر', 'sodino'); ?>
|
||||||
|
</a>
|
||||||
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
<a href="<?php echo admin_url('admin.php?page=sodino-competitor-price'); ?>" class="block px-3 py-2 rounded-md text-sm font-medium <?php echo $current_page === 'sodino-competitor-price' ? 'bg-blue-50 text-blue-700 border-r-2 border-blue-700' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'; ?>">
|
||||||
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
<?php _e('قیمت رقبا (بهزودی)', 'sodino'); ?>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -15,6 +15,16 @@ class AdminController {
|
|||||||
private $ruleRepository;
|
private $ruleRepository;
|
||||||
private $upsellRepository;
|
private $upsellRepository;
|
||||||
private $bannerRepository;
|
private $bannerRepository;
|
||||||
|
private $allowedBannerContentTypes = ['image', 'html', 'shortcode'];
|
||||||
|
private $allowedBannerPositions = ['top', 'middle', 'bottom', 'product_page', 'cart'];
|
||||||
|
private $allowedBannerDisplayTypes = ['inline', 'popup', 'floating_bar'];
|
||||||
|
private $allowedBannerUserTargets = ['all', 'new', 'returning'];
|
||||||
|
private $allowedBannerDeviceTargets = ['all', 'desktop', 'mobile'];
|
||||||
|
private $allowedUpsellTriggerTypes = ['product', 'category', 'cart_total'];
|
||||||
|
private $allowedUpsellDiscountTypes = ['percentage', 'fixed', 'none'];
|
||||||
|
private $allowedRuleConditionTypes = ['user_type', 'product_category', 'product_ids', 'cart_total_min', 'cart_total_max', 'cart_item_count_min', 'cart_item_count_max'];
|
||||||
|
private $allowedRuleActionTypes = ['discount_percent', 'discount_fixed', 'set_price', 'free_shipping'];
|
||||||
|
private $allowedStrategies = ['priority', 'highest_discount', 'first_valid'];
|
||||||
|
|
||||||
public function __construct(RuleRepository $ruleRepository, UpsellRepository $upsellRepository, BannerRepository $bannerRepository) {
|
public function __construct(RuleRepository $ruleRepository, UpsellRepository $upsellRepository, BannerRepository $bannerRepository) {
|
||||||
$this->ruleRepository = $ruleRepository;
|
$this->ruleRepository = $ruleRepository;
|
||||||
@@ -39,7 +49,7 @@ class AdminController {
|
|||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'sodino-rules',
|
'sodino-rules',
|
||||||
__('قوانین قیمتگذاری', 'sodino'),
|
__('قوانین قیمتگذاری', 'sodino'),
|
||||||
__('قوانین قیمتگذاری', 'sودino'),
|
__('قوانین قیمتگذاری', 'sodino'),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'sodino-rules',
|
'sodino-rules',
|
||||||
[$this, 'rulesPage']
|
[$this, 'rulesPage']
|
||||||
@@ -47,8 +57,8 @@ class AdminController {
|
|||||||
|
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'sodino-rules',
|
'sodino-rules',
|
||||||
__('افزودن قانون', 'sودino'),
|
__('افزودن قانون', 'sodino'),
|
||||||
__('افزودن قانون', 'sودino'),
|
__('افزودن قانون', 'sodino'),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'sodino-add-rule',
|
'sodino-add-rule',
|
||||||
[$this, 'addRulePage']
|
[$this, 'addRulePage']
|
||||||
@@ -101,7 +111,7 @@ class AdminController {
|
|||||||
|
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'sodino-rules',
|
'sodino-rules',
|
||||||
__('داشبورد سودینو', 'sودino'),
|
__('داشبورد سودینو', 'sodino'),
|
||||||
__('داشبورد سودینو', 'sodino'),
|
__('داشبورد سودینو', 'sodino'),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'sodino-dashboard',
|
'sodino-dashboard',
|
||||||
@@ -110,14 +120,61 @@ class AdminController {
|
|||||||
|
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'sodino-rules',
|
'sodino-rules',
|
||||||
__('تنظیمات', 'sودino'),
|
__('تنظیمات', 'sodino'),
|
||||||
__('تنظیمات', 'sودینو'),
|
__('تنظیمات', 'sodino'),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'sodino-settings',
|
'sodino-settings',
|
||||||
[$this, 'settingsPage']
|
[$this, 'settingsPage']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function redirectWithNotice($url, $message, $type = 'error') {
|
||||||
|
$notice = [
|
||||||
|
'type' => $type,
|
||||||
|
'message' => $message,
|
||||||
|
];
|
||||||
|
|
||||||
|
set_transient('sodino_admin_notice_' . get_current_user_id(), $notice, 60);
|
||||||
|
set_transient('sodino_admin_notice', $notice, 60);
|
||||||
|
|
||||||
|
if ($type === 'error' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$oldInput = wp_unslash($_POST);
|
||||||
|
$oldInput['_sodino_page'] = isset($_GET['page']) ? sanitize_key($_GET['page']) : '';
|
||||||
|
set_transient('sodino_old_input_' . get_current_user_id(), $oldInput, 120);
|
||||||
|
} else {
|
||||||
|
delete_transient('sodino_old_input_' . get_current_user_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_safe_redirect($url);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getBackUrl($fallbackPage) {
|
||||||
|
return wp_get_referer() ?: admin_url('admin.php?page=' . $fallbackPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function requireValue($value, $message, $fallbackPage) {
|
||||||
|
if (is_string($value)) {
|
||||||
|
$value = trim($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === '' || $value === null) {
|
||||||
|
$this->redirectWithNotice($this->getBackUrl($fallbackPage), $message, 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizeDatetime($value) {
|
||||||
|
$value = trim((string) $value);
|
||||||
|
if ($value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = strtotime($value);
|
||||||
|
return $timestamp ? date('Y-m-d H:i:s', $timestamp) : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rules admin page
|
* Rules admin page
|
||||||
*/
|
*/
|
||||||
@@ -289,6 +346,10 @@ class AdminController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function saveUpsell($upsell = null) {
|
private function saveUpsell($upsell = null) {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_die(__('دسترسی کافی ندارید.', 'sodino'));
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($_POST['sodino_upsell_nonce']) || !wp_verify_nonce($_POST['sodino_upsell_nonce'], 'sodino_save_upsell')) {
|
if (!isset($_POST['sodino_upsell_nonce']) || !wp_verify_nonce($_POST['sodino_upsell_nonce'], 'sodino_save_upsell')) {
|
||||||
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
||||||
}
|
}
|
||||||
@@ -297,22 +358,49 @@ class AdminController {
|
|||||||
$upsell = new Upsell();
|
$upsell = new Upsell();
|
||||||
}
|
}
|
||||||
|
|
||||||
$upsell->title = sanitize_text_field($_POST['title'] ?? '');
|
$triggerType = sanitize_key($_POST['trigger_type'] ?? 'product');
|
||||||
$upsell->trigger_type = sanitize_text_field($_POST['trigger_type'] ?? 'product');
|
if (!in_array($triggerType, $this->allowedUpsellTriggerTypes, true)) {
|
||||||
$upsell->trigger_value = sanitize_text_field($_POST['trigger_value'] ?? '');
|
$triggerType = 'product';
|
||||||
|
}
|
||||||
|
|
||||||
|
$triggerValues = isset($_POST['trigger_values']) && is_array($_POST['trigger_values']) ? wp_unslash($_POST['trigger_values']) : [];
|
||||||
|
$triggerValue = sanitize_text_field($triggerValues[$triggerType] ?? '');
|
||||||
|
$targetProductId = max(0, intval($_POST['target_product_id'] ?? 0));
|
||||||
|
$discountType = sanitize_key($_POST['discount_type'] ?? 'percentage');
|
||||||
|
if (!in_array($discountType, $this->allowedUpsellDiscountTypes, true)) {
|
||||||
|
$discountType = 'percentage';
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = sanitize_text_field($_POST['title'] ?? '');
|
||||||
|
$this->requireValue($title, __('عنوان آپسل الزامی است.', 'sodino'), 'sodino-add-upsell');
|
||||||
|
$this->requireValue($triggerValue, __('مقدار شرط آپسل را انتخاب یا وارد کنید.', 'sodino'), 'sodino-add-upsell');
|
||||||
|
|
||||||
|
if ($targetProductId <= 0) {
|
||||||
|
$this->redirectWithNotice($this->getBackUrl('sodino-add-upsell'), __('محصول پیشنهادی آپسل الزامی است.', 'sodino'), 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
$upsell->title = $title;
|
||||||
|
$upsell->trigger_type = $triggerType;
|
||||||
|
$upsell->trigger_value = $triggerValue;
|
||||||
$upsell->target_product_id = max(0, intval($_POST['target_product_id'] ?? 0));
|
$upsell->target_product_id = max(0, intval($_POST['target_product_id'] ?? 0));
|
||||||
$upsell->discount_type = sanitize_text_field($_POST['discount_type'] ?? 'percentage');
|
$upsell->discount_type = $discountType;
|
||||||
$upsell->discount_value = max(0, floatval($_POST['discount_value'] ?? 0));
|
$upsell->discount_value = max(0, floatval($_POST['discount_value'] ?? 0));
|
||||||
$upsell->priority = max(1, intval($_POST['priority'] ?? 10));
|
$upsell->priority = max(1, intval($_POST['priority'] ?? 10));
|
||||||
$upsell->status = isset($_POST['status']) ? 1 : 0;
|
$upsell->status = isset($_POST['status']) ? 1 : 0;
|
||||||
|
|
||||||
$this->upsellRepository->save($upsell);
|
$id = $this->upsellRepository->save($upsell);
|
||||||
|
if (!$id) {
|
||||||
|
$this->redirectWithNotice($this->getBackUrl('sodino-add-upsell'), __('ذخیره آپسل انجام نشد. لطفا مقادیر فرم را بررسی کنید.', 'sodino'), 'error');
|
||||||
|
}
|
||||||
|
|
||||||
wp_safe_redirect(admin_url('admin.php?page=sodino-upsells'));
|
$this->redirectWithNotice(admin_url('admin.php?page=sodino-upsells'), __('آپسل با موفقیت ذخیره شد.', 'sodino'), 'success');
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function saveBanner($banner = null) {
|
private function saveBanner($banner = null) {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_die(__('دسترسی کافی ندارید.', 'sodino'));
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($_POST['sodino_banner_nonce']) || !wp_verify_nonce($_POST['sodino_banner_nonce'], 'sodino_save_banner')) {
|
if (!isset($_POST['sodino_banner_nonce']) || !wp_verify_nonce($_POST['sodino_banner_nonce'], 'sodino_save_banner')) {
|
||||||
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
||||||
}
|
}
|
||||||
@@ -321,26 +409,60 @@ class AdminController {
|
|||||||
$banner = new Banner();
|
$banner = new Banner();
|
||||||
}
|
}
|
||||||
|
|
||||||
$banner->title = sanitize_text_field($_POST['title'] ?? '');
|
$contentType = sanitize_key($_POST['content_type'] ?? 'image');
|
||||||
$banner->content_type = sanitize_text_field($_POST['content_type'] ?? 'image');
|
if (!in_array($contentType, $this->allowedBannerContentTypes, true)) {
|
||||||
$banner->content_value = isset($_POST['content_value']) ? wp_kses_post($_POST['content_value']) : '';
|
$contentType = 'image';
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentValues = isset($_POST['content_values']) && is_array($_POST['content_values']) ? wp_unslash($_POST['content_values']) : [];
|
||||||
|
$contentValue = $contentValues[$contentType] ?? '';
|
||||||
|
$contentValue = $contentType === 'image' ? esc_url_raw($contentValue) : wp_kses_post($contentValue);
|
||||||
|
|
||||||
|
$title = sanitize_text_field($_POST['title'] ?? '');
|
||||||
|
$this->requireValue($title, __('عنوان بنر الزامی است.', 'sodino'), 'sodino-add-banner');
|
||||||
|
$this->requireValue($contentValue, __('محتوای بنر الزامی است.', 'sodino'), 'sodino-add-banner');
|
||||||
|
|
||||||
|
if ($contentType === 'image' && !filter_var($contentValue, FILTER_VALIDATE_URL)) {
|
||||||
|
$this->redirectWithNotice($this->getBackUrl('sodino-add-banner'), __('آدرس تصویر بنر معتبر نیست.', 'sodino'), 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
$startTime = $this->normalizeDatetime($_POST['start_time'] ?? '');
|
||||||
|
$endTime = $this->normalizeDatetime($_POST['end_time'] ?? '');
|
||||||
|
if ($startTime && $endTime && strtotime($endTime) < strtotime($startTime)) {
|
||||||
|
$this->redirectWithNotice($this->getBackUrl('sodino-add-banner'), __('تاریخ پایان بنر نباید قبل از تاریخ شروع باشد.', 'sodino'), 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
$position = sanitize_key($_POST['position'] ?? 'top');
|
||||||
|
$displayType = sanitize_key($_POST['display_type'] ?? 'inline');
|
||||||
|
$userTarget = sanitize_key($_POST['user_target'] ?? 'all');
|
||||||
|
$deviceTarget = sanitize_key($_POST['device_target'] ?? 'all');
|
||||||
|
|
||||||
|
$banner->title = $title;
|
||||||
|
$banner->content_type = $contentType;
|
||||||
|
$banner->content_value = $contentValue;
|
||||||
$banner->link_url = esc_url_raw($_POST['link_url'] ?? '');
|
$banner->link_url = esc_url_raw($_POST['link_url'] ?? '');
|
||||||
$banner->position = sanitize_text_field($_POST['position'] ?? 'top');
|
$banner->position = in_array($position, $this->allowedBannerPositions, true) ? $position : 'top';
|
||||||
$banner->display_type = sanitize_text_field($_POST['display_type'] ?? 'inline');
|
$banner->display_type = in_array($displayType, $this->allowedBannerDisplayTypes, true) ? $displayType : 'inline';
|
||||||
$banner->start_time = sanitize_text_field($_POST['start_time'] ?? '');
|
$banner->start_time = $startTime;
|
||||||
$banner->end_time = sanitize_text_field($_POST['end_time'] ?? '');
|
$banner->end_time = $endTime;
|
||||||
$banner->user_target = sanitize_text_field($_POST['user_target'] ?? 'all');
|
$banner->user_target = in_array($userTarget, $this->allowedBannerUserTargets, true) ? $userTarget : 'all';
|
||||||
$banner->device_target = sanitize_text_field($_POST['device_target'] ?? 'all');
|
$banner->device_target = in_array($deviceTarget, $this->allowedBannerDeviceTargets, true) ? $deviceTarget : 'all';
|
||||||
$banner->priority = max(1, intval($_POST['priority'] ?? 10));
|
$banner->priority = max(1, intval($_POST['priority'] ?? 10));
|
||||||
$banner->status = isset($_POST['status']) ? 1 : 0;
|
$banner->status = isset($_POST['status']) ? 1 : 0;
|
||||||
|
|
||||||
$this->bannerRepository->save($banner);
|
$id = $this->bannerRepository->save($banner);
|
||||||
|
if (!$id) {
|
||||||
|
$this->redirectWithNotice($this->getBackUrl('sodino-add-banner'), __('ذخیره بنر انجام نشد. لطفا مقادیر فرم را بررسی کنید.', 'sodino'), 'error');
|
||||||
|
}
|
||||||
|
|
||||||
wp_safe_redirect(admin_url('admin.php?page=sodino-banners'));
|
$this->redirectWithNotice(admin_url('admin.php?page=sodino-banners'), __('بنر با موفقیت ذخیره شد.', 'sodino'), 'success');
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleUpsellActions() {
|
public function handleUpsellActions() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($_GET['_wpnonce']) || !in_array($_GET['action'], ['delete_upsell', 'toggle_upsell_status'], true) || !wp_verify_nonce($_GET['_wpnonce'], $_GET['action'])) {
|
if (!isset($_GET['_wpnonce']) || !in_array($_GET['action'], ['delete_upsell', 'toggle_upsell_status'], true) || !wp_verify_nonce($_GET['_wpnonce'], $_GET['action'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -368,6 +490,10 @@ class AdminController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function handleBannerActions() {
|
public function handleBannerActions() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($_GET['_wpnonce']) || !in_array($_GET['action'], ['delete_banner', 'toggle_banner_status'], true) || !wp_verify_nonce($_GET['_wpnonce'], $_GET['action'])) {
|
if (!isset($_GET['_wpnonce']) || !in_array($_GET['action'], ['delete_banner', 'toggle_banner_status'], true) || !wp_verify_nonce($_GET['_wpnonce'], $_GET['action'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -395,6 +521,10 @@ class AdminController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function searchProductsAjax() {
|
public function searchProductsAjax() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_send_json([]);
|
||||||
|
}
|
||||||
|
|
||||||
if (!check_ajax_referer('sodino_search_products', 'security', false)) {
|
if (!check_ajax_referer('sodino_search_products', 'security', false)) {
|
||||||
wp_send_json([]);
|
wp_send_json([]);
|
||||||
}
|
}
|
||||||
@@ -426,13 +556,17 @@ class AdminController {
|
|||||||
'plugin_enabled' => 1,
|
'plugin_enabled' => 1,
|
||||||
'pricing_enabled' => 1,
|
'pricing_enabled' => 1,
|
||||||
'upsell_enabled' => 1,
|
'upsell_enabled' => 1,
|
||||||
|
'banner_enabled' => 1,
|
||||||
'allow_multiple_rules' => 0,
|
'allow_multiple_rules' => 0,
|
||||||
'strategy' => 'priority',
|
'strategy' => 'priority',
|
||||||
'max_discount_percent' => 100,
|
'max_discount_percent' => 100,
|
||||||
'min_product_price' => 0,
|
'min_product_price' => 0,
|
||||||
|
'cache_enabled' => 1,
|
||||||
|
'cache_duration' => 3600,
|
||||||
'ab_testing_enabled' => 0,
|
'ab_testing_enabled' => 0,
|
||||||
'cart_pricing_enabled' => 1,
|
'cart_pricing_enabled' => 1,
|
||||||
'scheduled_campaigns_enabled' => 1,
|
'scheduled_campaigns_enabled' => 1,
|
||||||
|
'debug_mode' => 0,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,23 +583,31 @@ class AdminController {
|
|||||||
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$strategy = sanitize_key($_POST['strategy'] ?? 'priority');
|
||||||
|
if (!in_array($strategy, $this->allowedStrategies, true)) {
|
||||||
|
$strategy = 'priority';
|
||||||
|
}
|
||||||
|
|
||||||
$settings = [
|
$settings = [
|
||||||
'plugin_enabled' => isset($_POST['plugin_enabled']) ? 1 : 0,
|
'plugin_enabled' => isset($_POST['plugin_enabled']) ? 1 : 0,
|
||||||
'pricing_enabled' => isset($_POST['pricing_enabled']) ? 1 : 0,
|
'pricing_enabled' => isset($_POST['pricing_enabled']) ? 1 : 0,
|
||||||
'upsell_enabled' => isset($_POST['upsell_enabled']) ? 1 : 0,
|
'upsell_enabled' => isset($_POST['upsell_enabled']) ? 1 : 0,
|
||||||
|
'banner_enabled' => isset($_POST['banner_enabled']) ? 1 : 0,
|
||||||
'allow_multiple_rules' => isset($_POST['allow_multiple_rules']) ? 1 : 0,
|
'allow_multiple_rules' => isset($_POST['allow_multiple_rules']) ? 1 : 0,
|
||||||
'strategy' => sanitize_text_field($_POST['strategy'] ?? 'priority'),
|
'strategy' => $strategy,
|
||||||
'max_discount_percent' => max(0, min(100, floatval($_POST['max_discount_percent'] ?? 100))),
|
'max_discount_percent' => max(0, min(100, floatval($_POST['max_discount_percent'] ?? 100))),
|
||||||
'min_product_price' => max(0, floatval($_POST['min_product_price'] ?? 0)),
|
'min_product_price' => max(0, floatval($_POST['min_product_price'] ?? 0)),
|
||||||
|
'cache_enabled' => isset($_POST['cache_enabled']) ? 1 : 0,
|
||||||
|
'cache_duration' => max(60, intval($_POST['cache_duration'] ?? 3600)),
|
||||||
'ab_testing_enabled' => isset($_POST['ab_testing_enabled']) ? 1 : 0,
|
'ab_testing_enabled' => isset($_POST['ab_testing_enabled']) ? 1 : 0,
|
||||||
'cart_pricing_enabled' => isset($_POST['cart_pricing_enabled']) ? 1 : 0,
|
'cart_pricing_enabled' => isset($_POST['cart_pricing_enabled']) ? 1 : 0,
|
||||||
'scheduled_campaigns_enabled' => isset($_POST['scheduled_campaigns_enabled']) ? 1 : 0,
|
'scheduled_campaigns_enabled' => isset($_POST['scheduled_campaigns_enabled']) ? 1 : 0,
|
||||||
|
'debug_mode' => isset($_POST['debug_mode']) ? 1 : 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
update_option('sodino_settings', $settings);
|
update_option('sodino_settings', $settings);
|
||||||
|
|
||||||
wp_safe_redirect(add_query_arg('updated', 'true', admin_url('admin.php?page=sodino-settings')));
|
$this->redirectWithNotice(add_query_arg('updated', 'true', admin_url('admin.php?page=sodino-settings')), __('تنظیمات با موفقیت ذخیره شد.', 'sodino'), 'success');
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -490,7 +632,11 @@ class AdminController {
|
|||||||
* Save rule
|
* Save rule
|
||||||
*/
|
*/
|
||||||
private function saveRule($rule = null) {
|
private function saveRule($rule = null) {
|
||||||
if (!isset($_POST['gheymatyar_rule_nonce']) || !wp_verify_nonce($_POST['gheymatyar_rule_nonce'], 'gheymatyar_save_rule')) {
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_die(__('دسترسی کافی ندارید.', 'sodino'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_POST['sodino_rule_nonce']) || !wp_verify_nonce($_POST['sodino_rule_nonce'], 'sodino_save_rule')) {
|
||||||
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,20 +644,53 @@ class AdminController {
|
|||||||
$rule = new Rule();
|
$rule = new Rule();
|
||||||
}
|
}
|
||||||
|
|
||||||
$rule->name = sanitize_text_field($_POST['name'] ?? '');
|
$name = sanitize_text_field($_POST['name'] ?? '');
|
||||||
|
$this->requireValue($name, __('عنوان قانون الزامی است.', 'sodino'), 'sodino-add-rule');
|
||||||
|
|
||||||
|
$conditionType = sanitize_key($_POST['condition_type'] ?? 'user_type');
|
||||||
|
if (!in_array($conditionType, $this->allowedRuleConditionTypes, true)) {
|
||||||
|
$conditionType = 'user_type';
|
||||||
|
}
|
||||||
|
|
||||||
|
$conditionValue = sanitize_text_field($_POST['condition_value'] ?? '');
|
||||||
|
$this->requireValue($conditionValue, __('مقدار شرط قانون الزامی است.', 'sodino'), 'sodino-add-rule');
|
||||||
|
|
||||||
|
$actionType = sanitize_key($_POST['action_type'] ?? 'discount_percent');
|
||||||
|
if (!in_array($actionType, $this->allowedRuleActionTypes, true)) {
|
||||||
|
$actionType = 'discount_percent';
|
||||||
|
}
|
||||||
|
|
||||||
|
$actionValue = sanitize_text_field($_POST['action_value'] ?? '0');
|
||||||
|
if ($actionType !== 'free_shipping' && floatval($actionValue) <= 0) {
|
||||||
|
$this->redirectWithNotice($this->getBackUrl('sodino-add-rule'), __('مقدار عملیات باید بزرگتر از صفر باشد.', 'sodino'), 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule->name = $name;
|
||||||
$rule->priority = max(1, intval($_POST['priority'] ?? 10));
|
$rule->priority = max(1, intval($_POST['priority'] ?? 10));
|
||||||
$rule->usage_limit = max(0, intval($_POST['usage_limit'] ?? 0));
|
$rule->usage_limit = max(0, intval($_POST['usage_limit'] ?? 0));
|
||||||
$rule->user_roles = array_map('sanitize_text_field', (array) ($_POST['user_roles'] ?? []));
|
$rule->user_roles = array_map('sanitize_text_field', (array) ($_POST['user_roles'] ?? []));
|
||||||
$rule->condition_type = sanitize_text_field($_POST['condition_type'] ?? 'user_type');
|
|
||||||
$rule->condition_value = sanitize_text_field($_POST['condition_value'] ?? 'new');
|
$rule->conditions = [
|
||||||
$rule->action_type = sanitize_text_field($_POST['action_type'] ?? 'discount_percent');
|
[
|
||||||
$rule->action_value = sanitize_text_field($_POST['action_value'] ?? '0');
|
'type' => $conditionType,
|
||||||
|
'value' => $conditionValue,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$rule->actions = [
|
||||||
|
[
|
||||||
|
'type' => $actionType,
|
||||||
|
'value' => $actionValue,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$rule->syncLegacyFields();
|
||||||
$rule->enabled = isset($_POST['enabled']) ? 1 : 0;
|
$rule->enabled = isset($_POST['enabled']) ? 1 : 0;
|
||||||
|
|
||||||
$this->ruleRepository->save($rule);
|
$id = $this->ruleRepository->save($rule);
|
||||||
|
if (!$id) {
|
||||||
|
$this->redirectWithNotice($this->getBackUrl('sodino-add-rule'), __('ذخیره قانون انجام نشد. لطفا مقادیر فرم را بررسی کنید.', 'sodino'), 'error');
|
||||||
|
}
|
||||||
|
|
||||||
wp_safe_redirect(admin_url('admin.php?page=sodino-rules'));
|
$this->redirectWithNotice(admin_url('admin.php?page=sodino-rules'), __('قانون با موفقیت ذخیره شد.', 'sodino'), 'success');
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -525,7 +704,7 @@ class AdminController {
|
|||||||
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||||
$this->ruleRepository->delete($id);
|
$this->ruleRepository->delete($id);
|
||||||
|
|
||||||
wp_redirect(admin_url('admin.php?page=sodino-rules'));
|
wp_safe_redirect(admin_url('admin.php?page=sodino-rules'));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,6 @@ class RuleController extends BaseController {
|
|||||||
$rule->end_date = !empty($_POST['end_date']) ? sanitize_text_field($_POST['end_date']) : null;
|
$rule->end_date = !empty($_POST['end_date']) ? sanitize_text_field($_POST['end_date']) : null;
|
||||||
$rule->enabled = isset($_POST['enabled']) ? 1 : 0;
|
$rule->enabled = isset($_POST['enabled']) ? 1 : 0;
|
||||||
|
|
||||||
// Parse conditions
|
|
||||||
if (isset($_POST['conditions']) && is_array($_POST['conditions'])) {
|
if (isset($_POST['conditions']) && is_array($_POST['conditions'])) {
|
||||||
$rule->conditions = array_map(function($condition) {
|
$rule->conditions = array_map(function($condition) {
|
||||||
return [
|
return [
|
||||||
@@ -174,9 +173,15 @@ class RuleController extends BaseController {
|
|||||||
'value' => sanitize_text_field($condition['value'] ?? '')
|
'value' => sanitize_text_field($condition['value'] ?? '')
|
||||||
];
|
];
|
||||||
}, $_POST['conditions']);
|
}, $_POST['conditions']);
|
||||||
|
} else {
|
||||||
|
$rule->conditions = [
|
||||||
|
[
|
||||||
|
'type' => sanitize_text_field($_POST['condition_type'] ?? 'user_type'),
|
||||||
|
'value' => sanitize_text_field($_POST['condition_value'] ?? 'new'),
|
||||||
|
],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse actions
|
|
||||||
if (isset($_POST['actions']) && is_array($_POST['actions'])) {
|
if (isset($_POST['actions']) && is_array($_POST['actions'])) {
|
||||||
$rule->actions = array_map(function($action) {
|
$rule->actions = array_map(function($action) {
|
||||||
return [
|
return [
|
||||||
@@ -184,6 +189,15 @@ class RuleController extends BaseController {
|
|||||||
'value' => sanitize_text_field($action['value'] ?? '')
|
'value' => sanitize_text_field($action['value'] ?? '')
|
||||||
];
|
];
|
||||||
}, $_POST['actions']);
|
}, $_POST['actions']);
|
||||||
|
} else {
|
||||||
|
$rule->actions = [
|
||||||
|
[
|
||||||
|
'type' => sanitize_text_field($_POST['action_type'] ?? 'discount_percent'),
|
||||||
|
'value' => sanitize_text_field($_POST['action_value'] ?? '0'),
|
||||||
|
],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$rule->syncLegacyFields();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ class Rule {
|
|||||||
public $enabled;
|
public $enabled;
|
||||||
public $created_at;
|
public $created_at;
|
||||||
public $updated_at;
|
public $updated_at;
|
||||||
|
public $condition_type;
|
||||||
|
public $condition_value;
|
||||||
|
public $action_type;
|
||||||
|
public $action_value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@@ -36,6 +40,7 @@ class Rule {
|
|||||||
$this->enabled = isset($data['enabled']) ? (int) $data['enabled'] : 1;
|
$this->enabled = isset($data['enabled']) ? (int) $data['enabled'] : 1;
|
||||||
$this->created_at = $data['created_at'] ?? null;
|
$this->created_at = $data['created_at'] ?? null;
|
||||||
$this->updated_at = $data['updated_at'] ?? null;
|
$this->updated_at = $data['updated_at'] ?? null;
|
||||||
|
$this->syncLegacyFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseJsonField($value) {
|
private function parseJsonField($value) {
|
||||||
@@ -59,6 +64,16 @@ class Rule {
|
|||||||
return array_filter(array_map('trim', explode(',', $value)));
|
return array_filter(array_map('trim', explode(',', $value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function syncLegacyFields() {
|
||||||
|
$condition = $this->conditions[0] ?? [];
|
||||||
|
$action = $this->actions[0] ?? [];
|
||||||
|
|
||||||
|
$this->condition_type = $condition['type'] ?? 'user_type';
|
||||||
|
$this->condition_value = $condition['value'] ?? 'new';
|
||||||
|
$this->action_type = $action['type'] ?? 'discount_percent';
|
||||||
|
$this->action_value = $action['value'] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert to array
|
* Convert to array
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -46,12 +46,20 @@ class BannerRepository {
|
|||||||
unset($data['id'], $data['created_at']);
|
unset($data['id'], $data['created_at']);
|
||||||
|
|
||||||
if ($banner->id) {
|
if ($banner->id) {
|
||||||
$wpdb->update($this->table_name, $data, ['id' => $banner->id]);
|
$result = $wpdb->update($this->table_name, $data, ['id' => $banner->id]);
|
||||||
|
if ($result === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$this->clearCache();
|
$this->clearCache();
|
||||||
return $banner->id;
|
return $banner->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
$wpdb->insert($this->table_name, $data);
|
$result = $wpdb->insert($this->table_name, $data);
|
||||||
|
if ($result === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$this->clearCache();
|
$this->clearCache();
|
||||||
return $wpdb->insert_id;
|
return $wpdb->insert_id;
|
||||||
}
|
}
|
||||||
@@ -76,6 +84,12 @@ class BannerRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function clearCache() {
|
public function clearCache() {
|
||||||
wp_cache_flush();
|
$version = microtime(true);
|
||||||
|
update_option('sodino_banners_cache_version', $version, false);
|
||||||
|
wp_cache_set('version', $version, 'sodino_banners');
|
||||||
|
|
||||||
|
if (function_exists('wp_cache_flush_group')) {
|
||||||
|
wp_cache_flush_group('sodino_banners');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,10 +87,18 @@ class RuleRepository {
|
|||||||
unset($data['id'], $data['created_at'], $data['updated_at']);
|
unset($data['id'], $data['created_at'], $data['updated_at']);
|
||||||
|
|
||||||
if ($rule->id) {
|
if ($rule->id) {
|
||||||
$wpdb->update($this->table_name, $data, ['id' => $rule->id]);
|
$result = $wpdb->update($this->table_name, $data, ['id' => $rule->id]);
|
||||||
|
if ($result === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$id = $rule->id;
|
$id = $rule->id;
|
||||||
} else {
|
} else {
|
||||||
$wpdb->insert($this->table_name, $data);
|
$result = $wpdb->insert($this->table_name, $data);
|
||||||
|
if ($result === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$id = $wpdb->insert_id;
|
$id = $wpdb->insert_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,19 @@ class UpsellRepository {
|
|||||||
unset($data['id'], $data['created_at'], $data['updated_at']);
|
unset($data['id'], $data['created_at'], $data['updated_at']);
|
||||||
|
|
||||||
if ($upsell->id) {
|
if ($upsell->id) {
|
||||||
$wpdb->update($this->table_name, $data, ['id' => $upsell->id]);
|
$result = $wpdb->update($this->table_name, $data, ['id' => $upsell->id]);
|
||||||
|
if ($result === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return $upsell->id;
|
return $upsell->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
$wpdb->insert($this->table_name, $data);
|
$result = $wpdb->insert($this->table_name, $data);
|
||||||
|
if ($result === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return $wpdb->insert_id;
|
return $wpdb->insert_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,12 @@ class BannerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function getCacheKey($position, array $context) {
|
private function getCacheKey($position, array $context) {
|
||||||
return 'sodino_active_banners_' . md5($position . '|' . serialize($context));
|
$version = wp_cache_get('version', 'sodino_banners');
|
||||||
|
if ($version === false) {
|
||||||
|
$version = get_option('sodino_banners_cache_version', 1);
|
||||||
|
wp_cache_set('version', $version, 'sodino_banners');
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'sodino_active_banners_' . md5($version . '|' . $position . '|' . serialize($context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class PricingService {
|
|||||||
private $trackingService;
|
private $trackingService;
|
||||||
private $settings;
|
private $settings;
|
||||||
private $cache;
|
private $cache;
|
||||||
|
private $trackedApplications = [];
|
||||||
|
|
||||||
public function __construct(RuleRepository $ruleRepository, TrackingService $trackingService) {
|
public function __construct(RuleRepository $ruleRepository, TrackingService $trackingService) {
|
||||||
$this->ruleRepository = $ruleRepository;
|
$this->ruleRepository = $ruleRepository;
|
||||||
@@ -51,8 +52,7 @@ class PricingService {
|
|||||||
$price = $this->applyRuleActions($rule, $price);
|
$price = $this->applyRuleActions($rule, $price);
|
||||||
|
|
||||||
if ($price < $oldPrice) {
|
if ($price < $oldPrice) {
|
||||||
$this->trackingService->recordDiscountApplied($product, $oldPrice, $price, $rule->id);
|
$this->trackDiscountOnce($product, $oldPrice, $price, $rule->id);
|
||||||
$this->ruleRepository->incrementUsage($rule->id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ class PricingService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (bool) array_intersect($product_cats, array_map('intval', $categories));
|
return (bool) array_intersect($product_cats, $this->normalizeIdList($categories));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function productIsInIds($product, $ids) {
|
private function productIsInIds($product, $ids) {
|
||||||
@@ -214,7 +214,23 @@ class PricingService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return in_array($product->get_id(), array_map('intval', $ids), true);
|
return in_array($product->get_id(), $this->normalizeIdList($ids), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizeIdList($value) {
|
||||||
|
$values = is_array($value) ? $value : [$value];
|
||||||
|
$ids = [];
|
||||||
|
|
||||||
|
foreach ($values as $item) {
|
||||||
|
foreach (explode(',', (string) $item) as $id) {
|
||||||
|
$id = absint(trim($id));
|
||||||
|
if ($id > 0) {
|
||||||
|
$ids[] = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values(array_unique($ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function applyRuleActions($rule, $price) {
|
private function applyRuleActions($rule, $price) {
|
||||||
@@ -273,4 +289,17 @@ class PricingService {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function trackDiscountOnce($product, $oldPrice, $price, $ruleId) {
|
||||||
|
$productId = $product ? $product->get_id() : 0;
|
||||||
|
$key = implode(':', [$productId, (int) $ruleId, round($oldPrice, 4), round($price, 4)]);
|
||||||
|
|
||||||
|
if (isset($this->trackedApplications[$key])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->trackedApplications[$key] = true;
|
||||||
|
$this->trackingService->recordDiscountApplied($product, $oldPrice, $price, $ruleId);
|
||||||
|
$this->ruleRepository->incrementUsage($ruleId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ global $sodino_banner_service;
|
|||||||
$bannerRepository = new BannerRepository();
|
$bannerRepository = new BannerRepository();
|
||||||
$sodino_banner_service = new BannerService($bannerRepository);
|
$sodino_banner_service = new BannerService($bannerRepository);
|
||||||
|
|
||||||
add_action('wp_head', 'sodino_render_top_banner', 1);
|
add_action('wp_body_open', 'sodino_render_top_banner', 5);
|
||||||
|
add_action('wp_footer', 'sodino_render_top_banner_fallback', 5);
|
||||||
add_filter('the_content', 'sodino_render_middle_banner');
|
add_filter('the_content', 'sodino_render_middle_banner');
|
||||||
add_action('wp_footer', 'sodino_render_bottom_banner', 20);
|
add_action('wp_footer', 'sodino_render_bottom_banner', 20);
|
||||||
add_action('woocommerce_after_single_product_summary', 'sodino_render_product_banner', 5);
|
add_action('woocommerce_after_single_product_summary', 'sodino_render_product_banner', 5);
|
||||||
@@ -101,13 +102,28 @@ function sodino_render_banner_position($position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sodino_render_top_banner() {
|
function sodino_render_top_banner() {
|
||||||
|
static $rendered = false;
|
||||||
|
|
||||||
if (is_admin()) {
|
if (is_admin()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($rendered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rendered = true;
|
||||||
echo sodino_render_banner_position('top');
|
echo sodino_render_banner_position('top');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sodino_render_top_banner_fallback() {
|
||||||
|
if (did_action('wp_body_open')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sodino_render_top_banner();
|
||||||
|
}
|
||||||
|
|
||||||
function sodino_render_middle_banner($content) {
|
function sodino_render_middle_banner($content) {
|
||||||
if (is_admin() || !is_singular() || !in_the_loop() || is_feed()) {
|
if (is_admin() || !is_singular() || !in_the_loop() || is_feed()) {
|
||||||
return $content;
|
return $content;
|
||||||
@@ -146,7 +162,8 @@ function sodino_render_cart_banner() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sodino_handle_banner_click() {
|
function sodino_handle_banner_click() {
|
||||||
if (!isset($_POST['banner_id']) || !isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'sodino_banner_click')) {
|
$nonce = $_POST['nonce'] ?? ($_POST['security'] ?? '');
|
||||||
|
if (!isset($_POST['banner_id']) || !wp_verify_nonce($nonce, 'sodino_banner_click')) {
|
||||||
wp_send_json_error();
|
wp_send_json_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,17 +22,7 @@ add_filter('woocommerce_product_get_sale_price', 'sodino_apply_dynamic_pricing',
|
|||||||
add_filter('woocommerce_product_variation_get_price', 'sodino_apply_dynamic_pricing', 10, 2);
|
add_filter('woocommerce_product_variation_get_price', 'sodino_apply_dynamic_pricing', 10, 2);
|
||||||
add_filter('woocommerce_product_variation_get_sale_price', 'sodino_apply_dynamic_pricing', 10, 2);
|
add_filter('woocommerce_product_variation_get_sale_price', 'sodino_apply_dynamic_pricing', 10, 2);
|
||||||
|
|
||||||
// Also hook into cart and checkout prices
|
|
||||||
add_filter('woocommerce_cart_item_price', 'sodino_apply_to_cart_item', 10, 3);
|
|
||||||
add_filter('woocommerce_cart_item_subtotal', 'sodino_apply_to_cart_item', 10, 3);
|
|
||||||
|
|
||||||
function sodino_apply_dynamic_pricing($price, $product) {
|
function sodino_apply_dynamic_pricing($price, $product) {
|
||||||
global $sodino_pricing_service;
|
global $sodino_pricing_service;
|
||||||
return $sodino_pricing_service->applyDynamicPricing($price, $product);
|
return $sodino_pricing_service->applyDynamicPricing($price, $product);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sodino_apply_to_cart_item($price, $cart_item, $cart_item_key) {
|
|
||||||
global $sodino_pricing_service;
|
|
||||||
$product = $cart_item['data'];
|
|
||||||
return wc_price($sodino_pricing_service->applyDynamicPricing($product->get_price(), $product));
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
||||||
body: 'action=sodino_banner_click&banner_id=' + encodeURIComponent(bannerId) + '&security=' + encodeURIComponent(window.sodinoBannerFrontend.nonce),
|
body: 'action=sodino_banner_click&banner_id=' + encodeURIComponent(bannerId) + '&nonce=' + encodeURIComponent(window.sodinoBannerFrontend.nonce),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user