Introduce a comprehensive address management system allowing users to
save, manage, and reuse sender and receiver addresses during the
ordering process. This feature bridges the WordPress frontend with the
Laravel backend API.
Key changes:
- Backend (Laravel):
- Added `AddressBookEntry` model and migration for storing addresses
with role (sender/receiver), usage tracking, and default status.
- Implemented `AddressBookController` with full CRUD functionality
secured via Sanctum and ownership validation.
- Integrated address usage tracking and upsert logic into the
`CustomerOrderController` to prevent duplicate entries.
- Added 18 new feature tests covering ownership, limits, and
validation.
- Frontend (WordPress):
- Added a new "Address Book" tab in the user dashboard with card-based
UI, filtering, and SweetAlert2 integration.
- Enhanced the order form with dropdown selectors for saved addresses
and "save this address" checkboxes for both sender and receiver.
- Implemented secure AJAX proxies in `user-bridge.php` with nonce
and sanitization.
- Documentation:
- Updated `AGENT.md` and `CLIENT_DELIVERY.md` to reflect phase 3.9
completion and updated the project roadmap.
- Removed obsolete `README-INSTALL.md`.
2639 lines
157 KiB
PHP
2639 lines
157 KiB
PHP
<?php
|
||
/**
|
||
* 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
|
||
// تفکیک بدهیها
|
||
$breakdown = $profile['pending_payments_breakdown'] ?? null;
|
||
$pending_total = $profile['pending_payments_total'] ?? 0;
|
||
$orders_debt = $breakdown['orders'] ?? $pending_total;
|
||
$invoices_debt = $breakdown['invoices'] ?? 0;
|
||
$invoices_count = $profile['pending_invoices_count'] ?? 0;
|
||
?>
|
||
|
||
<?php if ($pending_total > 0): ?>
|
||
<div style="font-size: 11px; color: #dc2626; margin-top: 8px; padding-top: 8px; border-top: 1px dashed #fecaca;">
|
||
⚠️ مجموع بدهی در انتظار پرداخت: <strong><?php echo number_format($pending_total); ?> ریال</strong>
|
||
</div>
|
||
<ul style="margin: 8px 0 0 0; padding-right: 20px; font-size: 11px; color: #666;">
|
||
<li>پرداختی سفارشات: <?php echo number_format($orders_debt); ?> ریال</li>
|
||
<li>
|
||
پرداختی فاکتورهای دستی
|
||
<?php if ($invoices_count > 0): ?>
|
||
(<?php echo (int)$invoices_count; ?> فاکتور)
|
||
<?php endif; ?>:
|
||
<?php echo number_format($invoices_debt); ?> ریال
|
||
<?php if ($invoices_count > 0): ?>
|
||
— <a href="<?php echo esc_url(home_url('/my-account/?tab=invoices')); ?>" style="color:#dc2626;font-weight:600;">مشاهده فاکتورها ←</a>
|
||
<?php endif; ?>
|
||
</li>
|
||
</ul>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<!-- بدهیهای چندارزی -->
|
||
<?php if (!empty($profile['foreign_credits'])): ?>
|
||
<div style="background: white; border-radius: 8px; padding: 15px; margin-bottom: 15px; border: 1px solid #fed7aa;">
|
||
<div style="font-size: 12px; color: #666; margin-bottom: 10px; font-weight: bold;">
|
||
🌍 بدهیهای چندارزی
|
||
</div>
|
||
<table style="width: 100%; font-size: 13px; border-collapse: collapse;">
|
||
<thead>
|
||
<tr style="text-align: right; color: #666; border-bottom: 1px solid #e5e7eb;">
|
||
<th style="padding: 4px 8px; font-weight: 600;">ارز</th>
|
||
<th style="padding: 4px 8px; font-weight: 600;">مبلغ کل</th>
|
||
<th style="padding: 4px 8px; font-weight: 600;">تسویهشده</th>
|
||
<th style="padding: 4px 8px; font-weight: 600;">مانده</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($profile['foreign_credits'] as $fc): ?>
|
||
<tr style="border-bottom: 1px solid #f3f4f6;">
|
||
<td style="padding: 6px 8px;">
|
||
<span style="background: #fef3c7; color: #b45309; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 700;">
|
||
<?php echo esc_html($fc['currency_code']); ?>
|
||
</span>
|
||
</td>
|
||
<td style="padding: 6px 8px; font-weight: 600;">
|
||
<?php echo number_format($fc['total_amount'], 2); ?>
|
||
</td>
|
||
<td style="padding: 6px 8px; color: #059669;">
|
||
<?php echo number_format($fc['total_settled'], 2); ?>
|
||
</td>
|
||
<td style="padding: 6px 8px; font-weight: 700; color: #dc2626;">
|
||
<?php echo number_format($fc['balance'], 2); ?>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<div style="font-size: 10px; color: #9ca3af; margin-top: 8px;">
|
||
⚠️ بدهیهای بالا به ارز اصلی ثبت شدهاند و با نرخ روز تسویه میشوند.
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- جزئیات -->
|
||
<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
|
||
// بخش فاکتورهای دستی — اگه هرگز آمار فاکتور داره
|
||
$inv_pending_count = $profile['stats']['pending_invoices_count'] ?? 0;
|
||
$inv_pending_total = $profile['stats']['pending_invoices_total'] ?? 0;
|
||
$inv_paid_count = $profile['stats']['paid_invoices_count'] ?? 0;
|
||
$inv_paid_total = $profile['stats']['paid_invoices_total'] ?? 0;
|
||
?>
|
||
<?php if ($inv_pending_count > 0 || $inv_paid_count > 0): ?>
|
||
<div class="ifnex-stats-cards" style="margin-top:12px;">
|
||
<div class="ifnex-stat-card ifnex-stat-warning">
|
||
<div class="ifnex-stat-value"><?php echo (int)$inv_pending_count; ?></div>
|
||
<div class="ifnex-stat-label">فاکتور در انتظار پرداخت</div>
|
||
<div style="font-size:11px;color:#92400e;margin-top:4px;">
|
||
<?php echo number_format($inv_pending_total); ?> ریال
|
||
</div>
|
||
</div>
|
||
<div class="ifnex-stat-card ifnex-stat-success">
|
||
<div class="ifnex-stat-value"><?php echo (int)$inv_paid_count; ?></div>
|
||
<div class="ifnex-stat-label">فاکتور پرداختشده</div>
|
||
<div style="font-size:11px;color:#065f46;margin-top:4px;">
|
||
<?php echo number_format($inv_paid_total); ?> ریال
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p style="font-size:12px;margin-top:8px;">
|
||
<a href="<?php echo esc_url(home_url('/my-account/?tab=invoices')); ?>" style="color:#0369a1;font-weight:600;">
|
||
📋 مشاهده همه فاکتورها ←
|
||
</a>
|
||
</p>
|
||
<?php endif; ?>
|
||
|
||
<?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>
|
||
|
||
<?php // دفترچه آدرس (فاز ۳.۹) — انتخاب از آدرسهای ذخیرهشده (با JS پر میشود) ?>
|
||
<div class="ifnex-saved-address-wrap" id="ifnex-sender-saved-wrap" style="display:none;">
|
||
<label for="ifnex-sender-saved">📒 انتخاب از آدرسهای ذخیرهشده</label>
|
||
<select id="ifnex-sender-saved">
|
||
<option value="">— انتخاب کنید —</option>
|
||
</select>
|
||
</div>
|
||
|
||
<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-state">استان / ایالت *</label><input type="text" id="ifnex-sender-state" name="ifnex_sender_state"></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"><label for="ifnex-sender-postal-code">کد پستی *</label><input type="text" id="ifnex-sender-postal-code" name="ifnex_sender_postal_code" pattern="[0-9]{5,15}" maxlength="15"></div>
|
||
<div class="ifnex-field"><label for="ifnex-sender-national-id">کد ملی / شماره گذرنامه *</label><input type="text" id="ifnex-sender-national-id" name="ifnex_sender_national_id"></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 class="ifnex-field full ifnex-save-address-row">
|
||
<label class="ifnex-save-address-label">
|
||
<input type="checkbox" id="ifnex-save-sender-address">
|
||
<span>💾 ذخیره این آدرس در دفترچه آدرس من</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<h3 style="margin-top:24px;">📮 اطلاعات گیرنده</h3>
|
||
|
||
<?php // دفترچه آدرس (فاز ۳.۹) ?>
|
||
<div class="ifnex-saved-address-wrap" id="ifnex-receiver-saved-wrap" style="display:none;">
|
||
<label for="ifnex-receiver-saved">📒 انتخاب از آدرسهای ذخیرهشده</label>
|
||
<select id="ifnex-receiver-saved">
|
||
<option value="">— انتخاب کنید —</option>
|
||
</select>
|
||
</div>
|
||
|
||
<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-state">استان / ایالت *</label><input type="text" id="ifnex-receiver-state" name="ifnex_receiver_state"></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"><label for="ifnex-receiver-postal-code">کد پستی *</label><input type="text" id="ifnex-receiver-postal-code" name="ifnex_receiver_postal_code" pattern="[0-9]{5,15}" maxlength="15"></div>
|
||
<div class="ifnex-field"><label for="ifnex-receiver-national-id">کد ملی / شماره گذرنامه *</label><input type="text" id="ifnex-receiver-national-id" name="ifnex_receiver_national_id"></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 class="ifnex-field full ifnex-save-address-row">
|
||
<label class="ifnex-save-address-label">
|
||
<input type="checkbox" id="ifnex-save-receiver-address">
|
||
<span>💾 ذخیره این آدرس در دفترچه آدرس من</span>
|
||
</label>
|
||
</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>
|
||
<p>🏙️ <?php echo esc_html(($data['sender']['state'] ?? '') . ' / ' . ($data['sender']['city'] ?? '')); ?></p>
|
||
<p>📮 <?php echo esc_html($data['sender']['postal_code'] ?? '—'); ?></p>
|
||
<p>🪪 <?php echo esc_html($data['sender']['national_id'] ?? '—'); ?></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>
|
||
<p>🏙️ <?php echo esc_html(($data['receiver']['state'] ?? '') . ' / ' . ($data['receiver']['city'] ?? '')); ?></p>
|
||
<p>📮 <?php echo esc_html($data['receiver']['postal_code'] ?? '—'); ?></p>
|
||
<p>🪪 <?php echo esc_html($data['receiver']['national_id'] ?? '—'); ?></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>
|
||
</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"/>',
|
||
'address' => '<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"/>',
|
||
'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"/>',
|
||
'invoice' => '<path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>',
|
||
];
|
||
|
||
$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', 'invoices', 'wallet', 'transactions', 'tracking', 'addresses', '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'];
|
||
}
|
||
|
||
// تعداد فاکتورهای در انتظار پرداخت برای badge
|
||
$pending_invoices_count = 0;
|
||
$invoices_result = $bridge->authenticated_request($user_id, '/customer/invoices?status=pending_payment&per_page=100', 'GET');
|
||
if (!is_wp_error($invoices_result) && isset($invoices_result['data']['invoices'])) {
|
||
$pending_invoices_count = count($invoices_result['data']['invoices']);
|
||
}
|
||
|
||
$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' => 'ثبت سفارش جدید'],
|
||
'invoices' => ['icon' => 'invoice', 'label' => 'فاکتورهای دستی'],
|
||
'wallet' => ['icon' => 'wallet', 'label' => 'کیف پول'],
|
||
'transactions' => ['icon' => 'chart', 'label' => 'تراکنشها'],
|
||
'tracking' => ['icon' => 'tracking', 'label' => 'رهگیری مرسوله'],
|
||
'addresses' => ['icon' => 'address', 'label' => 'دفترچه آدرس'],
|
||
'profile' => ['icon' => 'user', 'label' => 'پروفایل'],
|
||
];
|
||
|
||
$tab_titles = [
|
||
'dashboard' => 'داشبورد', 'notifications' => 'اعلانها', 'orders' => 'سفارشات من', 'new-order' => 'ثبت سفارش جدید',
|
||
'invoices' => 'فاکتورهای دستی',
|
||
'wallet' => 'کیف پول', 'transactions' => 'تراکنشها', 'tracking' => 'رهگیری مرسوله', 'addresses' => 'دفترچه آدرس', '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; ?>
|
||
<?php if ($key === 'invoices' && $pending_invoices_count > 0): ?>
|
||
<span class="ifnx-menu-badge"><?php echo (int)$pending_invoices_count; ?></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:#fff7ed; --clr:#ea580c;">
|
||
<div class="ifnx-stat-icon"><?php echo ifnex_icon('invoice', 22); ?></div>
|
||
<div class="ifnx-stat-info">
|
||
<span class="ifnx-stat-value"><?php echo (int)($stats['pending_invoices_count'] ?? 0); ?></span>
|
||
<span class="ifnx-stat-label">
|
||
فاکتور دستی در انتظار پرداخت
|
||
<?php if (($stats['pending_invoices_count'] ?? 0) > 0): ?>
|
||
<a href="<?php echo esc_url(home_url('/my-account/?tab=invoices')); ?>" style="font-size:10px;color:#ea580c;font-weight:600;display:block;margin-top:2px;">مشاهده ←</a>
|
||
<?php endif; ?>
|
||
</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 === 'invoices'): ?>
|
||
<?php
|
||
// نمایش لیست یا جزئیات فاکتور بر اساس پارامتر ?invoice=X
|
||
$single_invoice_id = isset($_GET['invoice']) ? intval($_GET['invoice']) : 0;
|
||
if ($single_invoice_id > 0) {
|
||
// دریافت جزئیات فاکتور از API
|
||
$single_result = $bridge->authenticated_request($user_id, "/customer/invoices/{$single_invoice_id}", 'GET');
|
||
if (is_wp_error($single_result)) {
|
||
echo '<div class="ifnex-alert ifnex-alert-danger">خطا در دریافت فاکتور: ' . esc_html($single_result->get_error_message()) . '</div>';
|
||
} else {
|
||
$inv = $single_result['data'] ?? null;
|
||
if ($inv):
|
||
$status_labels = [
|
||
'pending_payment' => 'در انتظار پرداخت',
|
||
'paid' => 'پرداخت شده',
|
||
'cancelled' => 'لغو شده',
|
||
'expired' => 'منقضی',
|
||
];
|
||
$status_class = $inv['status'] ?? '';
|
||
$status_label = $status_labels[$inv['status']] ?? $inv['status'];
|
||
$items = $inv['items'] ?? [];
|
||
$subtotal = (float)($inv['subtotal'] ?? 0);
|
||
$discount = (float)($inv['discount_amount'] ?? 0);
|
||
$total = (float)($inv['total_amount'] ?? 0);
|
||
?>
|
||
<style>
|
||
.ifnex-invoice-detail {background:#fff;border-radius:12px;padding:24px;border:1px solid #e2e8f0;}
|
||
.ifnex-invoice-detail h3 {margin:0 0 16px;color:#0f172a;font-size:20px;}
|
||
.ifnex-invoice-meta {display:flex;flex-wrap:wrap;gap:16px;margin-bottom:20px;padding:14px 16px;background:#f8fafc;border-radius:8px;}
|
||
.ifnex-invoice-meta div {font-size:13px;color:#475569;}
|
||
.ifnex-invoice-meta strong {color:#0f172a;font-weight:600;}
|
||
.ifnex-status-badge {padding:4px 12px;border-radius:999px;font-size:12px;font-weight:700;display:inline-block;}
|
||
.ifnex-status-pending_payment {background:#fef3c7;color:#92400e;}
|
||
.ifnex-status-paid {background:#ecfdf5;color:#065f46;}
|
||
.ifnex-status-cancelled {background:#fee2e2;color:#991b1b;}
|
||
.ifnex-status-expired {background:#f1f5f9;color:#475569;}
|
||
.ifnex-items-table {width:100%;border-collapse:collapse;margin:16px 0;}
|
||
.ifnex-items-table th, .ifnex-items-table td {padding:10px 12px;text-align:right;border-bottom:1px solid #e2e8f0;}
|
||
.ifnex-items-table th {background:#f1f5f9;font-weight:600;color:#334155;font-size:13px;}
|
||
.ifnex-items-table td {font-size:14px;color:#1e293b;}
|
||
.ifnex-items-table .num {text-align:left;font-variant-numeric:tabular-nums;}
|
||
.ifnex-totals {margin-top:16px;padding:14px 16px;background:#f8fafc;border-radius:8px;}
|
||
.ifnex-totals div {display:flex;justify-content:space-between;padding:6px 0;font-size:14px;}
|
||
.ifnex-totals .grand {font-size:18px;font-weight:700;color:#059669;border-top:2px solid #10b981;margin-top:8px;padding-top:10px;}
|
||
.ifnex-note {margin-top:16px;padding:12px 16px;background:#fef3c7;border-right:4px solid #f59e0b;border-radius:6px;font-size:13px;color:#78350f;}
|
||
.ifnex-invoice-actions {margin-top:20px;display:flex;gap:10px;}
|
||
.ifnex-pay-btn {background:#10b981;color:#fff;border:none;padding:12px 24px;border-radius:8px;font-size:15px;font-weight:700;cursor:pointer;}
|
||
.ifnex-pay-btn:hover {background:#059669;}
|
||
.ifnex-pay-btn:disabled {background:#9ca3af;cursor:not-allowed;}
|
||
.ifnex-back-btn {background:#e5e7eb;color:#1e293b;padding:12px 24px;border-radius:8px;text-decoration:none;font-size:14px;display:inline-flex;align-items:center;}
|
||
</style>
|
||
<div class="ifnex-invoice-detail">
|
||
<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:16px;">
|
||
<div>
|
||
<h3>فاکتور <?php echo esc_html($inv['invoice_number']); ?></h3>
|
||
<span class="ifnex-status-badge ifnex-status-<?php echo esc_attr($status_class); ?>"><?php echo esc_html($status_label); ?></span>
|
||
</div>
|
||
<a href="<?php echo esc_url(home_url('/my-account/?tab=invoices')); ?>" class="ifnex-back-btn">→ بازگشت به لیست</a>
|
||
</div>
|
||
|
||
<div class="ifnex-invoice-meta">
|
||
<div>صادرکننده: <strong><?php echo esc_html($inv['issued_by']['name'] ?? '—'); ?></strong></div>
|
||
<div>تاریخ صدور: <strong><?php echo esc_html(ifnex_format_jalali_date($inv['created_at'])); ?></strong></div>
|
||
<?php if (!empty($inv['due_date'])): ?>
|
||
<div>موعد پرداخت: <strong><?php echo esc_html($inv['due_date']); ?></strong></div>
|
||
<?php endif; ?>
|
||
<?php if (!empty($inv['paid_at'])): ?>
|
||
<div>تاریخ پرداخت: <strong><?php echo esc_html(ifnex_format_jalali_date($inv['paid_at'])); ?></strong></div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<?php if (!empty($items)): ?>
|
||
<table class="ifnex-items-table">
|
||
<thead>
|
||
<tr>
|
||
<th>#</th>
|
||
<th>شرح خدمت/کالا</th>
|
||
<th class="num">تعداد</th>
|
||
<th class="num">قیمت واحد (ریال)</th>
|
||
<th class="num">مجموع (ریال)</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($items as $i => $item): ?>
|
||
<?php
|
||
$qty = (float)($item['quantity'] ?? 0);
|
||
$unit = (float)($item['unit_price'] ?? 0);
|
||
$line_total = $qty * $unit;
|
||
?>
|
||
<tr>
|
||
<td><?php echo (int)$i + 1; ?></td>
|
||
<td><?php echo esc_html($item['description'] ?? ''); ?></td>
|
||
<td class="num"><?php echo number_format($qty); ?></td>
|
||
<td class="num"><?php echo number_format($unit); ?></td>
|
||
<td class="num"><?php echo number_format($line_total); ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<?php endif; ?>
|
||
|
||
<div class="ifnex-totals">
|
||
<div><span>مجموع اقلام:</span><strong><?php echo number_format($subtotal); ?> ریال</strong></div>
|
||
<?php if ($discount > 0): ?>
|
||
<div><span>تخفیف:</span><strong>- <?php echo number_format($discount); ?> ریال</strong></div>
|
||
<?php endif; ?>
|
||
<div class="grand"><span>مبلغ قابل پرداخت:</span><span><?php echo number_format($total); ?> ریال</span></div>
|
||
</div>
|
||
|
||
<?php if (!empty($inv['notes_to_customer'])): ?>
|
||
<div class="ifnex-note"><?php echo esc_html($inv['notes_to_customer']); ?></div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!empty($inv['can_pay'])): ?>
|
||
<div class="ifnex-invoice-actions">
|
||
<button type="button" class="ifnex-pay-btn" data-invoice-id="<?php echo (int)$inv['id']; ?>">💳 پرداخت از کیف پول</button>
|
||
</div>
|
||
<?php elseif ($inv['status'] === 'paid'): ?>
|
||
<div style="margin-top:20px;padding:14px 16px;background:#ecfdf5;border:1px solid #10b981;border-radius:8px;color:#065f46;font-weight:600;">
|
||
✅ این فاکتور پرداخت شده است.
|
||
</div>
|
||
<?php elseif ($inv['status'] === 'cancelled'): ?>
|
||
<div style="margin-top:20px;padding:14px 16px;background:#fee2e2;border:1px solid #ef4444;border-radius:8px;color:#991b1b;font-weight:600;">
|
||
❌ این فاکتور لغو شده است.
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<script>
|
||
jQuery(function($) {
|
||
$('.ifnex-pay-btn').on('click', function() {
|
||
var btn = $(this);
|
||
var invoiceId = btn.data('invoice-id');
|
||
if (!confirm('پرداخت این فاکتور از کیف پول شما انجام شود؟')) return;
|
||
btn.prop('disabled', true).text('در حال پرداخت...');
|
||
$.ajax({
|
||
url: ifnex_ajax.ajax_url,
|
||
method: 'POST',
|
||
data: { action: 'ifnex_pay_invoice', nonce: ifnex_ajax.nonce, invoice_id: invoiceId },
|
||
success: function(res) {
|
||
if (res.success) {
|
||
alert('✅ فاکتور با موفقیت پرداخت شد.');
|
||
location.reload();
|
||
} else {
|
||
alert('❌ ' + (res.data || 'خطا در پرداخت.'));
|
||
btn.prop('disabled', false).text('💳 پرداخت از کیف پول');
|
||
}
|
||
},
|
||
error: function() {
|
||
alert('❌ خطا در ارتباط با سرور.');
|
||
btn.prop('disabled', false).text('💳 پرداخت از کیف پول');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
<?php else: ?>
|
||
<div class="ifnex-alert ifnex-alert-warning">فاکتور یافت نشد.</div>
|
||
<?php endif; ?>
|
||
<?php } ?>
|
||
<?php } else { // نمایش لیست فاکتورها
|
||
// فیلتر از URL
|
||
$status_filter = isset($_GET['status']) ? sanitize_key($_GET['status']) : '';
|
||
$invoices_url = '/customer/invoices?per_page=50';
|
||
if (!empty($status_filter)) {
|
||
$invoices_url .= '&status=' . $status_filter;
|
||
}
|
||
$list_result = $bridge->authenticated_request($user_id, $invoices_url, 'GET');
|
||
$invoices = [];
|
||
if (!is_wp_error($list_result) && isset($list_result['data']['invoices'])) {
|
||
$invoices = $list_result['data']['invoices'];
|
||
}
|
||
$status_labels = [
|
||
'pending_payment' => 'در انتظار پرداخت',
|
||
'paid' => 'پرداخت شده',
|
||
'cancelled' => 'لغو شده',
|
||
'expired' => 'منقضی',
|
||
];
|
||
$status_classes = [
|
||
'pending_payment' => 'pending',
|
||
'paid' => 'paid',
|
||
'cancelled' => 'cancelled',
|
||
'expired' => 'expired',
|
||
];
|
||
?>
|
||
<style>
|
||
.ifnex-inv-filters {display:flex;gap:8px;margin-bottom:16px;flex-wrap:wrap;}
|
||
.ifnex-inv-filter-btn {padding:8px 16px;border-radius:8px;border:1px solid #e2e8f0;background:#fff;color:#475569;text-decoration:none;font-size:13px;font-weight:500;}
|
||
.ifnex-inv-filter-btn.active {background:#1e3a5f;color:#fff;border-color:#1e3a5f;}
|
||
.ifnex-inv-card {background:#fff;border-radius:12px;padding:18px;border:1px solid #e2e8f0;margin-bottom:12px;display:flex;justify-content:space-between;align-items:center;transition:box-shadow 0.2s;}
|
||
.ifnex-inv-card:hover {box-shadow:0 4px 12px rgba(0,0,0,0.08);}
|
||
.ifnex-inv-card-meta {font-size:12px;color:#64748b;margin-top:4px;}
|
||
.ifnex-inv-card-amount {font-size:18px;font-weight:700;color:#0f172a;}
|
||
.ifnex-status-badge {padding:4px 12px;border-radius:999px;font-size:11px;font-weight:700;display:inline-block;}
|
||
.ifnex-status-pending {background:#fef3c7;color:#92400e;}
|
||
.ifnex-status-paid {background:#ecfdf5;color:#065f46;}
|
||
.ifnex-status-cancelled {background:#fee2e2;color:#991b1b;}
|
||
.ifnex-status-expired {background:#f1f5f9;color:#475569;}
|
||
.ifnex-empty {text-align:center;padding:40px 20px;color:#64748b;}
|
||
.ifnex-empty-icon {font-size:48px;margin-bottom:12px;}
|
||
.ifnex-inv-actions {display:flex;gap:8px;}
|
||
.ifnex-inv-btn {padding:8px 16px;border-radius:6px;text-decoration:none;font-size:13px;font-weight:600;}
|
||
.ifnex-inv-btn-view {background:#e5e7eb;color:#1e293b;}
|
||
.ifnex-inv-btn-pay {background:#10b981;color:#fff;border:none;cursor:pointer;}
|
||
</style>
|
||
<div class="ifnex-inv-filters">
|
||
<a href="<?php echo esc_url(home_url('/my-account/?tab=invoices')); ?>" class="ifnex-inv-filter-btn <?php echo empty($status_filter) ? 'active' : ''; ?>">همه</a>
|
||
<a href="<?php echo esc_url(home_url('/my-account/?tab=invoices&status=pending_payment')); ?>" class="ifnex-inv-filter-btn <?php echo $status_filter === 'pending_payment' ? 'active' : ''; ?>">در انتظار پرداخت</a>
|
||
<a href="<?php echo esc_url(home_url('/my-account/?tab=invoices&status=paid')); ?>" class="ifnex-inv-filter-btn <?php echo $status_filter === 'paid' ? 'active' : ''; ?>">پرداخت شده</a>
|
||
<a href="<?php echo esc_url(home_url('/my-account/?tab=invoices&status=cancelled')); ?>" class="ifnex-inv-filter-btn <?php echo $status_filter === 'cancelled' ? 'active' : ''; ?>">لغو شده</a>
|
||
</div>
|
||
<?php if (empty($invoices)): ?>
|
||
<div class="ifnex-empty">
|
||
<div class="ifnex-empty-icon">📄</div>
|
||
<p>فاکتوری برای نمایش وجود ندارد.</p>
|
||
</div>
|
||
<?php else: ?>
|
||
<?php foreach ($invoices as $inv):
|
||
$inv_status = $inv['status'] ?? 'pending_payment';
|
||
$status_label = $status_labels[$inv_status] ?? $inv_status;
|
||
$status_class = $status_classes[$inv_status] ?? '';
|
||
$items_count = is_array($inv['items'] ?? null) ? count($inv['items']) : 0;
|
||
$detail_url = home_url('/my-account/?tab=invoices&invoice=' . $inv['id']);
|
||
$can_pay = !empty($inv['can_pay']);
|
||
?>
|
||
<div class="ifnex-inv-card">
|
||
<div>
|
||
<div style="font-weight:700;color:#0f172a;font-size:15px;"><?php echo esc_html($inv['invoice_number']); ?></div>
|
||
<div class="ifnex-inv-card-meta">
|
||
<?php echo (int)$items_count; ?> قلم | <?php echo esc_html(ifnex_format_jalali_date($inv['created_at'])); ?>
|
||
<?php if (!empty($inv['due_date'])): ?> | موعد: <?php echo esc_html($inv['due_date']); ?><?php endif; ?>
|
||
</div>
|
||
<div style="margin-top:8px;">
|
||
<span class="ifnex-status-badge ifnex-status-<?php echo esc_attr($status_class); ?>"><?php echo esc_html($status_label); ?></span>
|
||
</div>
|
||
</div>
|
||
<div style="text-align:left;">
|
||
<div class="ifnex-inv-card-amount"><?php echo number_format((float)$inv['total_amount']); ?> <small style="font-size:11px;color:#64748b;font-weight:normal;">ریال</small></div>
|
||
<div class="ifnex-inv-actions" style="margin-top:8px;">
|
||
<a href="<?php echo esc_url($detail_url); ?>" class="ifnex-inv-btn ifnex-inv-btn-view">مشاهده</a>
|
||
<?php if ($can_pay): ?>
|
||
<button type="button" class="ifnex-inv-btn ifnex-inv-btn-pay ifnex-btn-pay-list" data-invoice-id="<?php echo (int)$inv['id']; ?>">پرداخت</button>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
<script>
|
||
jQuery(function($) {
|
||
$('.ifnex-btn-pay-list').on('click', function() {
|
||
var btn = $(this);
|
||
var invoiceId = btn.data('invoice-id');
|
||
if (!confirm('پرداخت این فاکتور از کیف پول شما انجام شود؟')) return;
|
||
btn.prop('disabled', true).text('...');
|
||
$.ajax({
|
||
url: ifnex_ajax.ajax_url,
|
||
method: 'POST',
|
||
data: { action: 'ifnex_pay_invoice', nonce: ifnex_ajax.nonce, invoice_id: invoiceId },
|
||
success: function(res) {
|
||
if (res.success) {
|
||
alert('✅ فاکتور با موفقیت پرداخت شد.');
|
||
location.reload();
|
||
} else {
|
||
alert('❌ ' + (res.data || 'خطا در پرداخت.'));
|
||
btn.prop('disabled', false).text('پرداخت');
|
||
}
|
||
},
|
||
error: function() {
|
||
alert('❌ خطا در ارتباط با سرور.');
|
||
btn.prop('disabled', false).text('پرداخت');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
<?php } ?>
|
||
<?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 if (($profile['pending_payments_total'] ?? 0) > 0): ?>
|
||
<div class="ifnx-card" style="margin-top: 20px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 18px;">
|
||
<div style="font-size: 14px; font-weight: bold; color: #dc2626; margin-bottom: 8px;">
|
||
⚠️ بدهی در انتظار پرداخت
|
||
</div>
|
||
<div style="font-size: 22px; font-weight: bold; color: #dc2626;">
|
||
<?php echo number_format($profile['pending_payments_total']); ?> <small>ریال</small>
|
||
</div>
|
||
<div style="font-size: 12px; color: #991b1b; margin-top: 6px;">
|
||
<?php echo number_format($stats['pending_payment'] ?? 0); ?> سفارش تأییدشده در انتظار پرداخت
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- ════════ بدهیهای چندارزی (EUR/USD/AED/CNY) ════════ -->
|
||
<?php if (!empty($profile['foreign_credits'])): ?>
|
||
<div class="ifnx-card" style="margin-top: 20px; background: #fffbeb; border: 1px solid #fed7aa; border-radius: 12px; padding: 18px;">
|
||
<div style="font-size: 14px; font-weight: bold; color: #92400e; margin-bottom: 12px;">
|
||
🌍 بدهیهای چندارزی
|
||
</div>
|
||
<table style="width: 100%; border-collapse: collapse; font-size: 13px;">
|
||
<thead>
|
||
<tr style="border-bottom: 2px solid #fde68a; color: #92400e;">
|
||
<th style="padding: 8px 10px; text-align: right;">ارز</th>
|
||
<th style="padding: 8px 10px; text-align: right;">مبلغ کل</th>
|
||
<th style="padding: 8px 10px; text-align: right;">تسویهشده</th>
|
||
<th style="padding: 8px 10px; text-align: right;">مانده بدهی</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($profile['foreign_credits'] as $fc): ?>
|
||
<tr style="border-bottom: 1px solid #fef3c7;">
|
||
<td style="padding: 8px 10px;">
|
||
<span style="background: #fef3c7; color: #92400e; padding: 3px 10px; border-radius: 12px; font-size: 12px; font-weight: 700;">
|
||
<?php echo esc_html($fc['currency_code']); ?>
|
||
</span>
|
||
</td>
|
||
<td style="padding: 8px 10px; font-weight: 600; color: #1f2937;">
|
||
<?php echo number_format($fc['total_amount'], 2); ?>
|
||
</td>
|
||
<td style="padding: 8px 10px; color: #059669; font-weight: 600;">
|
||
<?php echo number_format($fc['total_settled'], 2); ?>
|
||
</td>
|
||
<td style="padding: 8px 10px; color: #dc2626; font-weight: 700; font-size: 15px;">
|
||
<?php echo number_format($fc['balance'], 2); ?>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<div style="font-size: 11px; color: #a16207; margin-top: 10px; line-height: 1.5;">
|
||
ℹ️ این بدهیها به ارز اصلی ثبت شدهاند و با نرخ روز تسویه میشوند.
|
||
برای تسویه با مدیر تماس بگیرید.
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- ════════ مانده حساب ════════ -->
|
||
<div class="ifnx-card" style="margin-top: 20px; background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 12px; padding: 18px;">
|
||
<div style="font-size: 12px; color: #15803d; margin-bottom: 6px;">مانده حساب (موجودی منهای بدهیهای ریالی)</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); ?> <small>ریال</small>
|
||
</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 = match($n_type) {
|
||
'order_approved' => 'check',
|
||
'order_needs_changes' => 'clock',
|
||
'manual_invoice_created' => 'invoice',
|
||
default => 'bell',
|
||
};
|
||
$invoice_no = $data['invoice_number'] ?? '';
|
||
// اگه action_url برای فاکتور نیست، خودکار به تب فاکتورها هدایت کن
|
||
if ($n_type === 'manual_invoice_created' && empty($action)) {
|
||
$action = '/my-account/?tab=invoices';
|
||
}
|
||
?>
|
||
<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 === 'addresses'): ?>
|
||
<?php
|
||
// ═══ دفترچه آدرس (فاز ۳.۹) ═══
|
||
$addresses = [];
|
||
$addresses_error = '';
|
||
$addresses_result = $bridge->authenticated_request($user_id, '/addresses', 'GET');
|
||
if (is_wp_error($addresses_result)) {
|
||
$addresses_error = $addresses_result->get_error_message();
|
||
} elseif (isset($addresses_result['data'])) {
|
||
$addresses = $addresses_result['data'];
|
||
}
|
||
|
||
$addr_filter = in_array($_GET['addr_role'] ?? '', ['sender', 'receiver'], true) ? $_GET['addr_role'] : '';
|
||
if ($addr_filter) {
|
||
$addresses = array_values(array_filter($addresses, function ($a) use ($addr_filter) {
|
||
return ($a['role'] ?? '') === $addr_filter;
|
||
}));
|
||
}
|
||
?>
|
||
<?php if ($addresses_error): ?>
|
||
<div class="ifnx-notice ifnx-notice-error">خطا در دریافت دفترچه آدرس: <?php echo esc_html($addresses_error); ?></div>
|
||
<?php else: ?>
|
||
<div class="ifnx-addresses" id="ifnx-addresses-app"
|
||
data-base-url="<?php echo esc_url($base_url); ?>"
|
||
data-current-tab="addresses">
|
||
<input type="hidden" id="ifnx-address-nonce" value="<?php echo esc_attr(wp_create_nonce('ifnex_ajax_nonce')); ?>">
|
||
<input type="hidden" id="ifnx-address-ajax-url" value="<?php echo esc_attr(admin_url('admin-ajax.php')); ?>">
|
||
|
||
<div class="ifnx-address-toolbar">
|
||
<div class="ifnx-address-filters">
|
||
<a href="<?php echo esc_url(add_query_arg(['tab' => 'addresses', 'addr_role' => ''], $base_url)); ?>" class="ifnx-chip <?php echo $addr_filter === '' ? 'active' : ''; ?>">همه</a>
|
||
<a href="<?php echo esc_url(add_query_arg(['tab' => 'addresses', 'addr_role' => 'sender'], $base_url)); ?>" class="ifnx-chip <?php echo $addr_filter === 'sender' ? 'active' : ''; ?>">👤 فرستنده</a>
|
||
<a href="<?php echo esc_url(add_query_arg(['tab' => 'addresses', 'addr_role' => 'receiver'], $base_url)); ?>" class="ifnx-chip <?php echo $addr_filter === 'receiver' ? 'active' : ''; ?>">📮 گیرنده</a>
|
||
</div>
|
||
<button type="button" id="ifnx-add-address-btn" class="ifnex-btn ifnex-btn-primary">+ افزودن آدرس جدید</button>
|
||
</div>
|
||
|
||
<!-- فرم افزودن / ویرایش (با JS نمایش داده میشود) -->
|
||
<div id="ifnx-address-form-wrap" style="display:none;">
|
||
<form id="ifnx-address-form" class="ifnx-address-form" autocomplete="off">
|
||
<input type="hidden" name="address_id" id="ifnx-addr-id" value="">
|
||
<div class="ifnx-address-form-grid">
|
||
<div class="ifnx-field">
|
||
<label for="ifnx-addr-role">نقش آدرس *</label>
|
||
<select name="role" id="ifnx-addr-role">
|
||
<option value="receiver">📮 گیرنده</option>
|
||
<option value="sender">👤 فرستنده</option>
|
||
</select>
|
||
</div>
|
||
<div class="ifnx-field">
|
||
<label for="ifnx-addr-label">برچسب (مثلاً خانه / دفتر)</label>
|
||
<input type="text" name="label" id="ifnx-addr-label" maxlength="100">
|
||
</div>
|
||
<div class="ifnx-field">
|
||
<label for="ifnx-addr-name">نام و نام خانوادگی *</label>
|
||
<input type="text" name="name" id="ifnx-addr-name" required>
|
||
</div>
|
||
<div class="ifnx-field">
|
||
<label for="ifnx-addr-phone">شماره تماس *</label>
|
||
<input type="tel" name="phone" id="ifnx-addr-phone" required>
|
||
</div>
|
||
<div class="ifnx-field">
|
||
<label for="ifnx-addr-state">استان / ایالت</label>
|
||
<input type="text" name="state" id="ifnx-addr-state">
|
||
</div>
|
||
<div class="ifnx-field">
|
||
<label for="ifnx-addr-city">شهر</label>
|
||
<input type="text" name="city" id="ifnx-addr-city">
|
||
</div>
|
||
<div class="ifnx-field">
|
||
<label for="ifnx-addr-postal">کد پستی</label>
|
||
<input type="text" name="postal_code" id="ifnx-addr-postal" maxlength="15">
|
||
</div>
|
||
<div class="ifnx-field">
|
||
<label for="ifnx-addr-national">کد ملی / شماره گذرنامه</label>
|
||
<input type="text" name="national_id" id="ifnx-addr-national" maxlength="50">
|
||
</div>
|
||
<div class="ifnx-field full">
|
||
<label for="ifnx-addr-address">آدرس کامل *</label>
|
||
<textarea name="address" id="ifnx-addr-address" rows="2" required></textarea>
|
||
</div>
|
||
<div class="ifnx-field full">
|
||
<label class="ifnx-addr-checkbox">
|
||
<input type="checkbox" name="is_default" id="ifnx-addr-default">
|
||
<span>⭐ تنظیم بهعنوان آدرس پیشفرض این نقش</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="ifnx-address-form-actions">
|
||
<button type="submit" id="ifnx-address-submit" class="ifnex-btn ifnex-btn-primary">💾 ذخیره آدرس</button>
|
||
<button type="button" id="ifnx-address-cancel" class="ifnex-btn">انصراف</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<?php if (empty($addresses)): ?>
|
||
<div class="ifnx-address-empty">
|
||
<div class="ifnx-address-empty-icon">📒</div>
|
||
<h3>دفترچه آدرس شما خالی است</h3>
|
||
<p>در فرم ثبت سفارش، چکباکس «ذخیره این آدرس» را بزنید یا همینجا آدرس جدید اضافه کنید تا دفعه بعد با یک کلیک پر شود.</p>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="ifnx-address-grid">
|
||
<?php foreach ($addresses as $addr): ?>
|
||
<?php
|
||
$is_sender = ($addr['role'] ?? '') === 'sender';
|
||
$role_label = $is_sender ? '👤 فرستنده' : '📮 گیرنده';
|
||
$title = $addr['display_label'] ?? ($addr['name'] ?? '');
|
||
?>
|
||
<div class="ifnx-address-card <?php echo !empty($addr['is_default']) ? 'is-default' : ''; ?>">
|
||
<div class="ifnx-address-card-head">
|
||
<span class="ifnx-address-role <?php echo $is_sender ? 'role-sender' : 'role-receiver'; ?>"><?php echo esc_html($role_label); ?></span>
|
||
<?php if (!empty($addr['is_default'])): ?>
|
||
<span class="ifnx-address-default-badge">⭐ پیشفرض</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="ifnx-address-card-title"><?php echo esc_html($title); ?></div>
|
||
<div class="ifnx-address-card-meta">
|
||
<?php if (!empty($addr['phone'])): ?><span>📞 <?php echo esc_html($addr['phone']); ?></span><?php endif; ?>
|
||
<?php if (!empty($addr['state']) || !empty($addr['city'])): ?><span>📍 <?php echo esc_html(trim(($addr['state'] ?? '') . '، ' . ($addr['city'] ?? ''), '، ')); ?></span><?php endif; ?>
|
||
<?php if (!empty($addr['postal_code'])): ?><span>🏷️ <?php echo esc_html($addr['postal_code']); ?></span><?php endif; ?>
|
||
</div>
|
||
<div class="ifnx-address-card-text"><?php echo esc_html($addr['address'] ?? ''); ?></div>
|
||
<div class="ifnx-address-card-actions"
|
||
data-id="<?php echo (int)($addr['id'] ?? 0); ?>"
|
||
data-role="<?php echo esc_attr($addr['role'] ?? 'receiver'); ?>"
|
||
data-label="<?php echo esc_attr($addr['label'] ?? ''); ?>"
|
||
data-name="<?php echo esc_attr($addr['name'] ?? ''); ?>"
|
||
data-phone="<?php echo esc_attr($addr['phone'] ?? ''); ?>"
|
||
data-state="<?php echo esc_attr($addr['state'] ?? ''); ?>"
|
||
data-city="<?php echo esc_attr($addr['city'] ?? ''); ?>"
|
||
data-postal="<?php echo esc_attr($addr['postal_code'] ?? ''); ?>"
|
||
data-national="<?php echo esc_attr($addr['national_id'] ?? ''); ?>"
|
||
data-address="<?php echo esc_attr($addr['address'] ?? ''); ?>"
|
||
data-default="<?php echo !empty($addr['is_default']) ? '1' : '0'; ?>">
|
||
<button type="button" class="ifnex-btn ifnex-btn-sm ifnex-addr-edit">✏️ ویرایش</button>
|
||
<button type="button" class="ifnex-btn ifnex-btn-sm ifnex-btn-info ifnx-addr-default" <?php echo !empty($addr['is_default']) ? 'disabled' : ''; ?>>⭐ پیشفرض</button>
|
||
<button type="button" class="ifnex-btn ifnex-btn-sm ifnex-btn-danger ifnx-addr-delete">🗑 حذف</button>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php elseif ($tab === 'profile'): ?>
|
||
<?php echo do_shortcode('[ifnex_user_profile]'); ?>
|
||
<?php endif; ?>
|
||
</main>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════
|
||
// فاکتورهای دستی (Manual Invoices)
|
||
// ═══════════════════════════════════════════════════════════════════
|
||
|
||
/**
|
||
* شورتکد لیست فاکتورهای دستی مشتری.
|
||
* استفاده: [ifnex_invoices]
|
||
*/
|
||
add_shortcode('ifnex_invoices', 'ifnex_invoices_list_shortcode');
|
||
function ifnex_invoices_list_shortcode($atts) {
|
||
if (!is_user_logged_in()) {
|
||
return '<div class="ifnex-alert ifnex-alert-warning">برای مشاهده فاکتورها باید وارد شوید.</div>';
|
||
}
|
||
|
||
$bridge = new IFNEX_User_Bridge();
|
||
$user_id = get_current_user_id();
|
||
|
||
// فراخوانی API برای دریافت فاکتورها
|
||
$result = $bridge->authenticated_request($user_id, '/customer/invoices', 'GET');
|
||
|
||
if (is_wp_error($result)) {
|
||
return '<div class="ifnex-alert ifnex-alert-danger">خطا در دریافت فاکتورها: ' . esc_html($result->get_error_message()) . '</div>';
|
||
}
|
||
|
||
$invoices = $result['data']['invoices'] ?? [];
|
||
$pagination = $result['data']['pagination'] ?? null;
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="ifnex-invoices-wrap">
|
||
<h2 class="ifnex-section-title">فاکتورهای دستی</h2>
|
||
|
||
<?php if (empty($invoices)): ?>
|
||
<div class="ifnex-empty-state">
|
||
<div class="ifnex-empty-icon">📄</div>
|
||
<p>فاکتور دستی برای شما ثبت نشده است.</p>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="ifnex-invoices-list">
|
||
<?php foreach ($invoices as $inv): ?>
|
||
<?php
|
||
$status_label = [
|
||
'pending_payment' => 'در انتظار پرداخت',
|
||
'paid' => 'پرداخت شده',
|
||
'cancelled' => 'لغو شده',
|
||
'expired' => 'منقضی',
|
||
][$inv['status']] ?? $inv['status'];
|
||
$status_class = [
|
||
'pending_payment' => 'pending',
|
||
'paid' => 'paid',
|
||
'cancelled' => 'cancelled',
|
||
'expired' => 'expired',
|
||
][$inv['status']] ?? '';
|
||
$items_count = is_array($inv['items'] ?? null) ? count($inv['items']) : 0;
|
||
?>
|
||
<div class="ifnex-invoice-card ifnex-status-<?php echo esc_attr($status_class); ?>">
|
||
<div class="ifnex-invoice-header">
|
||
<div>
|
||
<div class="ifnex-invoice-number"><?php echo esc_html($inv['invoice_number']); ?></div>
|
||
<div class="ifnex-invoice-date">صدور: <?php echo esc_html(ifnex_format_jalali_date($inv['created_at'])); ?></div>
|
||
</div>
|
||
<span class="ifnex-invoice-status ifnex-status-<?php echo esc_attr($status_class); ?>"><?php echo esc_html($status_label); ?></span>
|
||
</div>
|
||
<div class="ifnex-invoice-body">
|
||
<div class="ifnex-invoice-row">
|
||
<span>تعداد اقلام:</span>
|
||
<strong><?php echo (int)$items_count; ?> قلم</strong>
|
||
</div>
|
||
<div class="ifnex-invoice-row">
|
||
<span>مبلغ نهایی:</span>
|
||
<strong class="ifnex-amount"><?php echo number_format((float)$inv['total_amount']); ?> ریال</strong>
|
||
</div>
|
||
<?php if (!empty($inv['due_date'])): ?>
|
||
<div class="ifnex-invoice-row">
|
||
<span>موعد پرداخت:</span>
|
||
<strong><?php echo esc_html($inv['due_date']); ?></strong>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php if (!empty($inv['notes_to_customer'])): ?>
|
||
<div class="ifnex-invoice-note"><?php echo esc_html($inv['notes_to_customer']); ?></div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="ifnex-invoice-actions">
|
||
<a href="<?php echo esc_url(home_url('/my-account/?tab=invoices&invoice=' . $inv['id'])); ?>" class="ifnex-btn ifnex-btn-sm">جزئیات</a>
|
||
<?php if (!empty($inv['can_pay'])): ?>
|
||
<button type="button" class="ifnex-btn ifnex-btn-sm ifnex-btn-pay" data-invoice-id="<?php echo (int)$inv['id']; ?>">پرداخت از کیف پول</button>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<script>
|
||
jQuery(function($) {
|
||
$(document).on('click', '.ifnex-btn-pay', function() {
|
||
var btn = $(this);
|
||
var invoiceId = btn.data('invoice-id');
|
||
if (!confirm('پرداخت این فاکتور از کیف پول شما انجام شود؟')) return;
|
||
btn.prop('disabled', true).text('در حال پرداخت...');
|
||
|
||
$.ajax({
|
||
url: ifnex_ajax.ajax_url,
|
||
method: 'POST',
|
||
data: {
|
||
action: 'ifnex_pay_invoice',
|
||
nonce: ifnex_ajax.nonce,
|
||
invoice_id: invoiceId
|
||
},
|
||
success: function(res) {
|
||
if (res.success) {
|
||
alert('✅ فاکتور با موفقیت پرداخت شد.');
|
||
location.reload();
|
||
} else {
|
||
alert('❌ ' + (res.data || 'خطا در پرداخت فاکتور.'));
|
||
btn.prop('disabled', false).text('پرداخت از کیف پول');
|
||
}
|
||
},
|
||
error: function() {
|
||
alert('❌ خطا در ارتباط با سرور.');
|
||
btn.prop('disabled', false).text('پرداخت از کیف پول');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
/**
|
||
* فرمت تاریخ شمسی (helper).
|
||
*/
|
||
if (!function_exists('ifnex_format_jalali_date')) {
|
||
function ifnex_format_jalali_date($iso_date) {
|
||
if (empty($iso_date)) return '';
|
||
try {
|
||
$ts = strtotime($iso_date);
|
||
return jdate('Y/m/d H:i', $ts);
|
||
} catch (\Throwable $e) {
|
||
return $iso_date;
|
||
}
|
||
}
|
||
}
|