ifnex/03_WordPress/wp-content/plugins/ifnex-bridge/includes/shortcodes.php
Kazem Alghasi 4ee1d27d44 feat: Complete customer ordering system (Phase 3)
Laravel Backend:
- Add AuthController for Sanctum token login/logout
- Add CustomerOrderController with 6 endpoints
  (profile, countries, orders CRUD, cancel)
- Extend ShipmentStatus enum with pending_payment/cancelled
- Add SampleShippingRatesSeeder (10 zones, 3 service types)
- Zone-assign 232 countries (Gulf=1, Asia=2, EU=3, East=4, US=5)
- Add migration to extend shipments.status enum
- Add test-api.php automated test suite (5 tests)

WordPress Frontend:
- Add [ifnex_order_form] multi-step wizard (4 steps)
- Add [ifnex_orders_list] with status filter & cancel action
- Add [ifnex_user_profile] with wallet & stats
- Add ifnex-orders.css (RTL-ready, IFNEX theme)
- Add ifnex-order-form.js (step navigation + AJAX)
- Add AJAX handlers (countries, calculate, submit, cancel)
- Fix plugin asset paths with dirname(__FILE__)
2026-08-10 06:09:56 +03:30

607 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

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

<?php
/**
* 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);
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']); ?>" 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">
<h3>💰 کیف پول</h3>
<div class="ifnex-wallet-balance">
<span class="ifnex-balance-amount">
<?php echo number_format($profile['wallet']['balance']); ?> ریال
</span>
<?php if ($profile['wallet']['is_frozen']): ?>
<span class="ifnex-badge ifnex-badge-danger">مسدود</span>
<?php else: ?>
<span class="ifnex-badge ifnex-badge-success">فعال</span>
<?php endif; ?>
</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_orders']); ?></div>
<div class="ifnex-stat-label">در انتظار پرداخت</div>
</div>
<div class="ifnex-stat-card ifnex-stat-info">
<div class="ifnex-stat-value"><?php echo number_format($profile['stats']['active_orders']); ?></div>
<div class="ifnex-stat-label">در حال پردازش</div>
</div>
<div class="ifnex-stat-card ifnex-stat-success">
<div class="ifnex-stat-value"><?php echo number_format($profile['stats']['delivered_orders']); ?></div>
<div class="ifnex-stat-label">تحویل شده</div>
</div>
</div>
<?php if ($profile['stats']['total_spent'] > 0): ?>
<p class="ifnex-total-spent">
<strong>مجموع خرید:</strong>
<?php echo number_format($profile['stats']['total_spent']); ?> ریال
</p>
<?php endif; ?>
</div>
<?php endif; ?>
<!-- لینک‌های سریع -->
<div class="ifnex-quick-links">
<a href="<?php echo esc_url(home_url('/my-orders/')); ?>" class="ifnex-btn ifnex-btn-primary">
📦 مشاهده سفارشات
</a>
<a href="<?php echo esc_url(home_url('/new-order/')); ?>" class="ifnex-btn ifnex-btn-success">
ثبت سفارش جدید
</a>
</div>
</div>
<?php
return ob_get_clean();
}
// ══════════════════════════════════════════════════════════════
// شورت‌کد لیست سفارشات مشتری
// ══════════════════════════════════════════════════════════════
add_shortcode('ifnex_orders_list', 'ifnex_orders_list_shortcode');
function ifnex_orders_list_shortcode($atts) {
if (!is_user_logged_in()) {
return '<p class="ifnex-error">برای مشاهده سفارشات، باید وارد شوید.</p>';
}
$atts = shortcode_atts(array(
'per_page' => 10,
'status' => '',
), $atts);
$bridge = new IFNEX_User_Bridge();
$user_id = get_current_user_id();
$result = $bridge->get_customer_orders($user_id, intval($atts['per_page']), $atts['status']);
if (is_wp_error($result)) {
return '<p class="ifnex-error">' . esc_html($result->get_error_message()) . '</p>';
}
$orders = $result['data'] ?? [];
$pagination = $result['pagination'] ?? [];
ob_start();
?>
<div class="ifnex-orders-list">
<div class="ifnex-orders-header">
<h2>📦 سفارشات من</h2>
<a href="<?php echo esc_url(home_url('/new-order/')); ?>" class="ifnex-btn ifnex-btn-success">
ثبت سفارش جدید
</a>
</div>
<!-- فیلتر وضعیت -->
<div class="ifnex-filter-bar">
<a href="?status=" class="<?php echo empty($atts['status']) ? 'active' : ''; ?>">همه</a>
<a href="?status=pending_payment" class="<?php echo $atts['status'] === 'pending_payment' ? 'active' : ''; ?>">در انتظار پرداخت</a>
<a href="?status=processed" class="<?php echo $atts['status'] === 'processed' ? 'active' : ''; ?>">پردازش شده</a>
<a href="?status=in_transit" class="<?php echo $atts['status'] === 'in_transit' ? 'active' : ''; ?>">در حال حمل</a>
<a href="?status=delivered" class="<?php echo $atts['status'] === 'delivered' ? 'active' : ''; ?>">تحویل شده</a>
</div>
<?php if (empty($orders)): ?>
<div class="ifnex-empty-state">
<p>📭 هنوز سفارشی ثبت نکرده‌اید.</p>
<a href="<?php echo esc_url(home_url('/new-order/')); ?>" class="ifnex-btn ifnex-btn-primary">
اولین سفارش خود را ثبت کنید
</a>
</div>
<?php else: ?>
<div class="ifnex-orders-grid">
<?php foreach ($orders as $order): ?>
<div class="ifnex-order-card">
<div class="ifnex-order-header">
<div class="ifnex-awb">
<strong>AWB:</strong>
<code><?php echo esc_html($order['awb_no']); ?></code>
</div>
<span class="ifnex-badge ifnex-badge-<?php echo esc_attr($order['status']['color']); ?>">
<?php echo esc_html($order['status']['label']); ?>
</span>
</div>
<div class="ifnex-order-route">
<div class="ifnex-route-from">
<span class="ifnex-label">از:</span>
<span class="ifnex-country">
<?php echo esc_html($order['from_country']['name'] ?? '—'); ?>
</span>
</div>
<div class="ifnex-route-arrow">➜</div>
<div class="ifnex-route-to">
<span class="ifnex-label">به:</span>
<span class="ifnex-country">
<?php echo esc_html($order['to_country']['name'] ?? '—'); ?>
</span>
</div>
</div>
<div class="ifnex-order-details">
<div class="ifnex-detail-item">
<span class="ifnex-label">وزن:</span>
<span><?php echo esc_html($order['weight']); ?> kg</span>
</div>
<div class="ifnex-detail-item">
<span class="ifnex-label">مبلغ:</span>
<span class="ifnex-price">
<?php echo number_format($order['total_fee']); ?> ریال
</span>
</div>
<div class="ifnex-detail-item">
<span class="ifnex-label">تاریخ:</span>
<span><?php echo esc_html($order['created_at_jalali']); ?></span>
</div>
</div>
<div class="ifnex-order-actions">
<a href="<?php echo esc_url(home_url('/order-detail/' . $order['id'] . '/')); ?>"
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']['is_paid']): ?>
<a href="<?php echo esc_url(home_url('/payment/' . $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('❌ لغو سفارش');
}
});
});
});
</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" data-step="3"><span>۳</span><br>بررسی قیمت</div>
<div class="ifnex-step" data-step="4"><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>جهت ارسال *</label>
<select id="ifnex-direction">
<option value="export">صادرات (از ایران به خارج)</option>
<option value="import">واردات (از خارج به ایران)</option>
</select>
</div>
<div class="ifnex-field">
<label>نوع سرویس *</label>
<select id="ifnex-type">
<option value="PARCEL">بسته (Parcel)</option>
<option value="DOC_NORMAL">مدارک عادی</option>
<option value="DOC_ECONOMY">مدارک اقتصادی</option>
</select>
</div>
<div class="ifnex-field">
<label>کشور مبدأ *</label>
<select id="ifnex-from-country"></select>
</div>
<div class="ifnex-field">
<label>کشور مقصد *</label>
<select id="ifnex-to-country"></select>
</div>
<div class="ifnex-field">
<label>وزن واقعی (kg) *</label>
<input type="number" id="ifnex-weight" step="0.1" min="0.1" placeholder="مثلاً 2.5">
</div>
<div class="ifnex-field">
<label>وزن حجمی (kg)</label>
<input type="number" id="ifnex-volumetric-weight" step="0.1" min="0" placeholder="اختیاری">
</div>
</div>
<div class="ifnex-form-nav">
<span></span>
<button class="ifnex-btn ifnex-btn-primary ifnex-next-step">ادامه ←</button>
</div>
</div>
</div>
<!-- Step 2: Sender & Receiver -->
<div class="ifnex-form-step" data-step="2">
<div class="ifnex-form-card">
<h3>👤 اطلاعات فرستنده</h3>
<div class="ifnex-form-grid">
<div class="ifnex-field"><label>نام و نام خانوادگی *</label><input type="text" id="ifnex-sender-name"></div>
<div class="ifnex-field"><label>شماره تماس *</label><input type="tel" id="ifnex-sender-phone"></div>
<div class="ifnex-field"><label>شهر</label><input type="text" id="ifnex-sender-city"></div>
<div class="ifnex-field full"><label>آدرس کامل *</label><textarea id="ifnex-sender-address" rows="2"></textarea></div>
</div>
<h3 style="margin-top:24px;">📮 اطلاعات گیرنده</h3>
<div class="ifnex-form-grid">
<div class="ifnex-field"><label>نام و نام خانوادگی *</label><input type="text" id="ifnex-receiver-name"></div>
<div class="ifnex-field"><label>شماره تماس *</label><input type="tel" id="ifnex-receiver-phone"></div>
<div class="ifnex-field"><label>شهر</label><input type="text" id="ifnex-receiver-city"></div>
<div class="ifnex-field full"><label>آدرس کامل *</label><textarea id="ifnex-receiver-address" rows="2"></textarea></div>
</div>
<div class="ifnex-form-nav">
<button class="ifnex-btn ifnex-prev-step">→ قبلی</button>
<button class="ifnex-btn ifnex-btn-primary ifnex-next-step">ادامه ←</button>
</div>
</div>
</div>
<!-- Step 3: Review & Discount -->
<div class="ifnex-form-step" data-step="3">
<div class="ifnex-form-card">
<h3>🏷️ کد تخفیف (اختیاری)</h3>
<div class="ifnex-form-grid">
<div class="ifnex-field full"><label>کد تخفیف</label><input type="text" id="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 4: Confirm -->
<div class="ifnex-form-step" data-step="4">
<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();
}