feat(ui): implement user notification system in customer dashboard
Integrate a new notification module into the customer dashboard to allow users to view and manage alerts. - Add `get_notifications` and `mark_notification_read` methods to `IFNEX_User_Bridge` to interface with the API - Introduce a new 'notifications' tab in the customer dashboard shortcode - Implement notification badge logic to display unread counts in the sidebar menu - Add new SVG icon for notifications - Add comprehensive CSS styles for notification items, badges, and unread states
This commit is contained in:
parent
1c2ff9941e
commit
fc1c2f023d
@ -663,4 +663,101 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
animation: ifnexFadeIn 0.25s ease;
|
animation: ifnexFadeIn 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ═══ Notifications ═══ */
|
||||||
|
.ifnx-notif-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
right: -10px;
|
||||||
|
background: #ef4444;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 800;
|
||||||
|
min-width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 999px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 4px;
|
||||||
|
box-shadow: 0 2px 6px rgba(239,68,68,0.4);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ifnx-notif-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ifnx-notif-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 16px 18px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 14px;
|
||||||
|
border: 1px solid #eef2f7;
|
||||||
|
box-shadow: 0 1px 3px rgba(15,23,42,0.04);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.ifnx-notif-item:hover {
|
||||||
|
box-shadow: 0 6px 20px rgba(15,23,42,0.08);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.ifnx-notif-item.unread {
|
||||||
|
background: #fffbeb;
|
||||||
|
border-color: #fde68a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ifnx-notif-dot {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 5px;
|
||||||
|
background: #e2e8f0;
|
||||||
|
}
|
||||||
|
.ifnx-notif-item.unread .ifnx-notif-dot {
|
||||||
|
background: #f59e0b;
|
||||||
|
box-shadow: 0 0 0 3px rgba(245,158,11,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ifnx-notif-body { flex: 1; min-width: 0; }
|
||||||
|
|
||||||
|
.ifnx-notif-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #0f172a;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.ifnx-notif-item.unread .ifnx-notif-title { color: #92400e; }
|
||||||
|
|
||||||
|
.ifnx-notif-message {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #64748b;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ifnx-notif-link {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #f97316;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.ifnx-notif-link:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
.ifnx-notif-time {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #94a3b8;
|
||||||
|
direction: ltr;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.ifnx-notif-item { padding: 12px 14px; }
|
||||||
}
|
}
|
||||||
@ -1126,6 +1126,7 @@ function ifnex_icon($name, $size = 20) {
|
|||||||
'truck' => '<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 01-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 00-3.213-9.193 2.056 2.056 0 00-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 00-10.026 0 1.106 1.106 0 00-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12"/>',
|
'truck' => '<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 01-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 00-3.213-9.193 2.056 2.056 0 00-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 00-10.026 0 1.106 1.106 0 00-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12"/>',
|
||||||
'check' => '<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>',
|
'check' => '<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>',
|
||||||
'clock' => '<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"/>',
|
'clock' => '<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"/>',
|
||||||
|
'bell' => '<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0"/>',
|
||||||
];
|
];
|
||||||
|
|
||||||
$path = $icons[$name] ?? $icons['dashboard'];
|
$path = $icons[$name] ?? $icons['dashboard'];
|
||||||
@ -1153,7 +1154,7 @@ function ifnex_customer_dashboard_shortcode($atts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$tab = sanitize_key($_GET['tab'] ?? 'dashboard');
|
$tab = sanitize_key($_GET['tab'] ?? 'dashboard');
|
||||||
$allowed_tabs = ['dashboard', 'orders', 'new-order', 'wallet', 'transactions', 'tracking', 'profile'];
|
$allowed_tabs = ['dashboard', 'orders', 'new-order', 'wallet', 'transactions', 'notifications', 'tracking', 'profile'];
|
||||||
if (!in_array($tab, $allowed_tabs)) $tab = 'dashboard';
|
if (!in_array($tab, $allowed_tabs)) $tab = 'dashboard';
|
||||||
|
|
||||||
$bridge = new IFNEX_User_Bridge();
|
$bridge = new IFNEX_User_Bridge();
|
||||||
@ -1181,12 +1182,14 @@ function ifnex_customer_dashboard_shortcode($atts) {
|
|||||||
'wallet' => ['icon' => 'wallet', 'label' => 'کیف پول'],
|
'wallet' => ['icon' => 'wallet', 'label' => 'کیف پول'],
|
||||||
'transactions' => ['icon' => 'chart', 'label' => 'تراکنشها'],
|
'transactions' => ['icon' => 'chart', 'label' => 'تراکنشها'],
|
||||||
'tracking' => ['icon' => 'tracking', 'label' => 'رهگیری مرسوله'],
|
'tracking' => ['icon' => 'tracking', 'label' => 'رهگیری مرسوله'],
|
||||||
|
'notifications' => ['icon' => 'bell', 'label' => 'اعلانها'],
|
||||||
'profile' => ['icon' => 'user', 'label' => 'پروفایل'],
|
'profile' => ['icon' => 'user', 'label' => 'پروفایل'],
|
||||||
];
|
];
|
||||||
|
|
||||||
$tab_titles = [
|
$tab_titles = [
|
||||||
'dashboard' => 'داشبورد', 'orders' => 'سفارشات من', 'new-order' => 'ثبت سفارش جدید',
|
'dashboard' => 'داشبورد', 'orders' => 'سفارشات من', 'new-order' => 'ثبت سفارش جدید',
|
||||||
'wallet' => 'کیف پول', 'transactions' => 'تراکنشها', 'tracking' => 'رهگیری مرسوله', 'profile' => 'پروفایل',
|
'wallet' => 'کیف پول', 'transactions' => 'تراکنشها', 'notifications' => 'اعلانها',
|
||||||
|
'tracking' => 'رهگیری مرسوله', 'profile' => 'پروفایل',
|
||||||
];
|
];
|
||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
@ -1206,6 +1209,26 @@ function ifnex_customer_dashboard_shortcode($atts) {
|
|||||||
<span class="ifnx-menu-icon"><?php echo ifnex_icon($item['icon'], 19); ?></span>
|
<span class="ifnx-menu-icon"><?php echo ifnex_icon($item['icon'], 19); ?></span>
|
||||||
<span><?php echo esc_html($item['label']); ?></span>
|
<span><?php echo esc_html($item['label']); ?></span>
|
||||||
</a>
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php
|
||||||
|
// دریافت تعداد نوتیفیکیشنهای خواندهنشده
|
||||||
|
$unread_count = 0;
|
||||||
|
$notif_result = $bridge->get_notifications($user_id, 1);
|
||||||
|
if (!is_wp_error($notif_result)) {
|
||||||
|
$unread_count = $notif_result['unread_count'] ?? 0;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<?php foreach ($menu_items as $key => $item): ?>
|
||||||
|
<a href="<?php echo esc_url(add_query_arg('tab', $key, $base_url)); ?>"
|
||||||
|
class="ifnx-menu-item <?php echo $tab === $key ? 'active' : ''; ?>">
|
||||||
|
<span class="ifnx-menu-icon" style="position:relative;">
|
||||||
|
<?php echo ifnex_icon($item['icon'], 19); ?>
|
||||||
|
<?php if ($key === 'notifications' && $unread_count > 0): ?>
|
||||||
|
<span class="ifnx-notif-badge"><?php echo $unread_count > 9 ? '9+' : $unread_count; ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</span>
|
||||||
|
<span><?php echo esc_html($item['label']); ?></span>
|
||||||
|
</a>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<div class="ifnx-menu-divider"></div>
|
<div class="ifnx-menu-divider"></div>
|
||||||
<a href="<?php echo esc_url(wp_logout_url(home_url())); ?>" class="ifnx-menu-item logout">
|
<a href="<?php echo esc_url(wp_logout_url(home_url())); ?>" class="ifnx-menu-item logout">
|
||||||
@ -1301,6 +1324,34 @@ function ifnex_customer_dashboard_shortcode($atts) {
|
|||||||
</div>
|
</div>
|
||||||
<?php elseif ($tab === 'transactions'): ?>
|
<?php elseif ($tab === 'transactions'): ?>
|
||||||
<?php echo do_shortcode('[ifnex_transactions per_page="20"]'); ?>
|
<?php echo do_shortcode('[ifnex_transactions per_page="20"]'); ?>
|
||||||
|
<?php elseif ($tab === 'notifications'): ?>
|
||||||
|
<?php
|
||||||
|
$all_notifications = $bridge->get_notifications($user_id, 30);
|
||||||
|
if (is_wp_error($all_notifications)):
|
||||||
|
?>
|
||||||
|
<div class="ifnx-card"><div class="ifnx-empty"><p>خطا در دریافت اعلانها.</p></div></div>
|
||||||
|
<?php elseif (empty($all_notifications['data'])): ?>
|
||||||
|
<div class="ifnx-card"><div class="ifnx-empty">
|
||||||
|
<?php echo ifnex_icon('bell', 40); ?>
|
||||||
|
<p>اعلان جدیدی وجود ندارد.</p>
|
||||||
|
</div></div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="ifnx-notif-list">
|
||||||
|
<?php foreach ($all_notifications['data'] as $notif): ?>
|
||||||
|
<div class="ifnx-notif-item <?php echo empty($notif['read_at']) ? 'unread' : ''; ?>" data-id="<?php echo esc_attr($notif['id']); ?>">
|
||||||
|
<div class="ifnx-notif-dot"></div>
|
||||||
|
<div class="ifnx-notif-body">
|
||||||
|
<div class="ifnx-notif-title"><?php echo esc_html($notif['title'] ?? $notif['data']['title'] ?? 'اعلان'); ?></div>
|
||||||
|
<div class="ifnx-notif-message"><?php echo esc_html($notif['body'] ?? $notif['data']['message'] ?? ''); ?></div>
|
||||||
|
<?php if (!empty($notif['data']['shipment_awb'])): ?>
|
||||||
|
<a href="<?php echo esc_url(add_query_arg(['tab' => 'orders', 'view' => $notif['data']['shipment_id']], $base_url)); ?>" class="ifnx-notif-link">مشاهده سفارش</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<div class="ifnx-notif-time"><?php echo esc_html($notif['created_at_jalali'] ?? $notif['created_at'] ?? ''); ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
<?php elseif ($tab === 'tracking'): ?>
|
<?php elseif ($tab === 'tracking'): ?>
|
||||||
<?php echo do_shortcode('[ifnex_tracking_form]'); ?>
|
<?php echo do_shortcode('[ifnex_tracking_form]'); ?>
|
||||||
<?php elseif ($tab === 'profile'): ?>
|
<?php elseif ($tab === 'profile'): ?>
|
||||||
|
|||||||
@ -322,6 +322,29 @@ class IFNEX_User_Bridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* دریافت نوتیفیکیشنهای کاربر
|
||||||
|
*/
|
||||||
|
public function get_notifications($user_id, $per_page = 20) {
|
||||||
|
$result = $this->authenticated_request($user_id, '/customer/notifications', 'GET', array(
|
||||||
|
'per_page' => $per_page,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (is_wp_error($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* علامتگذاری نوتیفیکیشن به عنوان خواندهشده
|
||||||
|
*/
|
||||||
|
public function mark_notification_read($user_id, $notification_id) {
|
||||||
|
$result = $this->authenticated_request($user_id, "/customer/notifications/{$notification_id}/read", 'POST');
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════
|
||||||
// AJAX Handler برای لغو سفارش
|
// AJAX Handler برای لغو سفارش
|
||||||
// ══════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user