feat: Add banner management functionality
- Implemented a new Banner model to represent banner data. - Created a BannerRepository for database interactions related to banners. - Developed a BannerService to handle business logic for banners. - Added admin views for listing and adding banners. - Integrated banner hooks for frontend rendering and click tracking. - Created frontend styles and scripts for banner display and interaction. - Updated database migrations to include a new banners table. - Enhanced AdminController to manage banner actions and pages.
This commit is contained in:
@@ -5,13 +5,15 @@ if (!defined('ABSPATH')) {
|
||||
}
|
||||
|
||||
use Sodino\Controllers\AdminController;
|
||||
use Sodino\Repositories\BannerRepository;
|
||||
use Sodino\Repositories\RuleRepository;
|
||||
use Sodino\Repositories\UpsellRepository;
|
||||
|
||||
// Initialize admin
|
||||
$ruleRepository = new RuleRepository();
|
||||
$upsellRepository = new UpsellRepository();
|
||||
$adminController = new AdminController($ruleRepository, $upsellRepository);
|
||||
$bannerRepository = new BannerRepository();
|
||||
$adminController = new AdminController($ruleRepository, $upsellRepository, $bannerRepository);
|
||||
|
||||
// Add menu
|
||||
add_action('admin_menu', [$adminController, 'addMenu']);
|
||||
@@ -41,13 +43,20 @@ add_action('admin_enqueue_scripts', function($hook) use ($adminController) {
|
||||
'nonce' => wp_create_nonce('sodino_search_products'),
|
||||
]);
|
||||
}
|
||||
|
||||
if (strpos($hook, 'sodino_page_sodino-add-banner') !== false) {
|
||||
wp_enqueue_media();
|
||||
wp_enqueue_script('sodino-banner-admin', plugin_dir_url(__FILE__) . 'js/banner-admin.js', ['jquery'], SODINO_VERSION, true);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle delete for any Sodino admin page
|
||||
if (isset($_GET['page']) && strpos($_GET['page'], 'sodino') === 0 && isset($_GET['action']) && $_GET['action'] === 'delete') {
|
||||
add_action('admin_init', [$adminController, 'handleDelete']);
|
||||
}
|
||||
|
||||
if (isset($_GET['page']) && strpos($_GET['page'], 'sodino') === 0 && isset($_GET['action']) && in_array($_GET['action'], ['delete_banner', 'toggle_banner_status'], true)) {
|
||||
add_action('admin_init', [$adminController, 'handleBannerActions']);
|
||||
}
|
||||
// Handle upsell actions
|
||||
if (isset($_GET['page']) && strpos($_GET['page'], 'sodino') === 0 && isset($_GET['action']) && in_array($_GET['action'], ['delete_upsell', 'toggle_upsell_status'], true)) {
|
||||
add_action('admin_init', [$adminController, 'handleUpsellActions']);
|
||||
|
||||
142
admin/class-banner-list-table.php
Normal file
142
admin/class-banner-list-table.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!class_exists('WP_List_Table')) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||
}
|
||||
|
||||
class Sodino_Banner_List_Table extends WP_List_Table {
|
||||
private $repository;
|
||||
private $items_per_page = 20;
|
||||
|
||||
public function __construct($repository) {
|
||||
parent::__construct([
|
||||
'singular' => 'sodino_banner',
|
||||
'plural' => 'sodino_banners',
|
||||
'ajax' => false,
|
||||
]);
|
||||
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function get_columns() {
|
||||
return [
|
||||
'cb' => '<input type="checkbox" />',
|
||||
'title' => __('عنوان', 'sodino'),
|
||||
'position' => __('محل نمایش', 'sodino'),
|
||||
'display_type' => __('نوع نمایش', 'sodino'),
|
||||
'schedule' => __('زمانبندی', 'sodino'),
|
||||
'status' => __('وضعیت', 'sodino'),
|
||||
'stats' => __('آمار', 'sodino'),
|
||||
'actions' => __('عملیات', 'sodino'),
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_sortable_columns() {
|
||||
return [
|
||||
'priority' => ['priority', true],
|
||||
'title' => ['title', true],
|
||||
];
|
||||
}
|
||||
|
||||
protected function column_cb($item) {
|
||||
return sprintf('<input type="checkbox" name="banner_ids[]" value="%d" />', $item->id);
|
||||
}
|
||||
|
||||
public function get_bulk_actions() {
|
||||
return [
|
||||
'delete' => __('حذف گروهی', 'sodino'),
|
||||
];
|
||||
}
|
||||
|
||||
public function column_title($item) {
|
||||
$edit_url = admin_url('admin.php?page=sodino-add-banner&action=edit&id=' . $item->id);
|
||||
return sprintf('<strong><a href="%s">%s</a></strong>', esc_url($edit_url), esc_html($item->title));
|
||||
}
|
||||
|
||||
public function column_position($item) {
|
||||
$map = [
|
||||
'top' => __('بالای سایت', 'sodino'),
|
||||
'middle' => __('وسط محتوا', 'sodino'),
|
||||
'bottom' => __('پایین', 'sodino'),
|
||||
'product_page' => __('صفحه محصول', 'sodino'),
|
||||
'cart' => __('سبد خرید', 'sodino'),
|
||||
];
|
||||
return $map[$item->position] ?? __('نامشخص', 'sodino');
|
||||
}
|
||||
|
||||
public function column_display_type($item) {
|
||||
$map = [
|
||||
'inline' => __('درون صفحه', 'sodino'),
|
||||
'popup' => __('پاپآپ', 'sodino'),
|
||||
'floating_bar' => __('نوار شناور', 'sodino'),
|
||||
];
|
||||
return $map[$item->display_type] ?? __('نامشخص', 'sodino');
|
||||
}
|
||||
|
||||
public function column_schedule($item) {
|
||||
if (!empty($item->start_time) || !empty($item->end_time)) {
|
||||
$start = $item->start_time ? date_i18n('Y/m/d H:i', strtotime($item->start_time)) : __('بدون شروع', 'sodino');
|
||||
$end = $item->end_time ? date_i18n('Y/m/d H:i', strtotime($item->end_time)) : __('بدون پایان', 'sodino');
|
||||
return sprintf('%s<br><small class="description">%s</small>', esc_html($start), esc_html($end));
|
||||
}
|
||||
return __('بدون محدودیت زمانی', 'sodino');
|
||||
}
|
||||
|
||||
public function column_status($item) {
|
||||
return $item->status ? __('فعال', 'sodino') : __('غیرفعال', 'sodino');
|
||||
}
|
||||
|
||||
public function column_stats($item) {
|
||||
$ctr = $item->impressions > 0 ? round(($item->clicks / $item->impressions) * 100, 2) : 0;
|
||||
return sprintf('<div class="sodino-stats-grid"><p>%s: <strong>%s</strong></p><p>%s: <strong>%s</strong></p><p>CTR: <strong>%s%%</strong></p></div>',
|
||||
__('نمایش', 'sodino'), esc_html(number_format_i18n($item->impressions)),
|
||||
__('کلیک', 'sodino'), esc_html(number_format_i18n($item->clicks)),
|
||||
esc_html(number_format_i18n($ctr))
|
||||
);
|
||||
}
|
||||
|
||||
public function column_actions($item) {
|
||||
$edit_url = admin_url('admin.php?page=sodino-add-banner&action=edit&id=' . $item->id);
|
||||
$toggle_url = wp_nonce_url(admin_url('admin.php?page=sodino-banners&action=toggle_banner_status&id=' . $item->id), 'toggle_banner_status');
|
||||
$delete_url = wp_nonce_url(admin_url('admin.php?page=sodino-banners&action=delete_banner&id=' . $item->id), 'delete_banner');
|
||||
$toggle_label = $item->status ? __('غیرفعال کردن', 'sodino') : __('فعال کردن', 'sodino');
|
||||
|
||||
return sprintf(
|
||||
'<a href="%s">%s</a> | <a href="%s">%s</a> | <a href="%s" onclick="return confirm(\'%s\');">%s</a>',
|
||||
esc_url($edit_url), esc_html__('ویرایش', 'sodino'),
|
||||
esc_url($toggle_url), esc_html($toggle_label),
|
||||
esc_url($delete_url), esc_js(__('آیا از حذف این بنر مطمئن هستید؟', 'sodino')),
|
||||
esc_html__('حذف', 'sodino')
|
||||
);
|
||||
}
|
||||
|
||||
public function prepare_items() {
|
||||
$this->_column_headers = [$this->get_columns(), [], $this->get_sortable_columns()];
|
||||
$this->process_bulk_action();
|
||||
|
||||
$all_items = $this->repository->getAll();
|
||||
$current_page = $this->get_pagenum();
|
||||
$total_items = count($all_items);
|
||||
$this->items = array_slice($all_items, ($current_page - 1) * $this->items_per_page, $this->items_per_page);
|
||||
|
||||
$this->set_pagination_args([
|
||||
'total_items' => $total_items,
|
||||
'per_page' => $this->items_per_page,
|
||||
'total_pages' => ceil($total_items / $this->items_per_page),
|
||||
]);
|
||||
}
|
||||
|
||||
public function process_bulk_action() {
|
||||
if ('delete' === $this->current_action()) {
|
||||
$banner_ids = isset($_POST['banner_ids']) ? array_map('intval', $_POST['banner_ids']) : [];
|
||||
if (!empty($banner_ids) && check_admin_referer('bulk-' . $this->_args['plural'])) {
|
||||
foreach ($banner_ids as $id) {
|
||||
$this->repository->delete($id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,3 +294,94 @@
|
||||
#sodino-app .sd-chart-card {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
#sodino-app {
|
||||
min-height: 100vh;
|
||||
background: radial-gradient(circle at top right, rgba(99, 102, 241, 0.14), transparent 22%),
|
||||
radial-gradient(circle at bottom left, rgba(59, 130, 246, 0.08), transparent 15%),
|
||||
#f8fafc;
|
||||
padding: 30px 16px 50px;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
#sodino-app .bg-white.rounded-lg.shadow-sm.border,
|
||||
#sodino-app .bg-white.rounded-2xl.border {
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border-color: rgba(148, 163, 184, 0.70);
|
||||
box-shadow: 0 18px 35px rgba(15, 23, 42, 0.10);
|
||||
}
|
||||
|
||||
#sodino-app .bg-white.rounded-lg.shadow-sm.border,
|
||||
#sodino-app .bg-white.rounded-2xl.border,
|
||||
#sodino-app .bg-gray-50,
|
||||
#sodino-app .sd-sidebar,
|
||||
#sodino-app .sd-card,
|
||||
#sodino-app .sd-chart-card {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
#sodino-app .bg-gray-50 {
|
||||
background: #f7fbff !important;
|
||||
border-color: rgba(148, 163, 184, 0.35);
|
||||
}
|
||||
|
||||
#sodino-app .shadow-sm {
|
||||
box-shadow: 0 14px 30px rgba(15, 23, 42, 0.09) !important;
|
||||
}
|
||||
|
||||
#sodino-app .sd-sidebar,
|
||||
#sodino-app .sd-card,
|
||||
#sodino-app .sd-chart-card {
|
||||
border-color: rgba(148, 163, 184, 0.65);
|
||||
}
|
||||
|
||||
#sodino-app .rounded-lg,
|
||||
#sodino-app .rounded-2xl,
|
||||
#sodino-app .rounded-md {
|
||||
border-radius: 1.3rem !important;
|
||||
}
|
||||
|
||||
#sodino-app .space-y-2 > a {
|
||||
display: block;
|
||||
padding: 0.95rem 1rem;
|
||||
border-radius: 1rem;
|
||||
transition: all 0.18s ease-in-out;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
#sodino-app .space-y-2 > a:hover {
|
||||
background: rgba(99, 102, 241, 0.1);
|
||||
}
|
||||
|
||||
#sodino-app .text-gray-500,
|
||||
#sodino-app .text-gray-600,
|
||||
#sodino-app .text-gray-700 {
|
||||
color: #334155 !important;
|
||||
}
|
||||
|
||||
#sodino-app h1,
|
||||
#sodino-app h2,
|
||||
#sodino-app h3,
|
||||
#sodino-app h4 {
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
#sodino-app .bg-white.rounded-lg.shadow-sm.border p,
|
||||
#sodino-app .bg-white.rounded-2xl.border p {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
#sodino-app .bg-white.rounded-lg.shadow-sm.border .inline-flex,
|
||||
#sodino-app .bg-white.rounded-2xl.border .inline-flex {
|
||||
box-shadow: 0 10px 22px rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
#sodino-app table thead th {
|
||||
color: #334155 !important;
|
||||
}
|
||||
|
||||
#sodino-app .wp-list-table .column-name,
|
||||
#sodino-app .wp-list-table .column-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
75
admin/js/banner-admin.js
Normal file
75
admin/js/banner-admin.js
Normal file
@@ -0,0 +1,75 @@
|
||||
(function () {
|
||||
const contentTypeRadios = document.querySelectorAll('.sodino-banner-content-type');
|
||||
const contentGroups = document.querySelectorAll('.sodino-banner-content-group');
|
||||
const mediaButton = document.getElementById('sodino-banner-image-upload');
|
||||
const imageInput = document.getElementById('content_value_image');
|
||||
const presetButtons = document.querySelectorAll('.sodino-banner-preset');
|
||||
const startInput = document.getElementById('start_time');
|
||||
const endInput = document.getElementById('end_time');
|
||||
|
||||
function toggleFields() {
|
||||
const selectedType = document.querySelector('.sodino-banner-content-type:checked');
|
||||
const type = selectedType ? selectedType.value : 'image';
|
||||
contentGroups.forEach((group) => {
|
||||
group.style.display = group.dataset.type === type ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
if (contentTypeRadios.length) {
|
||||
contentTypeRadios.forEach((radio) => radio.addEventListener('change', toggleFields));
|
||||
toggleFields();
|
||||
}
|
||||
|
||||
if (mediaButton && imageInput && window.wp && wp.media) {
|
||||
mediaButton.addEventListener('click', function () {
|
||||
const mediaFrame = wp.media({
|
||||
title: 'انتخاب تصویر بنر',
|
||||
button: { text: 'انتخاب' },
|
||||
multiple: false,
|
||||
});
|
||||
|
||||
mediaFrame.on('select', function () {
|
||||
const attachment = mediaFrame.state().get('selection').first().toJSON();
|
||||
imageInput.value = attachment.url;
|
||||
});
|
||||
|
||||
mediaFrame.open();
|
||||
});
|
||||
}
|
||||
|
||||
const setDatetimeLocal = (element, date) => {
|
||||
if (!element) return;
|
||||
const tzOffset = date.getTimezoneOffset() * 60000;
|
||||
const localISO = new Date(date - tzOffset).toISOString().slice(0, 16);
|
||||
element.value = localISO;
|
||||
};
|
||||
|
||||
if (presetButtons.length) {
|
||||
presetButtons.forEach((button) => {
|
||||
button.addEventListener('click', function () {
|
||||
const preset = this.dataset.preset;
|
||||
const now = new Date();
|
||||
if (preset === 'today') {
|
||||
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0);
|
||||
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59);
|
||||
setDatetimeLocal(startInput, start);
|
||||
setDatetimeLocal(endInput, end);
|
||||
}
|
||||
if (preset === 'weekend') {
|
||||
const friday = new Date(now);
|
||||
friday.setDate(friday.getDate() + ((5 - friday.getDay() + 7) % 7));
|
||||
const saturday = new Date(friday);
|
||||
saturday.setDate(friday.getDate() + 1);
|
||||
setDatetimeLocal(startInput, new Date(friday.getFullYear(), friday.getMonth(), friday.getDate(), 0, 0));
|
||||
setDatetimeLocal(endInput, new Date(saturday.getFullYear(), saturday.getMonth(), saturday.getDate(), 23, 59));
|
||||
}
|
||||
if (preset === 'hourly') {
|
||||
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0);
|
||||
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 0);
|
||||
setDatetimeLocal(startInput, start);
|
||||
setDatetimeLocal(endInput, end);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
219
admin/views/banner-form.php
Normal file
219
admin/views/banner-form.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$current_page = sanitize_text_field($_GET['page'] ?? 'sodino-add-banner');
|
||||
?>
|
||||
<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="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-900"><?php echo $banner->id ? __('ویرایش بنر', 'sodino') : __('افزودن بنر جدید', 'sodino'); ?></h1>
|
||||
<p class="mt-1 text-sm text-gray-500"><?php _e('تنظیمات تبلیغات هوشمند و زمانبندی نمایش بنر در سایت.', 'sodino'); ?></p>
|
||||
</div>
|
||||
<a href="<?php echo admin_url('admin.php?page=sodino-banners'); ?>" class="inline-flex items-center gap-2 rounded-full bg-white px-5 py-3 text-sm font-semibold text-gray-700 border border-gray-200 shadow-sm hover:bg-gray-50">
|
||||
<?php _e('بازگشت به لیست بنرها', 'sodino'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="grid gap-8 lg:grid-cols-[280px_1fr]">
|
||||
<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>
|
||||
<nav class="space-y-2">
|
||||
<a href="<?php echo admin_url('admin.php?page=sodino-dashboard'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-dashboard' ? '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-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'); ?>
|
||||
</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'; ?>">
|
||||
<?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'; ?>">
|
||||
<?php _e('تنظیمات', 'sodino'); ?>
|
||||
</a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<main class="space-y-6">
|
||||
<form method="post">
|
||||
<?php wp_nonce_field('sodino_save_banner', 'sodino_banner_nonce'); ?>
|
||||
<div class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm space-y-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-gray-900"><?php _e('اطلاعات اصلی', 'sodino'); ?></h2>
|
||||
<p class="mt-2 text-sm text-gray-500"><?php _e('عنوان، محتوا و لینک بنر را تعریف کنید.', 'sodino'); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-2">
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span class="block text-sm font-medium text-gray-700 mb-2"><?php _e('نوع محتوا', 'sodino'); ?></span>
|
||||
<div class="space-y-2">
|
||||
<?php $content_types = ['image' => __('تصویر', 'sodino'), 'html' => __('HTML', 'sodino'), 'shortcode' => __('شورتکد', 'sodino')]; ?>
|
||||
<?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">
|
||||
<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">
|
||||
<span class="text-sm text-gray-700"><?php echo esc_html($label); ?></span>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6">
|
||||
<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>
|
||||
<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">
|
||||
<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 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>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<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://">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm space-y-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-gray-900"><?php _e('نمایش', 'sodino'); ?></h2>
|
||||
<p class="mt-2 text-sm text-gray-500"><?php _e('محل و سبک نمایش بنر را مشخص کنید.', 'sodino'); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-2">
|
||||
<div>
|
||||
<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">
|
||||
<option value="top" <?php selected($banner->position, 'top'); ?>><?php _e('بالای سایت', 'sodino'); ?></option>
|
||||
<option value="middle" <?php selected($banner->position, 'middle'); ?>><?php _e('وسط محتوا', 'sodino'); ?></option>
|
||||
<option value="bottom" <?php selected($banner->position, 'bottom'); ?>><?php _e('پایین', 'sodino'); ?></option>
|
||||
<option value="product_page" <?php selected($banner->position, 'product_page'); ?>><?php _e('صفحه محصول', 'sodino'); ?></option>
|
||||
<option value="cart" <?php selected($banner->position, 'cart'); ?>><?php _e('سبد خرید', 'sodino'); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2"><?php _e('نوع نمایش', 'sodino'); ?></label>
|
||||
<div class="grid gap-2">
|
||||
<?php $display_types = ['inline' => __('داخل صفحه', 'sodino'), 'popup' => __('پاپآپ', 'sodino'), 'floating_bar' => __('نوار شناور', 'sodino')]; ?>
|
||||
<?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">
|
||||
<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">
|
||||
<span class="text-sm text-gray-700"><?php echo esc_html($label); ?></span>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm space-y-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-gray-900"><?php _e('زمانبندی', 'sodino'); ?></h2>
|
||||
<p class="mt-2 text-sm text-gray-500"><?php _e('زمان شروع و پایان نمایش بنر را تنظیم کنید.', 'sodino'); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<button type="button" class="sodino-banner-preset inline-flex items-center justify-center rounded-2xl border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-sm hover:border-blue-300" data-preset="today"><?php _e('فقط امروز', 'sodino'); ?></button>
|
||||
<button type="button" class="sodino-banner-preset inline-flex items-center justify-center rounded-2xl border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-sm hover:border-blue-300" data-preset="weekend"><?php _e('فقط آخر هفته', 'sodino'); ?></button>
|
||||
<button type="button" class="sodino-banner-preset inline-flex items-center justify-center rounded-2xl border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-sm hover:border-blue-300" data-preset="hourly"><?php _e('ساعتی (18:00 تا 23:00)', 'sodino'); ?></button>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="start_time"><?php _e('تاریخ شروع', 'sodino'); ?></label>
|
||||
<input type="datetime-local" name="start_time" id="start_time" value="<?php echo esc_attr($banner->start_time ? date('Y-m-d\TH:i', strtotime($banner->start_time)) : ''); ?>" 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">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="end_time"><?php _e('تاریخ پایان', 'sodino'); ?></label>
|
||||
<input type="datetime-local" name="end_time" id="end_time" value="<?php echo esc_attr($banner->end_time ? date('Y-m-d\TH:i', strtotime($banner->end_time)) : ''); ?>" 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">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm space-y-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-gray-900"><?php _e('هدفگذاری', 'sodino'); ?></h2>
|
||||
<p class="mt-2 text-sm text-gray-500"><?php _e('نوع کاربر و دستگاه نمایش بنر را مشخص کنید.', 'sodino'); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="user_target"><?php _e('نوع کاربر', 'sodino'); ?></label>
|
||||
<select name="user_target" id="user_target" 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="all" <?php selected($banner->user_target, 'all'); ?>><?php _e('همه', 'sodino'); ?></option>
|
||||
<option value="new" <?php selected($banner->user_target, 'new'); ?>><?php _e('کاربر جدید', 'sodino'); ?></option>
|
||||
<option value="returning" <?php selected($banner->user_target, 'returning'); ?>><?php _e('بازگشتی', 'sodino'); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="device_target"><?php _e('دستگاه', 'sodino'); ?></label>
|
||||
<select name="device_target" id="device_target" 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="all" <?php selected($banner->device_target, 'all'); ?>><?php _e('همه', 'sodino'); ?></option>
|
||||
<option value="desktop" <?php selected($banner->device_target, 'desktop'); ?>><?php _e('دسکتاپ', 'sodino'); ?></option>
|
||||
<option value="mobile" <?php selected($banner->device_target, 'mobile'); ?>><?php _e('موبایل', 'sodino'); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm space-y-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-gray-900"><?php _e('تنظیمات پیشرفته', 'sodino'); ?></h2>
|
||||
<p class="mt-2 text-sm text-gray-500"><?php _e('اولویت و وضعیت نمایش بنر را تنظیم کنید.', 'sodino'); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="priority"><?php _e('اولویت', 'sodino'); ?></label>
|
||||
<input type="number" name="priority" id="priority" value="<?php echo esc_attr($banner->priority); ?>" min="1" 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">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="impressions"><?php _e('نمایشها', 'sodino'); ?></label>
|
||||
<input type="number" id="impressions" value="<?php echo esc_attr($banner->impressions); ?>" disabled class="w-full rounded-2xl border border-gray-300 bg-gray-100 px-4 py-3 text-gray-700 shadow-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2" for="clicks"><?php _e('کلیکها', 'sodino'); ?></label>
|
||||
<input type="number" id="clicks" value="<?php echo esc_attr($banner->clicks); ?>" disabled class="w-full rounded-2xl border border-gray-300 bg-gray-100 px-4 py-3 text-gray-700 shadow-sm">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<label class="inline-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="checkbox" name="status" value="1" <?php checked($banner->status, 1); ?> class="h-4 w-4 text-blue-600">
|
||||
<span class="text-sm text-gray-700"><?php _e('فعال باشد', 'sodino'); ?></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm text-right">
|
||||
<button type="submit" class="inline-flex items-center justify-center rounded-2xl bg-blue-600 px-6 py-3 text-sm font-semibold text-white shadow-sm hover:bg-blue-700 transition-colors">
|
||||
<?php echo $banner->id ? __('بهروزرسانی بنر', 'sodino') : __('ذخیره بنر', 'sodino'); ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
66
admin/views/banner-list.php
Normal file
66
admin/views/banner-list.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$current_page = sanitize_text_field($_GET['page'] ?? 'sodino-banners');
|
||||
?>
|
||||
<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="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-900"><?php _e('بنرهای هوشمند', 'sodino'); ?></h1>
|
||||
<p class="mt-1 text-sm text-gray-500"><?php _e('مدیریت بنرهای زمانبندی شده و هدفمند سایت.', 'sodino'); ?></p>
|
||||
</div>
|
||||
<a href="<?php echo admin_url('admin.php?page=sodino-add-banner'); ?>" class="inline-flex items-center gap-2 rounded-full bg-blue-600 px-5 py-3 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">
|
||||
<?php _e('افزودن بنر جدید', 'sodino'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="grid gap-8 lg:grid-cols-[280px_1fr]">
|
||||
<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>
|
||||
<nav class="space-y-2">
|
||||
<a href="<?php echo admin_url('admin.php?page=sodino-dashboard'); ?>" class="block px-3 py-2 rounded-xl text-sm font-medium <?php echo $current_page === 'sodino-dashboard' ? '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-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'); ?>
|
||||
</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'; ?>">
|
||||
<?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'; ?>">
|
||||
<?php _e('تنظیمات', 'sodino'); ?>
|
||||
</a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<main class="space-y-6">
|
||||
<div class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-gray-900"><?php _e('لیست بنرها', 'sodino'); ?></h2>
|
||||
<p class="mt-1 text-sm text-gray-500"><?php _e('نمایش، ویرایش، فعال/غیرفعال و حذف گروهی بنرها.', 'sodino'); ?></p>
|
||||
</div>
|
||||
<span class="inline-flex items-center rounded-full bg-blue-50 px-4 py-2 text-sm font-medium text-blue-700">
|
||||
<?php _e('حداکثر ۲ بنر همزمان نمایش داده میشوند', 'sodino'); ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm">
|
||||
<form method="post">
|
||||
<?php wp_nonce_field('bulk-sodino_banners'); ?>
|
||||
<?php $bannerTable->display(); ?>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user