Relocate PDF and commitment form download logic from shortcode execution to the `template_redirect` hook to prevent HTML output from corrupting binary streams. Key improvements: - Moved download interception to `template_redirect` to ensure clean headers and prevent partial file downloads caused by early buffer output. - Replaced direct `get_user_meta` calls with `IFNEX_User_Bridge::get_user_token` to support automatic token renewal and improved authentication reliability. - Added PDF signature validation (`%PDF` header check) to ensure integrity of remote file responses. - Updated Laravel base controller to include standard `AuthorizesRequests` and `ValidatesRequests` traits.
1831 lines
100 KiB
PHP
1831 lines
100 KiB
PHP
<?php
|
||
if (function_exists('opcache_reset')) { @opcache_reset(); }
|
||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||
header('Pragma: no-cache');
|
||
/**
|
||
* IFNEX Shortcodes
|
||
* شورتکدهای پلاگین IFNEX Bridge
|
||
*/
|
||
|
||
if ( ! defined( 'ABSPATH' ) ) {
|
||
exit;
|
||
}
|
||
|
||
// شورتکد فرم ترکینگ
|
||
add_shortcode('ifnex_tracking_form', 'ifnex_tracking_form_shortcode');
|
||
|
||
function ifnex_tracking_form_shortcode($atts) {
|
||
$atts = shortcode_atts(array(
|
||
'title' => 'رهگیری مرسوله',
|
||
'placeholder' => 'شماره بارنامه را وارد کنید',
|
||
'button_text' => 'رهگیری',
|
||
), $atts);
|
||
|
||
// خواندن پارامتر awb از URL
|
||
$awb_from_url = isset($_GET['awb']) ? sanitize_text_field($_GET['awb']) : '';
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-tracking-container">
|
||
<h2><?php echo esc_html($atts['title']); ?></h2>
|
||
<form id="ifnex-tracking-form" class="ifnex-tracking-form">
|
||
<div class="ifnex-form-group">
|
||
<input type="text" id="ifnex-awb-input" placeholder="<?php echo esc_attr($atts['placeholder']); ?>" value="<?php echo esc_attr($awb_from_url); ?>" required>
|
||
<button type="submit" id="ifnex-track-btn"><?php echo esc_html($atts['button_text']); ?></button>
|
||
</div>
|
||
</form>
|
||
<div id="ifnex-tracking-result" class="ifnex-tracking-result" style="display: none;"></div>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
// شورتکد نمایش وضعیت مستقیم
|
||
add_shortcode('ifnex_tracking_status', 'ifnex_tracking_status_shortcode');
|
||
|
||
function ifnex_tracking_status_shortcode($atts) {
|
||
$atts = shortcode_atts(array(
|
||
'awb' => '',
|
||
), $atts);
|
||
|
||
if (empty($atts['awb'])) {
|
||
return '<p>شماره بارنامه وارد نشده است.</p>';
|
||
}
|
||
|
||
$client = new IFNEX_API_Client();
|
||
$result = $client->track_shipment($atts['awb']);
|
||
|
||
if (is_wp_error($result)) {
|
||
return '<p class="ifnex-error">' . esc_html($result->get_error_message()) . '</p>';
|
||
}
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-tracking-status">
|
||
<h3>وضعیت مرسوله <?php echo esc_html($result['awb_no'] ?? ''); ?></h3>
|
||
<div class="ifnex-status-badge">
|
||
<?php echo esc_html($result['status'] ?? 'نامشخص'); ?>
|
||
</div>
|
||
<div class="ifnex-details">
|
||
<p><strong>نوع:</strong> <?php echo esc_html($result['type'] ?? ''); ?></p>
|
||
<p><strong>جهت:</strong> <?php echo esc_html($result['direction'] ?? ''); ?></p>
|
||
<p><strong>فرستنده:</strong> <?php echo esc_html($result['sender']['name'] ?? ''); ?></p>
|
||
<p><strong>گیرنده:</strong> <?php echo esc_html($result['receiver']['name'] ?? ''); ?></p>
|
||
</div>
|
||
<?php if (!empty($result['timeline']) && is_array($result['timeline'])): ?>
|
||
<div class="ifnex-timeline">
|
||
<h4>تاریخچه رهگیری</h4>
|
||
<ul>
|
||
<?php foreach ($result['timeline'] as $event): ?>
|
||
<li>
|
||
<strong><?php echo esc_html($event['event_date'] ?? ''); ?></strong>
|
||
<span><?php echo esc_html($event['event_description'] ?? ''); ?></span>
|
||
<?php if (!empty($event['location'])): ?>
|
||
<small><?php echo esc_html($event['location']); ?></small>
|
||
<?php endif; ?>
|
||
</li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
// شورتکد موجودی کیف پول
|
||
add_shortcode('ifnex_wallet_balance', 'ifnex_wallet_balance_shortcode');
|
||
|
||
function ifnex_wallet_balance_shortcode($atts) {
|
||
if (!is_user_logged_in()) {
|
||
return '<p class="ifnex-error">برای مشاهده موجودی باید وارد شوید.</p>';
|
||
}
|
||
|
||
$atts = shortcode_atts(array(
|
||
'show_details' => 'no',
|
||
), $atts);
|
||
|
||
$bridge = new IFNEX_User_Bridge();
|
||
$balance = $bridge->get_wallet_balance(get_current_user_id());
|
||
|
||
if (is_wp_error($balance)) {
|
||
return '<p class="ifnex-error">' . esc_html($balance->get_error_message()) . '</p>';
|
||
}
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-wallet-balance">
|
||
<div class="ifnex-balance-main">
|
||
<span class="ifnex-balance-label">موجودی کیف پول:</span>
|
||
<span class="ifnex-balance-amount"><?php echo number_format($balance['balance']); ?> ریال</span>
|
||
</div>
|
||
<?php if ($atts['show_details'] === 'yes'): ?>
|
||
<div class="ifnex-balance-details">
|
||
<p><strong>مجموع واریزی:</strong> <?php echo number_format($balance['total_deposited']); ?> ریال</p>
|
||
<p><strong>مجموع برداشت:</strong> <?php echo number_format($balance['total_withdrawn']); ?> ریال</p>
|
||
<?php if ($balance['is_frozen']): ?>
|
||
<p class="ifnex-wallet-frozen">⚠️ کیف پول شما مسدود است.</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
// شورتکد لیست تراکنشها
|
||
add_shortcode('ifnex_transactions', 'ifnex_transactions_shortcode');
|
||
|
||
function ifnex_transactions_shortcode($atts) {
|
||
if (!is_user_logged_in()) {
|
||
return '<p class="ifnex-error">برای مشاهده تراکنشها باید وارد شوید.</p>';
|
||
}
|
||
|
||
$atts = shortcode_atts(array(
|
||
'per_page' => 10,
|
||
'show_pagination' => 'no',
|
||
), $atts);
|
||
|
||
$bridge = new IFNEX_User_Bridge();
|
||
$transactions = $bridge->get_transactions(get_current_user_id(), intval($atts['per_page']));
|
||
|
||
if (is_wp_error($transactions)) {
|
||
return '<p class="ifnex-error">' . esc_html($transactions->get_error_message()) . '</p>';
|
||
}
|
||
|
||
if (empty($transactions['transactions'])) {
|
||
return '<p>هنوز تراکنشی ثبت نشده است.</p>';
|
||
}
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-transactions">
|
||
<h3>تاریخچه تراکنشها</h3>
|
||
<div class="ifnex-transactions-list">
|
||
<?php foreach ($transactions['transactions'] as $transaction): ?>
|
||
<div class="ifnex-transaction-item">
|
||
<div class="ifnex-transaction-header">
|
||
<span class="ifnex-transaction-type <?php echo esc_attr($transaction['type']); ?>">
|
||
<?php echo esc_html($transaction['type']); ?>
|
||
</span>
|
||
<span class="ifnex-transaction-amount <?php echo $transaction['amount'] >= 0 ? 'positive' : 'negative'; ?>">
|
||
<?php echo $transaction['amount'] >= 0 ? '+' : ''; ?>
|
||
<?php echo number_format($transaction['amount']); ?> ریال
|
||
</span>
|
||
</div>
|
||
<div class="ifnex-transaction-details">
|
||
<p class="ifnex-transaction-description"><?php echo esc_html($transaction['description']); ?></p>
|
||
<div class="ifnex-transaction-meta">
|
||
<span class="ifnex-transaction-status"><?php echo esc_html($transaction['status']); ?></span>
|
||
<span class="ifnex-transaction-date"><?php echo esc_html($transaction['created_at_jalali']); ?></span>
|
||
</div>
|
||
<?php if (!empty($transaction['reference_id'])): ?>
|
||
<p class="ifnex-transaction-ref">کد پیگیری: <?php echo esc_html($transaction['reference_id']); ?></p>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php if ($atts['show_pagination'] === 'yes' && $transactions['last_page'] > 1): ?>
|
||
<div class="ifnex-pagination">
|
||
<!-- Pagination can be added here -->
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// شورتکد پروفایل مشتری (جدید)
|
||
// ══════════════════════════════════════════════════════════════
|
||
|
||
add_shortcode('ifnex_user_profile', 'ifnex_user_profile_shortcode');
|
||
|
||
function ifnex_user_profile_shortcode($atts) {
|
||
if (!is_user_logged_in()) {
|
||
return '<p class="ifnex-error">برای مشاهده پروفایل، باید وارد شوید.</p>';
|
||
}
|
||
|
||
$atts = shortcode_atts(array(
|
||
'show_stats' => 'yes',
|
||
'show_wallet' => 'yes',
|
||
'show_recent_orders' => 'yes',
|
||
), $atts);
|
||
|
||
$bridge = new IFNEX_User_Bridge();
|
||
$user_id = get_current_user_id();
|
||
|
||
$profile = $bridge->get_customer_profile($user_id);
|
||
|
||
if (is_wp_error($profile)) {
|
||
return '<p class="ifnex-error">' . esc_html($profile->get_error_message()) . '</p>';
|
||
}
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-user-profile">
|
||
<!-- اطلاعات کاربر -->
|
||
<div class="ifnex-profile-header">
|
||
<h2>👤 <?php echo esc_html($profile['user']['name']); ?></h2>
|
||
<div class="ifnex-profile-info">
|
||
<p><strong>ایمیل:</strong> <?php echo esc_html($profile['user']['email']); ?></p>
|
||
<p><strong>تلفن:</strong> <?php echo esc_html($profile['user']['phone'] ?? '—'); ?></p>
|
||
<p><strong>عضویت از:</strong> <?php echo esc_html($profile['user']['member_since']); ?></p>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if ($atts['show_wallet'] === 'yes' && !empty($profile['wallet'])): ?>
|
||
<!-- موجودی کیف پول -->
|
||
<div class="ifnex-wallet-card" style="background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); border: 1px solid #bae6fd; border-radius: 12px; padding: 20px; margin-bottom: 20px;">
|
||
<h3 style="margin: 0 0 15px 0; color: #0369a1;">💰 وضعیت مالی</h3>
|
||
|
||
<!-- مانده حساب -->
|
||
<div style="background: white; border-radius: 8px; padding: 15px; margin-bottom: 15px; border: 1px solid #e0f2fe;">
|
||
<div style="font-size: 12px; color: #666; margin-bottom: 5px;">مانده حساب (موجودی منهای بدهیها)</div>
|
||
<div style="font-size: 24px; font-weight: bold; color: <?php echo ($profile['account_balance'] ?? 0) >= 0 ? '#059669' : '#dc2626'; ?>;">
|
||
<?php echo number_format($profile['account_balance'] ?? 0); ?> ریال
|
||
</div>
|
||
<?php if (($profile['pending_payments_total'] ?? 0) > 0): ?>
|
||
<div style="font-size: 11px; color: #dc2626; margin-top: 5px;">
|
||
⚠️ بدهی در انتظار پرداخت: <?php echo number_format($profile['pending_payments_total']); ?> ریال
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<!-- جزئیات -->
|
||
<div style="display: flex; gap: 15px;">
|
||
<div style="flex: 1; background: white; border-radius: 8px; padding: 12px; border: 1px solid #e0f2fe;">
|
||
<div style="font-size: 11px; color: #666;">موجودی کیف پول</div>
|
||
<div style="font-size: 16px; font-weight: bold; color: #0369a1;">
|
||
<?php echo number_format($profile['wallet']['balance']); ?> ریال
|
||
</div>
|
||
</div>
|
||
<div style="flex: 1; background: white; border-radius: 8px; padding: 12px; border: 1px solid #e0f2fe;">
|
||
<div style="font-size: 11px; color: #666;">وضعیت</div>
|
||
<div style="font-size: 14px; font-weight: bold;">
|
||
<?php if ($profile['wallet']['is_frozen']): ?>
|
||
<span style="color: #dc2626;">🔴 مسدود</span>
|
||
<?php else: ?>
|
||
<span style="color: #059669;">🟢 فعال</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($atts['show_stats'] === 'yes' && !empty($profile['stats'])): ?>
|
||
<!-- آمار سفارشات -->
|
||
<div class="ifnex-stats-grid">
|
||
<h3>📊 آمار سفارشات</h3>
|
||
<div class="ifnex-stats-cards">
|
||
<div class="ifnex-stat-card">
|
||
<div class="ifnex-stat-value"><?php echo number_format($profile['stats']['total_orders']); ?></div>
|
||
<div class="ifnex-stat-label">کل سفارشات</div>
|
||
</div>
|
||
<div class="ifnex-stat-card ifnex-stat-warning">
|
||
<div class="ifnex-stat-value"><?php echo number_format($profile['stats']['pending_approval'] ?? 0); ?></div>
|
||
<div class="ifnex-stat-label">در انتظار تأیید</div>
|
||
</div>
|
||
<div class="ifnex-stat-card ifnex-stat-info">
|
||
<div class="ifnex-stat-value"><?php echo number_format($profile['stats']['pending_payment'] ?? 0); ?></div>
|
||
<div class="ifnex-stat-label">در انتظار پرداخت</div>
|
||
</div>
|
||
<div class="ifnex-stat-card ifnex-stat-info">
|
||
<div class="ifnex-stat-value"><?php echo number_format($profile['stats']['active_orders']); ?></div>
|
||
<div class="ifnex-stat-label">در حال پردازش</div>
|
||
</div>
|
||
<div class="ifnex-stat-card ifnex-stat-success">
|
||
<div class="ifnex-stat-value"><?php echo number_format($profile['stats']['delivered_orders']); ?></div>
|
||
<div class="ifnex-stat-label">تحویل شده</div>
|
||
</div>
|
||
</div>
|
||
<?php if ($profile['stats']['total_spent'] > 0): ?>
|
||
<p class="ifnex-total-spent">
|
||
<strong>مجموع خرید:</strong>
|
||
<?php echo number_format($profile['stats']['total_spent']); ?> ریال
|
||
</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- لینکهای سریع -->
|
||
<div class="ifnex-quick-links">
|
||
<a href="<?php echo esc_url(home_url('/my-orders/')); ?>" class="ifnex-btn ifnex-btn-primary">
|
||
📦 مشاهده سفارشات
|
||
</a>
|
||
<a href="<?php echo esc_url(home_url('/new-order/')); ?>" class="ifnex-btn ifnex-btn-success">
|
||
➕ ثبت سفارش جدید
|
||
</a>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// شورتکد لیست سفارشات مشتری
|
||
// ══════════════════════════════════════════════════════════════
|
||
|
||
add_shortcode('ifnex_orders_list', 'ifnex_orders_list_shortcode');
|
||
|
||
function ifnex_orders_list_shortcode($atts) {
|
||
if (!is_user_logged_in()) {
|
||
return '<p class="ifnex-error">برای مشاهده سفارشات، باید وارد شوید.</p>';
|
||
}
|
||
|
||
$atts = shortcode_atts(array(
|
||
'per_page' => 10,
|
||
'status' => '',
|
||
), $atts);
|
||
|
||
$bridge = new IFNEX_User_Bridge();
|
||
$user_id = get_current_user_id();
|
||
|
||
$result = $bridge->get_customer_orders($user_id, intval($atts['per_page']), $atts['status']);
|
||
|
||
if (is_wp_error($result)) {
|
||
return '<p class="ifnex-error">' . esc_html($result->get_error_message()) . '</p>';
|
||
}
|
||
|
||
$orders = $result['data'] ?? [];
|
||
$pagination = $result['pagination'] ?? [];
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-orders-list">
|
||
<div class="ifnex-orders-header">
|
||
<h2>📦 سفارشات من</h2>
|
||
<a href="<?php echo esc_url(home_url('/new-order/')); ?>" class="ifnex-btn ifnex-btn-success">
|
||
➕ ثبت سفارش جدید
|
||
</a>
|
||
</div>
|
||
|
||
<!-- فیلتر وضعیت -->
|
||
<div class="ifnex-filter-bar">
|
||
<a href="?status=" class="<?php echo empty($atts['status']) ? 'active' : ''; ?>">همه</a>
|
||
<a href="?status=pending_payment" class="<?php echo $atts['status'] === 'pending_payment' ? 'active' : ''; ?>">در انتظار پرداخت</a>
|
||
<a href="?status=processed" class="<?php echo $atts['status'] === 'processed' ? 'active' : ''; ?>">پردازش شده</a>
|
||
<a href="?status=in_transit" class="<?php echo $atts['status'] === 'in_transit' ? 'active' : ''; ?>">در حال حمل</a>
|
||
<a href="?status=delivered" class="<?php echo $atts['status'] === 'delivered' ? 'active' : ''; ?>">تحویل شده</a>
|
||
</div>
|
||
|
||
<?php if (empty($orders)): ?>
|
||
<div class="ifnex-empty-state">
|
||
<p>📭 هنوز سفارشی ثبت نکردهاید.</p>
|
||
<a href="<?php echo esc_url(home_url('/new-order/')); ?>" class="ifnex-btn ifnex-btn-primary">
|
||
اولین سفارش خود را ثبت کنید
|
||
</a>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="ifnex-orders-grid">
|
||
<?php foreach ($orders as $order): ?>
|
||
<div class="ifnex-order-card">
|
||
<div class="ifnex-order-header">
|
||
<div class="ifnex-awb">
|
||
<strong>AWB:</strong>
|
||
<code><?php echo esc_html($order['awb_no']); ?></code>
|
||
</div>
|
||
<span class="ifnex-badge ifnex-badge-<?php echo esc_attr($order['status']['color']); ?>">
|
||
<?php echo esc_html($order['status']['label']); ?>
|
||
</span>
|
||
</div>
|
||
|
||
<div class="ifnex-order-route">
|
||
<div class="ifnex-route-from">
|
||
<span class="ifnex-label">از:</span>
|
||
<span class="ifnex-country">
|
||
<?php echo esc_html($order['from_country']['name'] ?? '—'); ?>
|
||
</span>
|
||
</div>
|
||
<div class="ifnex-route-arrow">➜</div>
|
||
<div class="ifnex-route-to">
|
||
<span class="ifnex-label">به:</span>
|
||
<span class="ifnex-country">
|
||
<?php echo esc_html($order['to_country']['name'] ?? '—'); ?>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ifnex-order-details">
|
||
<div class="ifnex-detail-item">
|
||
<span class="ifnex-label">وزن:</span>
|
||
<span><?php echo esc_html($order['weight']); ?> kg</span>
|
||
</div>
|
||
<div class="ifnex-detail-item">
|
||
<span class="ifnex-label">مبلغ:</span>
|
||
<span class="ifnex-price">
|
||
<?php echo number_format($order['total_fee']); ?> ریال
|
||
</span>
|
||
</div>
|
||
<div class="ifnex-detail-item">
|
||
<span class="ifnex-label">تاریخ:</span>
|
||
<span><?php echo esc_html($order['created_at_jalali']); ?></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ifnex-order-actions">
|
||
<a href="<?php echo esc_url(home_url('/order-detail/?order_id=' . $order['id'])); ?>"
|
||
class="ifnex-btn ifnex-btn-sm ifnex-btn-info">
|
||
👁️ جزئیات
|
||
</a>
|
||
|
||
<?php if ($order['status']['can_cancel']): ?>
|
||
<button class="ifnex-btn ifnex-btn-sm ifnex-btn-danger ifnex-cancel-order"
|
||
data-order-id="<?php echo esc_attr($order['id']); ?>">
|
||
❌ لغو سفارش
|
||
</button>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($order['status']['can_pay']): ?>
|
||
<a href="<?php echo esc_url(home_url('/order-payment/?order_id=' . $order['id'])); ?>"
|
||
class="ifnex-btn ifnex-btn-sm ifnex-btn-success">
|
||
💳 پرداخت
|
||
</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
|
||
<?php if (!empty($pagination) && $pagination['last_page'] > 1): ?>
|
||
<div class="ifnex-pagination">
|
||
<?php for ($i = 1; $i <= $pagination['last_page']; $i++): ?>
|
||
<a href="?page=<?php echo $i; ?>"
|
||
class="<?php echo $i === $pagination['current_page'] ? 'active' : ''; ?>">
|
||
<?php echo $i; ?>
|
||
</a>
|
||
<?php endfor; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<script>
|
||
jQuery(document).ready(function($) {
|
||
$('.ifnex-cancel-order').on('click', function() {
|
||
if (!confirm('آیا از لغو این سفارش اطمینان دارید؟')) {
|
||
return;
|
||
}
|
||
|
||
const orderId = $(this).data('order-id');
|
||
const btn = $(this);
|
||
|
||
btn.prop('disabled', true).text('در حال لغو...');
|
||
|
||
$.ajax({
|
||
url: ifnex_ajax.ajax_url,
|
||
method: 'POST',
|
||
data: {
|
||
action: 'ifnex_cancel_order',
|
||
order_id: orderId,
|
||
nonce: ifnex_ajax.nonce
|
||
},
|
||
success: function(response) {
|
||
if (response.success) {
|
||
alert('سفارش با موفقیت لغو شد.');
|
||
location.reload();
|
||
} else {
|
||
alert('خطا: ' + response.data);
|
||
btn.prop('disabled', false).text('❌ لغو سفارش');
|
||
}
|
||
},
|
||
error: function() {
|
||
alert('خطا در ارتباط با سرور.');
|
||
btn.prop('disabled', false).text('❌ لغو سفارش');
|
||
}
|
||
});
|
||
});
|
||
|
||
// 🔹 بارگذاری تعهدنامههای سفارش از طریق AJAX
|
||
var $cf = $('#ifnex-commitment-forms');
|
||
var cfOrderId = $cf.data('order-id');
|
||
if (cfOrderId) {
|
||
$.post(ifnex_ajax.ajax_url, {
|
||
action: 'ifnex_get_commitment_forms',
|
||
order_id: cfOrderId,
|
||
nonce: ifnex_ajax.nonce
|
||
}).done(function(res) {
|
||
if (!res || !res.success) {
|
||
$cf.html('<p style="color:#dc2626">خطا: ' + ((res && res.data) || 'دریافت نشد') + '</p>');
|
||
return;
|
||
}
|
||
var forms = res.data || [];
|
||
if (!forms.length) {
|
||
$cf.html('<p style="color:#94a3b8;font-size:13px">تعهدنامهای برای این سفارش ثبت نشده است.</p>');
|
||
return;
|
||
}
|
||
var html = '';
|
||
for (var i = 0; i < forms.length; i++) {
|
||
var f = forms[i];
|
||
var status = f.upload_status || 'pending';
|
||
var isUploaded = (status === 'uploaded' || status === 'approved');
|
||
var statusLabel = isUploaded ? '✅ آپلود شده' : '⏳ در انتظار آپلود';
|
||
var statusClass = isUploaded ? 'ifnex-badge-success' : 'ifnex-badge-warning';
|
||
var dlHref = '?order_id=' + cfOrderId + '&download=commitment-template&form_id=' + f.id;
|
||
var signedHref = '?order_id=' + cfOrderId + '&download=commitment-signed&form_id=' + f.id;
|
||
html += '<div class="ifnex-form-actions" style="border:1px solid #e2e8f0;border-radius:8px;padding:12px;margin-bottom:10px;background:#fff">';
|
||
html += '<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px">';
|
||
html += '<div>';
|
||
html += '<strong>' + (f.title || '') + '</strong>';
|
||
html += ' <span class="ifnex-badge ' + statusClass + '">' + statusLabel + '</span>';
|
||
if (f.file_type) html += ' <span style="color:#94a3b8;font-size:11px">(' + f.file_type + ')</span>';
|
||
if (f.description) html += '<div style="color:#64748b;font-size:12px;margin-top:2px">' + f.description + '</div>';
|
||
html += '</div>';
|
||
html += '<div style="display:flex;gap:8px;flex-wrap:wrap">';
|
||
html += '<a href="' + dlHref + '" class="ifnex-btn ifnex-btn-sm ifnex-btn-info" target="_blank">📥 دانلود فرم</a>';
|
||
if (isUploaded) {
|
||
html += '<a href="' + signedHref + '" class="ifnex-btn ifnex-btn-sm ifnex-btn-success" target="_blank">📄 دانلود امضاشده</a>';
|
||
}
|
||
html += '<label class="ifnex-btn ifnex-btn-sm ifnex-btn-primary" style="cursor:pointer;margin:0">';
|
||
html += '📤 آپلود امضاشده';
|
||
html += '<input type="file" accept=".pdf,.jpg,.jpeg,.png" style="display:none" onchange="uploadCommitmentForm(' + cfOrderId + ', ' + f.id + ', this)">';
|
||
html += '</label>';
|
||
html += '</div>';
|
||
html += '</div>';
|
||
html += '</div>';
|
||
}
|
||
$cf.html(html);
|
||
}).fail(function() {
|
||
$cf.html('<p style="color:#dc2626">خطا در ارتباط با سرور.</p>');
|
||
});
|
||
}
|
||
});
|
||
|
||
// تعریف روی window چون از onclick صدا زده میشود
|
||
window.uploadCommitmentForm = function(orderId, formId, input) {
|
||
var file = input.files[0];
|
||
if (!file) return;
|
||
|
||
var formData = new FormData();
|
||
formData.append('action', 'ifnex_upload_commitment_form');
|
||
formData.append('order_id', orderId);
|
||
formData.append('form_id', formId);
|
||
formData.append('file', file);
|
||
formData.append('nonce', ifnex_ajax.nonce);
|
||
|
||
// جستجوی نزدیکترین container با .ifnex-form-actions (والد دکمه)
|
||
var container = input.closest('.ifnex-form-actions') || input.parentElement;
|
||
var originalHtml = container.innerHTML;
|
||
container.innerHTML = '<span class="ifnex-loading">در حال آپلود...</span>';
|
||
|
||
jQuery.ajax({
|
||
url: ifnex_ajax.ajax_url,
|
||
method: 'POST',
|
||
data: formData,
|
||
processData: false,
|
||
contentType: false,
|
||
success: function(res) {
|
||
if (res.success) {
|
||
alert('فایل با موفقیت آپلود شد.');
|
||
location.reload();
|
||
} else {
|
||
alert('خطا: ' + (res.data || 'نامشخص'));
|
||
container.innerHTML = originalHtml;
|
||
}
|
||
},
|
||
error: function(xhr) {
|
||
var msg = 'خطا در ارتباط با سرور.';
|
||
if (xhr && xhr.responseJSON && xhr.responseJSON.data) {
|
||
msg = 'خطا: ' + xhr.responseJSON.data;
|
||
}
|
||
alert(msg);
|
||
container.innerHTML = originalHtml;
|
||
}
|
||
});
|
||
};
|
||
</script>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// شورتکد فرم ثبت سفارش چند مرحلهای
|
||
// ══════════════════════════════════════════════════════════════
|
||
|
||
add_shortcode('ifnex_order_form', 'ifnex_order_form_shortcode');
|
||
|
||
function ifnex_order_form_shortcode($atts) {
|
||
if (!is_user_logged_in()) {
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-login-prompt">
|
||
<h3>🔐 ورود لازم است</h3>
|
||
<p>برای ثبت سفارش، ابتدا باید وارد حساب کاربری خود شوید.</p>
|
||
<a href="<?php echo esc_url(wp_login_url(get_permalink())); ?>" class="ifnex-btn ifnex-btn-primary">ورود به حساب</a>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-order-form-container" id="ifnex-order-form">
|
||
|
||
<!-- Progress Steps -->
|
||
<div class="ifnex-steps">
|
||
<div class="ifnex-step active" data-step="1"><span>۱</span><br>مسیر و بسته</div>
|
||
<div class="ifnex-step" data-step="2"><span>۲</span><br>اطلاعات تماس</div>
|
||
<div class="ifnex-step ifnex-step-invoice" data-step="3" style="display:none;"><span>۳</span><br>اقلام گمرکی</div>
|
||
<div class="ifnex-step ifnex-step-discount" data-step="4"><span>۴</span><br>بررسی قیمت</div>
|
||
<div class="ifnex-step" data-step="5"><span>۵</span><br>تأیید نهایی</div>
|
||
</div>
|
||
|
||
<div id="ifnex-form-error" class="ifnex-error-box" style="display:none;"></div>
|
||
|
||
<!-- Step 1: Route & Package -->
|
||
<div class="ifnex-form-step active" data-step="1">
|
||
<div class="ifnex-form-card">
|
||
<h3>📦 مشخصات مرسوله</h3>
|
||
<div class="ifnex-form-grid">
|
||
<div class="ifnex-field">
|
||
<label for="ifnex-direction">جهت ارسال *</label>
|
||
<select id="ifnex-direction" name="ifnex_direction">
|
||
<option value="export">صادرات (از ایران به خارج)</option>
|
||
<option value="import">واردات (از خارج به ایران)</option>
|
||
</select>
|
||
</div>
|
||
<div class="ifnex-field">
|
||
<label for="ifnex-type">نوع سرویس *</label>
|
||
<select id="ifnex-type" name="ifnex_type">
|
||
<option value="PARCEL">بسته (Parcel)</option>
|
||
<option value="DOC_NORMAL">مدارک عادی</option>
|
||
<option value="DOC_ECONOMY">مدارک اقتصادی</option>
|
||
</select>
|
||
</div>
|
||
<div class="ifnex-field">
|
||
<label for="ifnex-from-country">کشور مبدأ *</label>
|
||
<select id="ifnex-from-country" name="ifnex_from_country"></select>
|
||
</div>
|
||
<div class="ifnex-field">
|
||
<label for="ifnex-to-country">کشور مقصد *</label>
|
||
<select id="ifnex-to-country" name="ifnex_to_country"></select>
|
||
</div>
|
||
</div>
|
||
<!-- ─── بخش بستهها (Multi-Package) ─── -->
|
||
<h3 style="margin-top: 24px;">📦 بستههای مرسوله</h3>
|
||
<p style="font-size: 12px; color: #666; margin-bottom: 12px;">
|
||
اگر چند بسته دارید، برای هرکدوم اطلاعات جداگانه وارد کنید. وزن واقعی و ابعاد هر دو اجباری هستند (وزن حجمی خودکار محاسبه میشود).
|
||
</p>
|
||
|
||
<div id="ifnex-packages-container">
|
||
<!-- بستهها اینجا اضافه میشن -->
|
||
</div>
|
||
|
||
<button type="button" id="ifnex-add-package" class="ifnex-btn ifnex-btn-info" style="margin-top: 10px;">
|
||
➕ افزودن بسته دیگر
|
||
</button>
|
||
|
||
<!-- خلاصه وزنها -->
|
||
<div class="ifnex-packages-summary" id="ifnex-packages-summary" style="display:none; margin-top: 16px; padding: 12px; background: #f0f9ff; border: 1px solid #bae6fd; border-radius: 6px;">
|
||
<div style="display: flex; justify-content: space-between; gap: 20px;">
|
||
<div>
|
||
<strong>تعداد بستهها:</strong> <span id="ifnex-package-count">0</span>
|
||
</div>
|
||
<div>
|
||
<strong>مجموع وزن واقعی:</strong> <span id="ifnex-total-weight">0</span> kg
|
||
</div>
|
||
<div>
|
||
<strong>مجموع وزن حجمی:</strong> <span id="ifnex-total-volumetric">0</span> kg
|
||
</div>
|
||
<div>
|
||
<strong>وزن قابل محاسبه:</strong> <span id="ifnex-chargeable-weight">0</span> kg
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="ifnex-form-nav">
|
||
<span></span>
|
||
<button class="ifnex-btn ifnex-btn-primary ifnex-next-step">ادامه ←</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 2: Sender & Receiver -->
|
||
<div class="ifnex-form-step" data-step="2">
|
||
<div class="ifnex-form-card">
|
||
<h3>👤 اطلاعات فرستنده</h3>
|
||
<div class="ifnex-form-grid">
|
||
<div class="ifnex-field"><label for="ifnex-sender-name">نام و نام خانوادگی *</label><input type="text" id="ifnex-sender-name" name="ifnex_sender_name"></div>
|
||
<div class="ifnex-field"><label for="ifnex-sender-phone">شماره تماس *</label><input type="tel" id="ifnex-sender-phone" name="ifnex_sender_phone"></div>
|
||
<div class="ifnex-field"><label for="ifnex-sender-city">شهر</label><input type="text" id="ifnex-sender-city" name="ifnex_sender_city"></div>
|
||
<div class="ifnex-field full"><label for="ifnex-sender-address">آدرس کامل *</label><textarea id="ifnex-sender-address" name="ifnex_sender_address" rows="2"></textarea></div>
|
||
</div>
|
||
<h3 style="margin-top:24px;">📮 اطلاعات گیرنده</h3>
|
||
<div class="ifnex-form-grid">
|
||
<div class="ifnex-field"><label for="ifnex-receiver-name">نام و نام خانوادگی *</label><input type="text" id="ifnex-receiver-name" name="ifnex_receiver_name"></div>
|
||
<div class="ifnex-field"><label for="ifnex-receiver-phone">شماره تماس *</label><input type="tel" id="ifnex-receiver-phone" name="ifnex_receiver_phone"></div>
|
||
<div class="ifnex-field"><label for="ifnex-receiver-city">شهر</label><input type="text" id="ifnex-receiver-city" name="ifnex_receiver_city"></div>
|
||
<div class="ifnex-field full"><label for="ifnex-receiver-address">آدرس کامل *</label><textarea id="ifnex-receiver-address" name="ifnex_receiver_address" rows="2"></textarea></div>
|
||
</div>
|
||
<div class="ifnex-form-nav">
|
||
<button class="ifnex-btn ifnex-prev-step">→ قبلی</button>
|
||
<button class="ifnex-btn ifnex-btn-primary ifnex-next-step">ادامه ←</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 3: Invoice Items (فقط برای PARCEL) -->
|
||
<div class="ifnex-form-step ifnex-step-invoice-content" data-step="3" style="display:none;">
|
||
<div class="ifnex-form-card">
|
||
<h3>📋 اقلام گمرکی (Invoice)</h3>
|
||
<p style="font-size: 12px; color: #666; margin-bottom: 12px;">
|
||
اقلام موجود در بستههای خود را وارد کنید. این اطلاعات برای فاکتور صادراتی الزامی است. حداکثر ۹ ردیف.
|
||
</p>
|
||
|
||
<div id="ifnex-items-container">
|
||
<!-- آیتمها اینجا اضافه میشن -->
|
||
</div>
|
||
|
||
<button type="button" id="ifnex-add-item" class="ifnex-btn ifnex-btn-info" style="margin-top: 10px;">
|
||
➕ افزودن قلم دیگر
|
||
</button>
|
||
|
||
<!-- خلاصه فاکتور -->
|
||
<div class="ifnex-items-summary" id="ifnex-items-summary" style="display:none; margin-top: 16px; padding: 12px; background: #fef3c7; border: 1px solid #fde68a; border-radius: 6px;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||
<div>
|
||
<strong>📦 تعداد اقلام:</strong> <span id="ifnex-item-count">0</span>
|
||
</div>
|
||
<div>
|
||
<strong>💰 مجموع فاکتور (USD):</strong> <span id="ifnex-invoice-total" style="color: #059669; font-size: 16px;">0.00</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ifnex-form-nav">
|
||
<button class="ifnex-btn ifnex-prev-step">→ قبلی</button>
|
||
<button class="ifnex-btn ifnex-btn-primary ifnex-next-step">ادامه ←</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 4: Review & Discount -->
|
||
<div class="ifnex-form-step" data-step="4">
|
||
<div class="ifnex-form-card">
|
||
<h3>🏷️ کد تخفیف (اختیاری)</h3>
|
||
<div class="ifnex-form-grid">
|
||
<div class="ifnex-field full"><label for="ifnex-discount-code">کد تخفیف</label><input type="text" id="ifnex-discount-code" name="ifnex_discount_code" placeholder="مثلاً WELCOME10"></div>
|
||
</div>
|
||
<div id="ifnex-calc-error" class="ifnex-error-box" style="display:none;"></div>
|
||
<div id="ifnex-calc-loading" style="display:none; text-align:center; padding:20px;">
|
||
⏳ در حال محاسبه قیمت...
|
||
</div>
|
||
<div class="ifnex-form-nav">
|
||
<button class="ifnex-btn ifnex-prev-step">→ قبلی</button>
|
||
<button class="ifnex-btn ifnex-btn-success ifnex-next-step">💰 محاسبه قیمت</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 5: Confirm -->
|
||
<div class="ifnex-form-step" data-step="5">
|
||
<div class="ifnex-form-card">
|
||
<h3>✅ تأیید نهایی سفارش</h3>
|
||
<div class="ifnex-price-summary" id="ifnex-price-summary"></div>
|
||
<div id="ifnex-submit-error" class="ifnex-error-box" style="display:none; margin-top:16px;"></div>
|
||
<div class="ifnex-form-nav">
|
||
<button class="ifnex-btn ifnex-prev-step">→ قبلی</button>
|
||
<button class="ifnex-btn ifnex-btn-success" id="ifnex-submit-order">✅ تأیید و ثبت سفارش</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// شورتکد صفحه پرداخت سفارش
|
||
// ══════════════════════════════════════════════════════════════
|
||
|
||
add_shortcode('ifnex_order_payment', 'ifnex_order_payment_shortcode');
|
||
|
||
function ifnex_order_payment_shortcode($atts) {
|
||
if (!is_user_logged_in()) {
|
||
return '<p class="ifnex-error">برای پرداخت، باید وارد شوید.</p>';
|
||
}
|
||
|
||
$atts = shortcode_atts(array('order_id' => ''), $atts);
|
||
|
||
// دریافت order_id از URL parameter
|
||
$order_id = $atts['order_id'] ?: ($_GET['order_id'] ?? 0);
|
||
|
||
if (!$order_id) {
|
||
return '<p class="ifnex-error">شناسه سفارش مشخص نشده است.</p>';
|
||
}
|
||
|
||
$bridge = new IFNEX_User_Bridge();
|
||
$user_id = get_current_user_id();
|
||
|
||
$order = $bridge->get_customer_order($user_id, $order_id);
|
||
|
||
if (is_wp_error($order)) {
|
||
return '<p class="ifnex-error">' . esc_html($order->get_error_message()) . '</p>';
|
||
}
|
||
|
||
$data = $order['data'];
|
||
|
||
// بررسی وضعیت (فقط سفارشات تأیید شده قابل پرداخت هستند)
|
||
if ($data['status']['value'] !== 'approved') {
|
||
$message = 'این سفارش هنوز تأیید نشده است و قابل پرداخت نیست.';
|
||
if ($data['status']['value'] === 'pending_approval') {
|
||
$message = 'این سفارش در انتظار تأیید کارمند است. پس از تأیید، گزینه پرداخت فعال خواهد شد.';
|
||
} elseif ($data['status']['is_paid']) {
|
||
$message = 'این سفارش قبلاً پرداخت شده است.';
|
||
}
|
||
|
||
return '<div class="ifnex-warning-box">
|
||
<h3>⏳ ' . $message . '</h3>
|
||
<p>وضعیت فعلی: ' . esc_html($data['status']['label']) . '</p>
|
||
<a href="' . esc_url(home_url('/my-orders/')) . '" class="ifnex-btn ifnex-btn-primary">بازگشت به سفارشات</a>
|
||
</div>';
|
||
}
|
||
|
||
$balance = $bridge->get_wallet_balance($user_id);
|
||
$wallet_balance = is_wp_error($balance) ? 0 : ($balance['balance'] ?? 0);
|
||
$can_pay_wallet = $wallet_balance >= $data['total_fee'];
|
||
|
||
// بررسی نتیجه پرداخت از درگاه (callback)
|
||
$payment_status = sanitize_text_field($_GET['status'] ?? '');
|
||
$payment_message = sanitize_text_field($_GET['message'] ?? '');
|
||
$payment_ref_id = sanitize_text_field($_GET['ref_id'] ?? '');
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-payment-page">
|
||
<div class="ifnex-payment-card">
|
||
<h2>💳 پرداخت سفارش</h2>
|
||
|
||
<?php if ($payment_status === 'success'): ?>
|
||
<div class="ifnex-success-box">
|
||
<h3>✅ پرداخت با موفقیت انجام شد!</h3>
|
||
<p>کد پیگیری تراکنش: <strong><?php echo esc_html($payment_ref_id); ?></strong></p>
|
||
<p>مبلغ: <strong><?php echo number_format($data['total_fee']); ?> ریال</strong></p>
|
||
<p>سفارش شما در حال پردازش است.</p>
|
||
<a href="<?php echo esc_url(home_url('/my-orders/')); ?>" class="ifnex-btn ifnex-btn-primary">مشاهده سفارشات</a>
|
||
</div>
|
||
<?php elseif ($payment_status === 'failed'): ?>
|
||
<div class="ifnex-error-box">
|
||
<h3>❌ پرداخت ناموفق بود</h3>
|
||
<p><?php echo esc_html($payment_message ?: 'پرداخت با خطا مواجه شد. لطفاً دوباره تلاش کنید.'); ?></p>
|
||
</div>
|
||
<?php elseif ($payment_status === 'error'): ?>
|
||
<div class="ifnex-error-box">
|
||
<h3>⚠️ خطا در پرداخت</h3>
|
||
<p><?php echo esc_html($payment_message ?: 'خطای سیستمی رخ داده است.'); ?></p>
|
||
</div>
|
||
<?php else: ?>
|
||
|
||
<div class="ifnex-order-summary">
|
||
<div class="ifnex-summary-row">
|
||
<span>شماره پیگیری:</span>
|
||
<strong><?php echo esc_html($data['awb_no']); ?></strong>
|
||
</div>
|
||
<div class="ifnex-summary-row">
|
||
<span>مسیر:</span>
|
||
<strong><?php echo esc_html($data['from_country']['name']); ?> → <?php echo esc_html($data['to_country']['name']); ?></strong>
|
||
</div>
|
||
<div class="ifnex-summary-row total">
|
||
<span>مبلغ قابل پرداخت:</span>
|
||
<strong><?php echo number_format($data['total_fee']); ?> ریال</strong>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ifnex-payment-methods">
|
||
<!-- پرداخت از کیف پول -->
|
||
<div class="ifnex-payment-option <?php echo $can_pay_wallet ? '' : 'disabled'; ?>">
|
||
<h3>💰 پرداخت از کیف پول</h3>
|
||
<p>موجودی فعلی: <strong><?php echo number_format($wallet_balance); ?> ریال</strong></p>
|
||
<?php if ($can_pay_wallet): ?>
|
||
<button class="ifnex-btn ifnex-btn-success ifnex-pay-wallet" data-order-id="<?php echo esc_attr($order_id); ?>">
|
||
پرداخت فوری از کیف پول
|
||
</button>
|
||
<?php else: ?>
|
||
<p class="ifnex-warning">⚠️ موجودی کافی نیست. لطفاً ابتدا کیف پول خود را شارژ کنید.</p>
|
||
<a href="<?php echo esc_url(home_url('/wallet/')); ?>" class="ifnex-btn ifnex-btn-info">شارژ کیف پول</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<!-- پرداخت از درگاه -->
|
||
<div class="ifnex-payment-option">
|
||
<h3>🏦 پرداخت از درگاه بانکی</h3>
|
||
<p>انتقال به درگاه پرداخت امن (زرینپال)</p>
|
||
<button class="ifnex-btn ifnex-btn-primary ifnex-pay-gateway" data-order-id="<?php echo esc_attr($order_id); ?>">
|
||
انتقال به درگاه بانکی
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="ifnex-payment-message" style="display:none; margin-top:20px;"></div>
|
||
|
||
<div class="ifnex-payment-footer">
|
||
<a href="<?php echo esc_url(home_url('/my-orders/')); ?>" class="ifnex-btn ifnex-btn-sm">
|
||
← بازگشت به سفارشات
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
<script>
|
||
jQuery(document).ready(function($) {
|
||
// پرداخت از کیف پول
|
||
$('.ifnex-pay-wallet').on('click', function() {
|
||
if (!confirm('آیا از پرداخت از کیف پول اطمینان دارید؟')) return;
|
||
|
||
var btn = $(this);
|
||
var orderId = btn.data('order-id');
|
||
|
||
console.log('═══════════════════════════════════════');
|
||
console.log('🚀 Starting wallet payment');
|
||
console.log('Order ID:', orderId);
|
||
console.log('AJAX URL:', ifnex_ajax.ajax_url);
|
||
console.log('Nonce:', ifnex_ajax.nonce);
|
||
console.log('═══════════════════════════════════════');
|
||
|
||
btn.prop('disabled', true).text('در حال پرداخت...');
|
||
|
||
$.ajax({
|
||
url: ifnex_ajax.ajax_url,
|
||
method: 'POST',
|
||
data: {
|
||
action: 'ifnex_pay_order_wallet',
|
||
order_id: orderId,
|
||
nonce: ifnex_ajax.nonce
|
||
},
|
||
beforeSend: function() {
|
||
console.log('📤 Sending AJAX request...');
|
||
},
|
||
success: function(res) {
|
||
console.log('📥 Response received:', res);
|
||
console.log('Success:', res.success);
|
||
console.log('Data:', res.data);
|
||
|
||
if (res.success) {
|
||
console.log('✅ Payment successful!');
|
||
console.log('New status:', res.data?.status?.value);
|
||
console.log('Transaction ID:', res.data?.transaction_id);
|
||
|
||
$('#ifnex-payment-message')
|
||
.removeClass('ifnex-error-box')
|
||
.addClass('ifnex-success-box')
|
||
.html('✅ پرداخت با موفقیت انجام شد! در حال انتقال...')
|
||
.show();
|
||
|
||
setTimeout(function() {
|
||
window.location.href = ifnex_ajax.orders_url + '?t=' + Date.now();
|
||
}, 2000);
|
||
} else {
|
||
console.error('❌ Payment failed:', res.data);
|
||
btn.prop('disabled', false).text('پرداخت فوری از کیف پول');
|
||
$('#ifnex-payment-message')
|
||
.addClass('ifnex-error-box')
|
||
.text('خطا: ' + (res.data || 'نامشخص'))
|
||
.show();
|
||
}
|
||
},
|
||
error: function(xhr, status, error) {
|
||
console.error('═══════════════════════════════════════');
|
||
console.error('❌ AJAX ERROR');
|
||
console.error('Status:', status);
|
||
console.error('Error:', error);
|
||
console.error('Response Code:', xhr.status);
|
||
console.error('Response Text:', xhr.responseText);
|
||
console.error('═══════════════════════════════════════');
|
||
|
||
btn.prop('disabled', false).text('پرداخت فوری از کیف پول');
|
||
$('#ifnex-payment-message')
|
||
.addClass('ifnex-error-box')
|
||
.html('خطا در ارتباط با سرور<br><small>' + error + '</small>')
|
||
.show();
|
||
}
|
||
});
|
||
});
|
||
|
||
// پرداخت از درگاه
|
||
$('.ifnex-pay-gateway').on('click', function() {
|
||
var btn = $(this);
|
||
var orderId = btn.data('order-id');
|
||
|
||
btn.prop('disabled', true).text('در حال اتصال به درگاه...');
|
||
|
||
$.ajax({
|
||
url: ifnex_ajax.ajax_url,
|
||
method: 'POST',
|
||
data: {
|
||
action: 'ifnex_pay_order_gateway',
|
||
order_id: orderId,
|
||
callback_url: window.location.href,
|
||
nonce: ifnex_ajax.nonce
|
||
},
|
||
success: function(res) {
|
||
if (res.success && res.data.payment_url) {
|
||
window.location.href = res.data.payment_url;
|
||
} else {
|
||
btn.prop('disabled', false).text('انتقال به درگاه بانکی');
|
||
$('#ifnex-payment-message')
|
||
.addClass('ifnex-error-box')
|
||
.text('خطا: ' + (res.data || 'خطا در اتصال به درگاه'))
|
||
.show();
|
||
}
|
||
},
|
||
error: function() {
|
||
btn.prop('disabled', false).text('انتقال به درگاه بانکی');
|
||
$('#ifnex-payment-message')
|
||
.addClass('ifnex-error-box')
|
||
.text('خطا در ارتباط با سرور')
|
||
.show();
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// شورتکد جزئیات سفارش
|
||
// ══════════════════════════════════════════════════════════════
|
||
|
||
add_shortcode('ifnex_order_detail', 'ifnex_order_detail_shortcode');
|
||
|
||
function ifnex_order_detail_shortcode($atts) {
|
||
if (!is_user_logged_in()) {
|
||
return '<p class="ifnex-error">برای مشاهده جزئیات، باید وارد شوید.</p>';
|
||
}
|
||
|
||
$atts = shortcode_atts(array('order_id' => ''), $atts);
|
||
$order_id = $atts['order_id'] ?: ($_GET['order_id'] ?? 0);
|
||
|
||
if (!$order_id && isset($_SERVER['REQUEST_URI'])) {
|
||
if (preg_match('/order-detail\/(\d+)/', $_SERVER['REQUEST_URI'], $matches)) {
|
||
$order_id = $matches[1];
|
||
}
|
||
}
|
||
|
||
if (!$order_id) {
|
||
return '<p class="ifnex-error">شناسه سفارش مشخص نشده است.</p>';
|
||
}
|
||
|
||
|
||
// ⚠️ هندلر دانلودها (PDF و تعهدنامه) به template_redirect منتقل شده —
|
||
// اجرای آن داخل شورتکد یعنی بعد از چاپ HTML هدر قالب؛ خروجی باینری به
|
||
// HTML میچسبد و با Content-Length ناهمخوان، PDF نیمهتمام دانلود میشود.
|
||
|
||
$bridge = new IFNEX_User_Bridge();
|
||
$user_id = get_current_user_id();
|
||
|
||
$order = $bridge->get_customer_order($user_id, $order_id);
|
||
|
||
if (is_wp_error($order)) {
|
||
return '<p class="ifnex-error">' . esc_html($order->get_error_message()) . '</p>';
|
||
}
|
||
|
||
$data = $order['data'];
|
||
$status = $data['status'];
|
||
$awb = $data['awb_no'];
|
||
$oid = (int)$order_id;
|
||
$dl_base = home_url('/order-detail/?order_id=' . $oid . '&download=');
|
||
|
||
// مراحل پیشرفت
|
||
$status_val = $status['value'] ?? '';
|
||
$stage_order = ['pending_approval'=>1,'approved'=>2,'processed'=>3,'picked_up'=>4,'in_transit'=>5,'out_for_delivery'=>6,'delivered'=>7];
|
||
$current_stage = $stage_order[$status_val] ?? 0;
|
||
$stages = [
|
||
1 => 'ثبت', 2 => 'تأیید', 3 => 'پرداخت', 4 => 'جمعآوری',
|
||
5 => 'ارسال', 6 => 'تحویل'
|
||
];
|
||
|
||
ob_start();
|
||
?>
|
||
<style>
|
||
.ifnex-od{font-family:inherit;color:#0f172a}
|
||
.ifnex-od h3{font-size:15px;font-weight:700;margin:0 0 12px;display:flex;align-items:center;gap:6px}
|
||
.ifnex-od-header{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;margin-bottom:16px}
|
||
.ifnex-od-header h2{font-size:20px;font-weight:800;margin:0}
|
||
.ifnex-od-awb{background:#f1f5f9;padding:4px 10px;border-radius:6px;font-size:13px;color:#475569}
|
||
.ifnex-od-actions{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
|
||
.ifnex-od-actions .ifnex-btn{font-size:13px;padding:8px 14px;border-radius:8px;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:4px;border:none}
|
||
.ifnex-od-actions .ifnex-btn-sm{font-size:13px}
|
||
.ifnex-od-actions .ifnex-btn-success{background:#10b981;color:#fff}
|
||
.ifnex-od-actions .ifnex-btn-danger{background:#ef4444;color:#fff}
|
||
.ifnex-od-actions .ifnex-btn-info{background:#3b82f6;color:#fff}
|
||
.ifnex-od-actions .ifnex-btn-default{background:#f1f5f9;color:#475569}
|
||
.ifnex-od-actions .ifnex-btn-warning{background:#f59e0b;color:#fff}
|
||
.ifnex-od-review{background:#fffbeb;border:1px solid #fde68a;border-radius:12px;padding:16px;margin-bottom:16px}
|
||
.ifnex-od-review h4{margin:0 0 8px;color:#92400e;font-size:15px}
|
||
.ifnex-od-review p{margin:4px 0;font-size:13px;color:#78350f}
|
||
.ifnex-od-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
|
||
.ifnex-od-left,.ifnex-od-right{display:flex;flex-direction:column;gap:16px}
|
||
.ifnex-od-card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:16px;box-shadow:0 1px 3px rgba(0,0,0,.04)}
|
||
.ifnex-od-card.sticky{position:sticky;top:16px}
|
||
.ifnex-od-route{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
|
||
.ifnex-od-route-point{flex:1;min-width:120px}
|
||
.ifnex-od-route-point strong{display:block;font-size:12px;color:#64748b;margin-bottom:2px}
|
||
.ifnex-od-route-point span{font-size:14px;font-weight:600}
|
||
.ifnex-od-plane{font-size:20px;color:#f59e0b}
|
||
.ifnex-od-info{display:grid;grid-template-columns:1fr 1fr;gap:8px}
|
||
.ifnex-od-info>div{font-size:13px}
|
||
.ifnex-od-info>div span{color:#64748b;display:block;font-size:11px}
|
||
.ifnex-od-table{width:100%;border-collapse:collapse;font-size:12px}
|
||
.ifnex-od-table th{background:#f8fafc;padding:8px 10px;text-align:right;font-weight:600;color:#64748b;border-bottom:1px solid #e2e8f0}
|
||
.ifnex-od-table td{padding:8px 10px;border-bottom:1px solid #f1f5f9}
|
||
.ifnex-od-table tbody tr:last-child td{border-bottom:none}
|
||
.ifnex-od-people{display:grid;grid-template-columns:1fr 1fr;gap:12px}
|
||
.ifnex-od-person h4{margin:0 0 6px;font-size:13px;color:#64748b}
|
||
.ifnex-od-person p{margin:3px 0;font-size:13px}
|
||
.ifnex-od-progress{display:flex;justify-content:space-between;align-items:center;gap:4px;position:relative}
|
||
.ifnex-od-progress::before{content:'';position:absolute;top:14px;right:5%;left:5%;height:2px;background:#e2e8f0;z-index:0}
|
||
.ifnex-od-stage{display:flex;flex-direction:column;align-items:center;gap:4px;z-index:1;flex:1}
|
||
.ifnex-od-stage-dot{width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700}
|
||
.ifnex-od-stage.done .ifnex-od-stage-dot{background:#10b981;color:#fff}
|
||
.ifnex-od-stage.current .ifnex-od-stage-dot{background:#f59e0b;color:#fff;box-shadow:0 0 0 4px rgba(245,158,11,.15)}
|
||
.ifnex-od-stage.pending .ifnex-od-stage-dot{background:#e2e8f0;color:#94a3b8}
|
||
.ifnex-od-stage-label{font-size:10px;color:#64748b;text-align:center;white-space:nowrap}
|
||
.ifnex-od-stage.done .ifnex-od-stage-label{color:#10b981;font-weight:600}
|
||
.ifnex-od-stage.current .ifnex-od-stage-label{color:#f59e0b;font-weight:600}
|
||
.ifnex-od-price{text-align:center}
|
||
.ifnex-od-price-total{font-size:22px;font-weight:800;color:#0f172a;margin-bottom:4px}
|
||
.ifnex-od-price-total small{font-size:12px;color:#64748b;font-weight:400}
|
||
.ifnex-od-price-status{font-size:13px}
|
||
.ifnex-od-price-status.paid{color:#10b981}
|
||
.ifnex-od-price-status.unpaid{color:#f59e0b}
|
||
.ifnex-od-dl{display:flex;gap:8px;flex-wrap:wrap}
|
||
.ifnex-od-dl-btn{flex:1;min-width:100px;text-align:center;padding:12px 8px;border-radius:10px;background:#f8fafc;border:1px solid #e2e8f0;text-decoration:none;color:#475569;font-size:13px;font-weight:600;transition:all .15s;display:flex;flex-direction:column;align-items:center;gap:4px}
|
||
.ifnex-od-dl-btn:hover{background:#fffbeb;border-color:#fde68a;color:#d97706}
|
||
.ifnex-od-dl-icon{font-size:20px}
|
||
.ifnex-od-timeline{position:relative;padding-right:24px}
|
||
.ifnex-od-timeline::before{content:'';position:absolute;top:0;bottom:0;right:11px;width:2px;background:#e2e8f0}
|
||
.ifnex-od-tl-item{position:relative;padding-bottom:16px;padding-right:12px}
|
||
.ifnex-od-tl-item:last-child{padding-bottom:0}
|
||
.ifnex-od-tl-dot{position:absolute;right:0;top:4px;width:12px;height:12px;border-radius:50%;background:#f59e0b;border:2px solid #fff;box-shadow:0 0 0 2px #fde68a}
|
||
.ifnex-od-tl-item:last-child .ifnex-od-tl-dot{background:#10b981;box-shadow:0 0 0 2px #d1fae5}
|
||
.ifnex-od-tl-body strong{font-size:13px;display:block}
|
||
.ifnex-od-tl-date{font-size:11px;color:#94a3b8}
|
||
.ifnex-od-tl-loc{font-size:12px;color:#64748b;margin-top:2px}
|
||
.ifnex-od-full{margin-top:16px}
|
||
@media(max-width:768px){
|
||
.ifnex-od-grid{grid-template-columns:1fr}
|
||
.ifnex-od-card.sticky{position:static}
|
||
.ifnex-od-people{grid-template-columns:1fr}
|
||
.ifnex-od-progress::before{display:none}
|
||
.ifnex-od-stage{gap:2px}
|
||
}
|
||
</style>
|
||
|
||
<div class="ifnex-od">
|
||
<div class="ifnex-od-header">
|
||
<div>
|
||
<h2>📦 جزئیات سفارش</h2>
|
||
<code class="ifnex-od-awb"><?php echo esc_html($awb); ?></code>
|
||
</div>
|
||
<span class="ifnex-badge ifnex-badge-<?php echo esc_attr($status['color']); ?> ifnex-badge-lg"><?php echo esc_html($status['label']); ?></span>
|
||
</div>
|
||
|
||
<div class="ifnex-od-actions">
|
||
<a href="<?php echo esc_url(home_url('/my-orders/')); ?>" class="ifnex-btn ifnex-btn-sm ifnex-btn-default">← بازگشت</a>
|
||
<?php if ($status['can_pay']): ?>
|
||
<a href="<?php echo esc_url(home_url('/order-payment/?order_id=' . $oid)); ?>" class="ifnex-btn ifnex-btn-sm ifnex-btn-success">💳 پرداخت</a>
|
||
<?php endif; ?>
|
||
<?php if ($status['can_cancel']): ?>
|
||
<button class="ifnex-btn ifnex-btn-sm ifnex-btn-danger ifnex-cancel-order-detail" data-order-id="<?php echo esc_attr($oid); ?>">❌ لغو</button>
|
||
<?php endif; ?>
|
||
<button onclick="window.print()" class="ifnex-btn ifnex-btn-sm ifnex-btn-info">🖨️ چاپ</button>
|
||
</div>
|
||
|
||
<?php if (!empty($data['review']) && ($data['review']['state'] ?? '') === 'changes_requested'): ?>
|
||
<div class="ifnex-od-review">
|
||
<h4>⚠️ نیاز به اصلاح اطلاعات سفارش</h4>
|
||
<?php if (!empty($data['review']['latest']['reason'])): ?><p><strong>دلیل:</strong> <?php echo esc_html($data['review']['latest']['reason']); ?></p><?php endif; ?>
|
||
<?php if (!empty($data['review']['latest']['notes'])): ?><p><strong>توضیحات:</strong> <?php echo nl2br(esc_html($data['review']['latest']['notes'])); ?></p><?php endif; ?>
|
||
<?php if (!empty($data['review']['can_resubmit'])): ?>
|
||
<a href="<?php echo esc_url(home_url('/new-order/?resubmit=' . $oid)); ?>" class="ifnex-btn ifnex-btn-sm ifnex-btn-warning">✏️ اصلاح و ارسال مجدد</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="ifnex-od-grid">
|
||
<div class="ifnex-od-left">
|
||
<div class="ifnex-od-card">
|
||
<h3>🗺️ مسیر ارسال</h3>
|
||
<div class="ifnex-od-route">
|
||
<div class="ifnex-od-route-point"><strong>از</strong><span><?php echo esc_html($data['from_country']['name'] ?? '—'); ?></span></div>
|
||
<div class="ifnex-od-plane">✈️</div>
|
||
<div class="ifnex-od-route-point"><strong>به</strong><span><?php echo esc_html($data['to_country']['name'] ?? '—'); ?></span></div>
|
||
</div>
|
||
</div>
|
||
<div class="ifnex-od-card">
|
||
<h3>📦 اطلاعات مرسوله</h3>
|
||
<div class="ifnex-od-info">
|
||
<div><span>نوع سرویس</span><?php echo esc_html($data['type'] ?? '—'); ?></div>
|
||
<div><span>جهت</span><?php echo ($data['direction'] ?? '') === 'export' ? 'صادرات' : 'واردات'; ?></div>
|
||
<div><span>وزن واقعی</span><?php echo esc_html($data['weight'] ?? '—'); ?> kg</div>
|
||
<div><span>وزن محاسبهای</span><?php echo esc_html($data['chargeable_weight'] ?? '—'); ?> kg</div>
|
||
<div><span>تاریخ ثبت</span><?php echo esc_html($data['created_at_jalali'] ?? '—'); ?></div>
|
||
</div>
|
||
</div>
|
||
<?php if (!empty($data['packages']) && is_array($data['packages'])): ?>
|
||
<div class="ifnex-od-card">
|
||
<h3>📦 بستهها</h3>
|
||
<table class="ifnex-od-table">
|
||
<thead><tr><th>#</th><th>وزن</th><th>وزن حجمی</th><th>ابعاد</th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($data['packages'] as $pkg): ?>
|
||
<tr><td><?php echo esc_html($pkg['package_no'] ?? '—'); ?></td><td><?php echo esc_html($pkg['weight'] ?? '—'); ?> kg</td><td><?php echo esc_html($pkg['volumetric_weight'] ?? '—'); ?> kg</td><td><?php echo esc_html($pkg['dimensions'] ?? '—'); ?></td></tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php endif; ?>
|
||
<div class="ifnex-od-card">
|
||
<h3>👤 فرستنده / 📮 گیرنده</h3>
|
||
<div class="ifnex-od-people">
|
||
<div class="ifnex-od-person">
|
||
<h4>👤 فرستنده</h4>
|
||
<p><?php echo esc_html($data['sender']['name'] ?? '—'); ?></p>
|
||
<p>📞 <?php echo esc_html($data['sender']['phone'] ?? '—'); ?></p>
|
||
<p>📍 <?php echo esc_html($data['sender']['address'] ?? '—'); ?></p>
|
||
</div>
|
||
<div class="ifnex-od-person">
|
||
<h4>📮 گیرنده</h4>
|
||
<p><?php echo esc_html($data['receiver']['name'] ?? '—'); ?></p>
|
||
<p>📞 <?php echo esc_html($data['receiver']['phone'] ?? '—'); ?></p>
|
||
<p>📍 <?php echo esc_html($data['receiver']['address'] ?? '—'); ?></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ifnex-od-right">
|
||
<div class="ifnex-od-card">
|
||
<h3>📊 مراحل سفارش</h3>
|
||
<div class="ifnex-od-progress">
|
||
<?php foreach ($stages as $num => $label):
|
||
$cls = $num <= $current_stage ? 'done' : ($num === $current_stage + 1 ? 'current' : 'pending');
|
||
$icon = $num <= $current_stage ? '✓' : $num;
|
||
?>
|
||
<div class="ifnex-od-stage <?php echo $cls; ?>">
|
||
<div class="ifnex-od-stage-dot"><?php echo $icon; ?></div>
|
||
<span class="ifnex-od-stage-label"><?php echo $label; ?></span>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
<div class="ifnex-od-card sticky">
|
||
<h3>💰 خلاصه مالی</h3>
|
||
<div class="ifnex-od-price">
|
||
<div class="ifnex-od-price-total"><?php echo number_format((float)($data['total_fee'] ?? 0)); ?><br><small>ریال</small></div>
|
||
<div class="ifnex-od-price-status <?php echo $status['is_paid'] ? 'paid' : 'unpaid'; ?>"><?php echo $status['is_paid'] ? '✅ پرداخت شده' : '⏳ در انتظار پرداخت'; ?></div>
|
||
<?php if (!empty($data['net_dirham'])): ?><div style="font-size:12px;color:#94a3b8;margin-top:4px"><?php echo number_format((float)$data['net_dirham']); ?> AED</div><?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<div class="ifnex-od-card">
|
||
<h3>📄 دانلود مدارک</h3>
|
||
<div class="ifnex-od-dl">
|
||
<a href="<?php echo esc_url($dl_base . 'awb'); ?>" class="ifnex-od-dl-btn" target="_blank"><span class="ifnex-od-dl-icon">📦</span>AWB</a>
|
||
<a href="<?php echo esc_url($dl_base . 'label'); ?>" class="ifnex-od-dl-btn" target="_blank"><span class="ifnex-od-dl-icon">🏷️</span>لیبل</a>
|
||
<?php if (!empty($data['items']) || ($data['type'] ?? '') === 'PARCEL'): ?>
|
||
<a href="<?php echo esc_url($dl_base . 'invoice'); ?>" class="ifnex-od-dl-btn" target="_blank"><span class="ifnex-od-dl-icon">🧾</span>فاکتور</a>
|
||
<?php endif; ?>
|
||
<?php if (($data['direction'] ?? '') === 'import'): ?>
|
||
<a href="<?php echo esc_url($dl_base . 'import-invoice'); ?>" class="ifnex-od-dl-btn" target="_blank"><span class="ifnex-od-dl-icon">📋</span>فاکتور خدمات</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<div class="ifnex-od-card">
|
||
<h3>📝 تعهدنامهها</h3>
|
||
<div id="ifnex-commitment-forms" data-order-id="<?php echo esc_attr($order_id); ?>">
|
||
<p style="color:#94a3b8">در حال بارگذاری...</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ifnex-od-card ifnex-od-full">
|
||
<h3>📍 تاریخچه رهگیری</h3>
|
||
<?php if (!empty($data['tracking_events'])): ?>
|
||
<div class="ifnex-od-timeline">
|
||
<?php foreach ($data['tracking_events'] as $ev): ?>
|
||
<div class="ifnex-od-tl-item">
|
||
<div class="ifnex-od-tl-dot"></div>
|
||
<div class="ifnex-od-tl-body">
|
||
<strong><?php echo esc_html($ev['description'] ?? ''); ?></strong>
|
||
<div class="ifnex-od-tl-date"><?php echo esc_html($ev['date'] ?? ''); ?></div>
|
||
<?php if (!empty($ev['location'])): ?><div class="ifnex-od-tl-loc">📍 <?php echo esc_html($ev['location']); ?></div><?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php else: ?>
|
||
<p style="color:#94a3b8;font-size:13px">هنوز رویدادی ثبت نشده است.</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
jQuery(document).ready(function($) {
|
||
$('.ifnex-cancel-order-detail').on('click', function() {
|
||
if (!confirm('آیا از لغو این سفارش اطمینان دارید؟')) return;
|
||
var btn = $(this);
|
||
var orderId = btn.data('order-id');
|
||
btn.prop('disabled', true).text('در حال لغو...');
|
||
$.ajax({
|
||
url: ifnex_ajax.ajax_url, method: 'POST',
|
||
data: { action: 'ifnex_cancel_order', order_id: orderId, nonce: ifnex_ajax.nonce },
|
||
success: function(r) { if (r.success) { alert('سفارش لغو شد.'); location.reload(); } else { alert('خطا: ' + r.data); btn.prop('disabled', false).text('❌ لغو'); } },
|
||
error: function() { alert('خطا در ارتباط با سرور.'); btn.prop('disabled', false).text('❌ لغو'); }
|
||
});
|
||
|
||
});
|
||
|
||
// بارگذاری تعهدنامهها
|
||
$.ajax({
|
||
url: ifnex_ajax.ajax_url,
|
||
method: 'POST',
|
||
data: { action: 'ifnex_get_commitment_forms', order_id: <?php echo $oid; ?>, nonce: ifnex_ajax.nonce },
|
||
success: function(res) {
|
||
if (res.success && res.data && res.data.length > 0) {
|
||
var html = '';
|
||
res.data.forEach(function(form) {
|
||
var badge = form.upload_status === 'verified' ? '<span style="color:#10b981">✅ تأیید شده</span>'
|
||
: form.upload_status === 'uploaded' ? '<span style="color:#3b82f6">✅ آپلود شده (در انتظار بررسی)</span>'
|
||
: form.upload_status === 'rejected' ? '<span style="color:#ef4444">❌ رد شده</span>'
|
||
: '<span style="color:#94a3b8">⏳ آپلود نشده</span>';
|
||
var reasonHtml = (form.upload_status === 'rejected' && form.notes)
|
||
? '<div style="color:#ef4444;font-size:12px;margin-top:4px">⚠️ دلیل رد: ' + form.notes + '</div>'
|
||
: '';
|
||
// دانلود از طریق پروکسی وردپرس (توکن لاراول سمت سرور ارسال میشود؛ لینک مستقیم به API بدون توکن 401/500 میدهد)
|
||
var dlHref = '?order_id=<?php echo $oid; ?>&download=commitment-template&form_id=' + form.id;
|
||
var signedHref = '?order_id=<?php echo $oid; ?>&download=commitment-signed&form_id=' + form.id;
|
||
html += '<div class="ifnex-form-actions" style="border:1px solid #e2e8f0;border-radius:10px;padding:12px;margin-bottom:8px">';
|
||
html += '<div style="display:flex;justify-content:space-between;align-items:start;gap:8px;flex-wrap:wrap">';
|
||
html += '<div><strong>' + form.title + '</strong><br><small style="color:#64748b">' + (form.description || '') + '</small>' + reasonHtml + '</div>';
|
||
html += badge;
|
||
html += '</div><div style="display:flex;gap:8px;margin-top:8px;flex-wrap:wrap">';
|
||
if (form.file_url) html += '<a href="' + dlHref + '" target="_blank" class="ifnex-btn ifnex-btn-sm ifnex-btn-info">📥 دانلود فرم</a>';
|
||
if (form.uploaded_file_url) html += '<a href="' + signedHref + '" target="_blank" class="ifnex-btn ifnex-btn-sm ifnex-btn-success">📎 فایل آپلودشده</a>';
|
||
html += '<label class="ifnex-btn ifnex-btn-sm ifnex-btn-warning" style="cursor:pointer">📤 آپلود امضاشده<input type="file" accept=".pdf,.jpg,.jpeg,.png" style="display:none" onchange="uploadCommitmentForm(<?php echo $oid; ?>,' + form.id + ',this)"></label>';
|
||
html += '</div></div>';
|
||
});
|
||
$('#ifnex-commitment-forms').html(html);
|
||
} else {
|
||
$('#ifnex-commitment-forms').html('<p style="color:#94a3b8;font-size:13px">تعهدنامهای برای این سفارش موجود نیست.</p>');
|
||
}
|
||
},
|
||
error: function() {
|
||
$('#ifnex-commitment-forms').html('<p style="color:#ef4444;font-size:13px">خطا در بارگذاری تعهدنامهها.</p>');
|
||
}
|
||
});
|
||
});
|
||
function uploadCommitmentForm(orderId, formId, input) {
|
||
// بدون $ — وردپرس jQuery را در حالت noConflict لود میکند و $ سراسری وجود ندارد
|
||
var file = input.files[0]; if (!file) return;
|
||
var fd = new FormData();
|
||
fd.append('action', 'ifnex_upload_commitment_form');
|
||
fd.append('order_id', orderId); fd.append('form_id', formId);
|
||
fd.append('file', file); fd.append('nonce', ifnex_ajax.nonce);
|
||
var btn = jQuery(input).closest('.ifnex-form-actions'); var orig = btn.html();
|
||
btn.html('<span>در حال آپلود...</span>');
|
||
jQuery.ajax({
|
||
url: ifnex_ajax.ajax_url, method: 'POST', data: fd, processData: false, contentType: false,
|
||
success: function(r) { if (r.success) { alert('آپلود شد.'); location.reload(); } else { alert('خطا: ' + (r.data||'')); btn.html(orig); } },
|
||
error: function() { alert('خطا در ارتباط با سرور.'); btn.html(orig); }
|
||
});
|
||
}
|
||
</script>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// رهگیری درخواستهای دانلود قبل از هر خروجی صفحه (template_redirect)
|
||
// اجرای دانلود داخل شورتکد یعنی بعد از چاپ HTML هدر قالب — بایتهای HTML
|
||
// به ابتدای فایل میچسبند و با Content-Length ناهمخوان، فایل ناقص میرسد.
|
||
// ══════════════════════════════════════════════════════════════
|
||
|
||
add_action('template_redirect', 'ifnex_intercept_download_request', 1);
|
||
|
||
function ifnex_intercept_download_request(): void {
|
||
$download = sanitize_text_field($_GET['download'] ?? '');
|
||
$order_id = intval($_GET['order_id'] ?? 0);
|
||
if (!$download || !$order_id) return;
|
||
|
||
if (in_array($download, ['awb', 'invoice', 'label', 'import-invoice'], true)) {
|
||
ifnex_handle_pdf_download($order_id, $download); // exit داخل تابع
|
||
}
|
||
|
||
if (in_array($download, ['commitment-template', 'commitment-signed'], true)) {
|
||
ifnex_handle_commitment_download(
|
||
$order_id,
|
||
intval($_GET['form_id'] ?? 0),
|
||
$download === 'commitment-signed' ? 'signed' : 'template'
|
||
); // exit داخل تابع
|
||
}
|
||
}
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// آیکونهای SVG مدرن (سبک Heroicons)
|
||
// ══════════════════════════════════════════════════════════════
|
||
|
||
function ifnex_handle_pdf_download($order_id, $download_type) {
|
||
$user_id = get_current_user_id();
|
||
if (!$user_id) wp_die('برای دانلود باید وارد شوید.');
|
||
|
||
$bridge = new IFNEX_User_Bridge();
|
||
$order = $bridge->get_customer_order($user_id, $order_id);
|
||
if (is_wp_error($order)) wp_die('خطا: ' . $order->get_error_message());
|
||
|
||
$api_url = get_option('ifnex_api_url', 'http://localhost:8000/api/v1');
|
||
$download_url = rtrim($api_url, '/') . "/customer/orders/{$order_id}/pdf/{$download_type}";
|
||
|
||
$token = $bridge->get_user_token($user_id); // با تجدید خودکار در صورت انقضا/حذف
|
||
if (!$token) wp_die('توکن احراز هویت در دسترس نیست — لطفاً دوباره وارد شوید.');
|
||
|
||
$response = wp_remote_get($download_url, array(
|
||
'headers' => array(
|
||
'Authorization' => 'Bearer ' . $token,
|
||
'Accept' => 'application/pdf',
|
||
),
|
||
'timeout' => 30,
|
||
));
|
||
|
||
if (is_wp_error($response)) wp_die('خطا در ارتباط با سرور.');
|
||
|
||
$status_code = wp_remote_retrieve_response_code($response);
|
||
if ($status_code !== 200) wp_die('خطا در دانلود فایل.');
|
||
|
||
$file_content = wp_remote_retrieve_body($response);
|
||
|
||
// پاسخ باید PDF خام باشد؛ وگرنه بهجای فایل خراب، پیام واضح نمایش بده
|
||
if (substr($file_content, 0, 4) !== '%PDF') {
|
||
wp_die('پاسخ سرور یک فایل PDF معتبر نبود — لطفاً دوباره تلاش کنید.');
|
||
}
|
||
|
||
$filename = strtoupper($download_type) . '-' . $order['data']['awb_no'] . '.pdf';
|
||
|
||
// پاکسازی همه بافرهای خروجی تا جریان فایل تمیز و همطول با Content-Length ارسال شود
|
||
while (ob_get_level()) { ob_end_clean(); }
|
||
|
||
nocache_headers();
|
||
header('Content-Type: application/pdf');
|
||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||
header('Content-Length: ' . strlen($file_content));
|
||
echo $file_content;
|
||
exit;
|
||
}
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// پروکسی دانلود تعهدنامه (قالب خام یا فایل امضاشده) با توکن لاراول
|
||
// ══════════════════════════════════════════════════════════════
|
||
function ifnex_handle_commitment_download($order_id, $form_id, $type = 'template') {
|
||
$user_id = get_current_user_id();
|
||
if (!$user_id) wp_die('برای دانلود باید وارد شوید.');
|
||
|
||
$form_id = intval($form_id);
|
||
if (!$form_id) wp_die('شناسه فرم نامعتبر است.');
|
||
|
||
$bridge = new IFNEX_User_Bridge();
|
||
$order = $bridge->get_customer_order($user_id, $order_id);
|
||
if (is_wp_error($order)) wp_die('خطا: ' . $order->get_error_message());
|
||
|
||
$api_url = get_option('ifnex_api_url', 'http://localhost:8000/api/v1');
|
||
$endpoint = ($type === 'signed') ? 'download' : 'template';
|
||
$download_url = rtrim($api_url, '/') . "/customer/orders/{$order_id}/commitment-forms/{$form_id}/{$endpoint}";
|
||
|
||
$token = $bridge->get_user_token($user_id); // با تجدید خودکار در صورت انقضا/حذف
|
||
if (!$token) wp_die('توکن احراز هویت در دسترس نیست — لطفاً دوباره وارد شوید.');
|
||
|
||
$response = wp_remote_get($download_url, array(
|
||
'headers' => array(
|
||
'Authorization' => 'Bearer ' . $token,
|
||
'Accept' => '*/*',
|
||
),
|
||
'timeout' => 60,
|
||
));
|
||
|
||
if (is_wp_error($response)) wp_die('خطا در ارتباط با سرور.');
|
||
|
||
$status_code = wp_remote_retrieve_response_code($response);
|
||
if ($status_code !== 200) {
|
||
$body = wp_remote_retrieve_body($response);
|
||
$err = json_decode($body, true);
|
||
$msg = $err['message'] ?? ('خطا در دانلود فایل (کد: ' . $status_code . ')');
|
||
wp_die(esc_html($msg));
|
||
}
|
||
|
||
$file_content = wp_remote_retrieve_body($response);
|
||
|
||
// پاسخ باید فایل باینری باشد؛ شروع با '<' یعنی HTML/خطا برگشته
|
||
if ($file_content === '' || $file_content[0] === '<') {
|
||
wp_die('پاسخ سرور یک فایل معتبر نبود — لطفاً دوباره تلاش کنید.');
|
||
}
|
||
|
||
$awb = $order['data']['awb_no'] ?? ('order-' . $order_id);
|
||
|
||
// تشخیص نوع محتوا و نام فایل از روی هدر Laravel
|
||
$ctype = wp_remote_retrieve_header($response, 'content-type');
|
||
if (empty($ctype)) $ctype = 'application/octet-stream';
|
||
|
||
$disp = wp_remote_retrieve_header($response, 'content-disposition');
|
||
$filename = ($type === 'signed' ? 'signed-' : 'template-') . $form_id . '-' . $awb;
|
||
if ($disp && preg_match('/filename="?([^";]+)"?/i', $disp, $m)) {
|
||
$filename = trim($m[1]);
|
||
} else {
|
||
// fallback بر اساس content-type
|
||
$ext = (stripos($ctype, 'pdf') !== false) ? 'pdf'
|
||
: ((stripos($ctype, 'png') !== false) ? 'png'
|
||
: ((stripos($ctype, 'jpeg') !== false) ? 'jpg' : 'bin'));
|
||
$filename .= '.' . $ext;
|
||
}
|
||
|
||
// پاکسازی همه بافرهای خروجی تا جریان فایل تمیز و همطول با Content-Length ارسال شود
|
||
while (ob_get_level()) { ob_end_clean(); }
|
||
|
||
nocache_headers();
|
||
header('Content-Type: ' . $ctype);
|
||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||
header('Content-Length: ' . strlen($file_content));
|
||
echo $file_content;
|
||
exit;
|
||
}
|
||
|
||
function ifnex_icon($name, $size = 20) {
|
||
$icons = [
|
||
'dashboard' => '<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75"/>',
|
||
'orders' => '<path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9"/>',
|
||
'plus' => '<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/>',
|
||
'wallet' => '<path stroke-linecap="round" stroke-linejoin="round" d="M21 12a2.25 2.25 0 00-2.25-2.25H15a3 3 0 11-6 0H5.25A2.25 2.25 0 003 12m18 0v6a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 18v-6m18 0V9M3 12V9m18 0a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 9m18 0V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v3"/>',
|
||
'chart' => '<path stroke-linecap="round" stroke-linejoin="round" d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z"/>',
|
||
'tracking' => '<path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z"/>',
|
||
'user' => '<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z"/>',
|
||
'logout' => '<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9"/>',
|
||
'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"/>',
|
||
'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="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>',
|
||
];
|
||
|
||
$path = $icons[$name] ?? $icons['dashboard'];
|
||
|
||
return '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.7" stroke="currentColor" width="' . $size . '" height="' . $size . '">' . $path . '</svg>';
|
||
}
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// شورتکد پنل مشتری (Customer Dashboard)
|
||
// ══════════════════════════════════════════════════════════════
|
||
|
||
add_shortcode('ifnex_customer_dashboard', 'ifnex_customer_dashboard_shortcode');
|
||
|
||
function ifnex_customer_dashboard_shortcode($atts) {
|
||
if (!is_user_logged_in()) {
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-login-prompt">
|
||
<h3>🔐 ورود لازم است</h3>
|
||
<p>برای دسترسی به پنل کاربری، ابتدا وارد حساب خود شوید.</p>
|
||
<a href="<?php echo esc_url(wp_login_url(get_permalink())); ?>" class="ifnex-btn ifnex-btn-primary">ورود به حساب</a>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
$tab = sanitize_key($_GET['tab'] ?? 'dashboard');
|
||
$allowed_tabs = ['dashboard', 'notifications', 'orders', 'new-order', 'wallet', 'transactions', 'tracking', 'profile'];
|
||
if (!in_array($tab, $allowed_tabs)) $tab = 'dashboard';
|
||
|
||
$bridge = new IFNEX_User_Bridge();
|
||
$user_id = get_current_user_id();
|
||
$current_user = wp_get_current_user();
|
||
|
||
$profile = $bridge->get_customer_profile($user_id);
|
||
$wallet_balance = 0; $stats = [];
|
||
if (!is_wp_error($profile)) {
|
||
$wallet_balance = $profile['wallet']['balance'] ?? 0;
|
||
$stats = $profile['stats'] ?? [];
|
||
}
|
||
|
||
// تعداد اعلانهای خواندهنشده برای badge سایدبار
|
||
$unread_notifications = 0;
|
||
$notif_count_result = $bridge->get_notifications($user_id, 1);
|
||
if (!is_wp_error($notif_count_result) && isset($notif_count_result['unread_count'])) {
|
||
$unread_notifications = (int) $notif_count_result['unread_count'];
|
||
}
|
||
|
||
$recent_orders = [];
|
||
if ($tab === 'dashboard') {
|
||
$orders_result = $bridge->get_customer_orders($user_id, 3);
|
||
if (!is_wp_error($orders_result)) $recent_orders = $orders_result['data'] ?? [];
|
||
}
|
||
|
||
$base_url = get_permalink();
|
||
$menu_items = [
|
||
'dashboard' => ['icon' => 'dashboard', 'label' => 'داشبورد'],
|
||
'notifications'=> ['icon' => 'bell', 'label' => 'اعلانها'],
|
||
'orders' => ['icon' => 'orders', 'label' => 'سفارشات من'],
|
||
'new-order' => ['icon' => 'plus', 'label' => 'ثبت سفارش جدید'],
|
||
'wallet' => ['icon' => 'wallet', 'label' => 'کیف پول'],
|
||
'transactions' => ['icon' => 'chart', 'label' => 'تراکنشها'],
|
||
'tracking' => ['icon' => 'tracking', 'label' => 'رهگیری مرسوله'],
|
||
'profile' => ['icon' => 'user', 'label' => 'پروفایل'],
|
||
];
|
||
|
||
$tab_titles = [
|
||
'dashboard' => 'داشبورد', 'notifications' => 'اعلانها', 'orders' => 'سفارشات من', 'new-order' => 'ثبت سفارش جدید',
|
||
'wallet' => 'کیف پول', 'transactions' => 'تراکنشها', 'tracking' => 'رهگیری مرسوله', 'profile' => 'پروفایل',
|
||
];
|
||
|
||
ob_start();
|
||
?>
|
||
<style>.ifnx-menu-badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:10px;background:#ef4444;color:#fff;font-size:11px;font-weight:700;margin-inline-start:auto}</style>
|
||
<div class="ifnx-dashboard">
|
||
<!-- سایدبار -->
|
||
<aside class="ifnx-sidebar">
|
||
<div class="ifnx-user-card">
|
||
<div class="ifnx-avatar"><?php echo ifnex_icon('user', 26); ?></div>
|
||
<div class="ifnx-user-name"><?php echo esc_html($current_user->display_name); ?></div>
|
||
<div class="ifnx-user-email"><?php echo esc_html($current_user->user_email); ?></div>
|
||
</div>
|
||
<nav class="ifnx-menu">
|
||
<?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"><?php echo ifnex_icon($item['icon'], 19); ?></span>
|
||
<span><?php echo esc_html($item['label']); ?></span>
|
||
<?php if ($key === 'notifications' && $unread_notifications > 0): ?>
|
||
<span class="ifnx-menu-badge"><?php echo (int)$unread_notifications; ?></span>
|
||
<?php endif; ?>
|
||
</a>
|
||
<?php endforeach; ?>
|
||
<div class="ifnx-menu-divider"></div>
|
||
<a href="<?php echo esc_url(wp_logout_url(home_url())); ?>" class="ifnx-menu-item logout">
|
||
<span class="ifnx-menu-icon"><?php echo ifnex_icon('logout', 19); ?></span>
|
||
<span>خروج از حساب</span>
|
||
</a>
|
||
</nav>
|
||
</aside>
|
||
|
||
<!-- محتوا -->
|
||
<main class="ifnx-content">
|
||
<div class="ifnx-content-header">
|
||
<h2><?php echo esc_html($tab_titles[$tab]); ?></h2>
|
||
<span class="ifnx-breadcrumb">پنل کاربری / <?php echo esc_html($tab_titles[$tab]); ?></span>
|
||
</div>
|
||
|
||
<?php if ($tab === 'dashboard'): ?>
|
||
<div class="ifnx-stats">
|
||
<div class="ifnx-stat" style="--tint:#ecfdf5; --clr:#10b981;">
|
||
<div class="ifnx-stat-icon"><?php echo ifnex_icon('wallet', 22); ?></div>
|
||
<div class="ifnx-stat-info">
|
||
<span class="ifnx-stat-value"><?php echo number_format($wallet_balance); ?></span>
|
||
<span class="ifnx-stat-label">موجودی کیف پول (ریال)</span>
|
||
</div>
|
||
</div>
|
||
<div class="ifnx-stat" style="--tint:#eff6ff; --clr:#3b82f6;">
|
||
<div class="ifnx-stat-icon"><?php echo ifnex_icon('orders', 22); ?></div>
|
||
<div class="ifnx-stat-info">
|
||
<span class="ifnx-stat-value"><?php echo number_format($stats['total_orders'] ?? 0); ?></span>
|
||
<span class="ifnx-stat-label">کل سفارشات</span>
|
||
</div>
|
||
</div>
|
||
<div class="ifnx-stat" style="--tint:#fffbeb; --clr:#f59e0b;">
|
||
<div class="ifnx-stat-icon"><?php echo ifnex_icon('clock', 22); ?></div>
|
||
<div class="ifnx-stat-info">
|
||
<span class="ifnx-stat-value"><?php echo number_format($stats['pending_payment'] ?? 0); ?></span>
|
||
<span class="ifnx-stat-label">در انتظار پرداخت</span>
|
||
</div>
|
||
</div>
|
||
<div class="ifnx-stat" style="--tint:#f5f3ff; --clr:#8b5cf6;">
|
||
<div class="ifnx-stat-icon"><?php echo ifnex_icon('truck', 22); ?></div>
|
||
<div class="ifnx-stat-info">
|
||
<span class="ifnx-stat-value"><?php echo number_format($stats['active_orders'] ?? 0); ?></span>
|
||
<span class="ifnx-stat-label">در حال پردازش</span>
|
||
</div>
|
||
</div>
|
||
<div class="ifnx-stat" style="--tint:#ecfdf5; --clr:#059669;">
|
||
<div class="ifnx-stat-icon"><?php echo ifnex_icon('check', 22); ?></div>
|
||
<div class="ifnx-stat-info">
|
||
<span class="ifnx-stat-value"><?php echo number_format($stats['delivered_orders'] ?? 0); ?></span>
|
||
<span class="ifnx-stat-label">تحویل شده</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ifnx-card">
|
||
<div class="ifnx-card-header">
|
||
<h3>آخرین سفارشات</h3>
|
||
<a href="<?php echo esc_url(add_query_arg('tab', 'orders', $base_url)); ?>" class="ifnx-link">مشاهده همه ←</a>
|
||
</div>
|
||
<?php if (empty($recent_orders)): ?>
|
||
<div class="ifnx-empty">
|
||
<?php echo ifnex_icon('orders', 40); ?>
|
||
<p>هنوز سفارشی ثبت نکردهاید.</p>
|
||
<a href="<?php echo esc_url(add_query_arg('tab', 'new-order', $base_url)); ?>" class="ifnex-btn ifnex-btn-primary">ثبت اولین سفارش</a>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="ifnx-order-list">
|
||
<?php foreach ($recent_orders as $order): ?>
|
||
<div class="ifnx-order-row">
|
||
<div class="ifnx-order-awb"><code><?php echo esc_html($order['awb_no']); ?></code></div>
|
||
<div class="ifnx-order-route"><?php echo esc_html($order['from_country']['name'] ?? '—'); ?> ← <?php echo esc_html($order['to_country']['name'] ?? '—'); ?></div>
|
||
<div class="ifnx-order-amount"><?php echo number_format($order['total_fee']); ?> <small>ریال</small></div>
|
||
<span class="ifnex-badge ifnex-badge-<?php echo esc_attr($order['status']['color']); ?>"><?php echo esc_html($order['status']['label']); ?></span>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<?php elseif ($tab === 'orders'): ?>
|
||
<?php echo do_shortcode('[ifnex_orders_list]'); ?>
|
||
<?php elseif ($tab === 'new-order'): ?>
|
||
<?php echo do_shortcode('[ifnex_order_form]'); ?>
|
||
<?php elseif ($tab === 'wallet'): ?>
|
||
<div class="ifnx-wallet-hero">
|
||
<div class="ifnx-wallet-label">موجودی فعلی کیف پول</div>
|
||
<div class="ifnx-wallet-amount"><?php echo number_format($wallet_balance); ?> <small>ریال</small></div>
|
||
<div class="ifnx-wallet-actions">
|
||
<a href="<?php echo esc_url(home_url('/wallet/')); ?>" class="ifnx-wallet-btn">+ شارژ کیف پول</a>
|
||
<a href="<?php echo esc_url(add_query_arg('tab', 'transactions', $base_url)); ?>" class="ifnx-wallet-btn ghost">مشاهده تراکنشها</a>
|
||
</div>
|
||
</div>
|
||
<?php elseif ($tab === 'transactions'): ?>
|
||
<?php echo do_shortcode('[ifnex_transactions]'); ?>
|
||
<?php elseif ($tab === 'tracking'): ?>
|
||
<?php echo do_shortcode('[ifnex_tracking_form]'); ?>
|
||
<?php elseif ($tab === 'notifications'): ?>
|
||
<?php
|
||
$notif_result = $bridge->get_notifications($user_id);
|
||
$notifications = [];
|
||
if (!is_wp_error($notif_result)) {
|
||
$notifications = $notif_result['data'] ?? $notif_result ?? [];
|
||
if (!isset($notif_result['data']) && isset($notif_result[0])) {
|
||
$notifications = $notif_result;
|
||
}
|
||
}
|
||
$unread = 0;
|
||
foreach ($notifications as $n) {
|
||
if (empty($n['read_at'])) $unread++;
|
||
}
|
||
?>
|
||
<style>
|
||
.ifnex-notifications-list{display:flex;flex-direction:column;gap:10px}
|
||
.ifnex-notification{display:flex;align-items:center;gap:14px;padding:16px;border-radius:12px;background:#f8fafc;border:1px solid #e2e8f0;transition:all .2s}
|
||
.ifnex-notification.unread{background:#fffbeb;border-color:#fde68a}
|
||
.ifnex-notification-icon{flex-shrink:0;width:42px;height:42px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.08)}
|
||
.ifnex-notification.unread .ifnex-notification-icon{background:#fef3c7}
|
||
.ifnex-notification-body{flex:1;min-width:0}
|
||
.ifnex-notification-msg{font-weight:600;color:#0f172a;font-size:14px;line-height:1.5}
|
||
.ifnex-notification-meta{font-size:12px;color:#64748b;margin-top:4px}
|
||
.ifnex-notification-meta code{background:#e2e8f0;padding:1px 6px;border-radius:4px;font-size:11px}
|
||
.ifnex-notification-time{font-size:11px;color:#94a3b8;margin-top:6px}
|
||
.ifnex-notification-action{flex-shrink:0;font-size:13px;font-weight:600;color:#f97316;text-decoration:none;white-space:nowrap}
|
||
.ifnex-notification-action:hover{text-decoration:underline}
|
||
</style>
|
||
<div class="ifnx-card">
|
||
<div class="ifnx-card-header">
|
||
<h3>اعلانها<?php if ($unread > 0): ?> <span class="ifnex-badge ifnex-badge-warning"><?php echo (int)$unread; ?></span><?php endif; ?></h3>
|
||
</div>
|
||
<?php if (empty($notifications)): ?>
|
||
<div class="ifnx-empty">
|
||
<?php echo ifnex_icon('bell', 40); ?>
|
||
<p>اعلانی ندارید.</p>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="ifnex-notifications-list">
|
||
<?php foreach ($notifications as $n):
|
||
$raw_data = $n['data'] ?? [];
|
||
$data = is_string($raw_data) ? json_decode($raw_data, true) : $raw_data;
|
||
$is_unread = empty($n['read_at']);
|
||
$msg = $data['message'] ?? '';
|
||
$awb = $data['awb_no'] ?? '';
|
||
$action = $data['action_url'] ?? '';
|
||
$created = $n['created_at'] ?? '';
|
||
$n_type = $data['type'] ?? '';
|
||
$icon_name = ($n_type === 'order_approved') ? 'check' : (($n_type === 'order_needs_changes') ? 'clock' : 'bell');
|
||
?>
|
||
<div class="ifnex-notification <?php echo $is_unread ? 'unread' : ''; ?>">
|
||
<div class="ifnex-notification-icon"><?php echo ifnex_icon($icon_name, 22); ?></div>
|
||
<div class="ifnex-notification-body">
|
||
<div class="ifnex-notification-msg"><?php echo esc_html($msg); ?></div>
|
||
<?php if ($awb): ?>
|
||
<div class="ifnex-notification-meta">سفارش: <code><?php echo esc_html($awb); ?></code></div>
|
||
<?php endif; ?>
|
||
<div class="ifnex-notification-time"><?php echo esc_html($created); ?></div>
|
||
</div>
|
||
<?php if ($action): ?>
|
||
<a href="<?php echo esc_url(home_url($action)); ?>" class="ifnex-notification-action">مشاهده ←</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php elseif ($tab === 'profile'): ?>
|
||
<?php echo do_shortcode('[ifnex_user_profile]'); ?>
|
||
<?php endif; ?>
|
||
</main>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|