ifnex/03_WordPress/wp-content/plugins/ifnex-bridge/includes/shortcodes.php
Kazem Alghasi 0209b6b32e feat(commitment-forms): implement end-to-end management and secure file access
Integrate a complete workflow for commitment forms including secure template downloads, customer uploads via WordPress AJAX, and administrative verification with automated notifications.

- Implement `downloadTemplate` and `downloadSigned` logic in `CommitmentFormController` to replace public symlinks with ownership-verified routes.
- Add `CommitmentFormVerifiedNotification` and `CommitmentFormRejectedNotification` to alert customers of status changes.
- Enhance WordPress bridge with AJAX-driven form listing, status badges, and upload capabilities.
- Update Filament `CommitmentFormsRelationManager` to provide better visibility into form directions and descriptions while restricting status edits to specific actions.
- Refactor Laravel 11 bootstrap configuration to handle guest redirection for API and web routes correctly.
- Add administrative controllers for secure file access within the admin panel.
2026-10-03 05:58:59 +03:30

1800 lines
98 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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>';
}
// Handle PDF download
$download_type = sanitize_text_field($_GET['download'] ?? '');
if ($download_type && in_array($download_type, ['awb', 'invoice', 'label', 'import-invoice'])) {
ifnex_handle_pdf_download($order_id, $download_type);
return '';
}
// Handle commitment form downloads (template / signed) — proxied با توکن لاراول
if (in_array($download_type, ['commitment-template', 'commitment-signed'], true)) {
$form_id = intval($_GET['form_id'] ?? 0);
$type = ($download_type === 'commitment-signed') ? 'signed' : 'template';
ifnex_handle_commitment_download($order_id, $form_id, $type);
return '';
}
$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();
}
// ══════════════════════════════════════════════════════════════
// آیکون‌های 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 = get_user_meta($user_id, 'ifnex_laravel_token', true);
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);
$filename = strtoupper($download_type) . '-' . $order['data']['awb_no'] . '.pdf';
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 = get_user_meta($user_id, 'ifnex_laravel_token', true);
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);
$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;
}
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();
}