1Nhận đơn – Bộ lọc – Số lượng đơn
Thông tin
1. Nhân đơn (đã loại bỏ shortcode)
2. Bộ lọc đơn + số lượng đơn
3. Không hiển thị đơn hoàn tất + hủy trong bộ lọc TẤT CẢ
4. Chỉ hiển thị đơn trong ngày cho HOÀN TẤT + HỦY
1. Nhân đơn (đã loại bỏ shortcode)
2. Bộ lọc đơn + số lượng đơn
3. Không hiển thị đơn hoàn tất + hủy trong bộ lọc TẤT CẢ
4. Chỉ hiển thị đơn trong ngày cho HOÀN TẤT + HỦY
// 1. Hàm hiển thị cấu trúc HTML trực tiếp (Chỉ hoạt động trên Mobile & Tablet)
function bks_render_order_tracker_html($status_requested = 'any') {
if ( ! wp_is_mobile() ) {
echo '<!-- Tính năng này chỉ khả dụng trên thiết bị di động -->';
return;
}
wp_enqueue_script('bks-ajax-tracker');
// CSS Khóa cố định bộ lọc (Sticky) và tiêu đề bảng ổn định, không hiệu ứng nhấp nháy
$html = '<style>
.bks-tracker-wrapper { position: relative; font-family: sans-serif; }
.bks-status-filter-wrapper {
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 100;
background: #ffffff;
padding: 10px 0;
margin-bottom: 5px;
overflow-x: auto;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
box-shadow: 0 4px 6px -6px rgba(0,0,0,0.1);
}
.bks-filter-btn {
padding: 6px 14px;
margin-right: 8px;
border: 1px solid #eee;
background: #f8f9fa;
border-radius: 16px;
font-size: 13px;
color: #666;
cursor: pointer;
transition: all 0.2s ease;
}
.bks-filter-btn.active {
background: #fff !important;
color: #333 !important;
border-color: #ccc !important;
font-weight: bold;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.bks-table-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
</style>';
$html .= '<div class="bks-tracker-wrapper">';
$html .= '<div class="bks-status-filter-wrapper" id="bks-status-filter-container" data-default-status="' . esc_attr($status_requested) . '">';
$html .= '<button class="bks-filter-btn active" data-filter="' . esc_attr($status_requested) . '">Tất cả</button>';
$html .= '</div>';
$html .= '<div id="bks-order-list-container" data-status="' . esc_attr($status_requested) . '">Đang tải đơn hàng...</div>';
$html .= '</div>';
echo $html;
}
// 2. Đăng ký Script AJAX cập nhật liên tục thông minh và xử lý sự kiện bấm Tab lọc
function bks_register_tracker_scripts() {
if ( ! wp_is_mobile() ) {
return;
}
wp_register_script('bks-ajax-tracker', '', [], '', true);
$js_code = "
jQuery(document).ready(function($) {
var container = $('#bks-order-list-container');
var filterContainer = $('#bks-status-filter-container');
if (!container.length) return;
var currentActiveStatus = container.data('status');
var timer = null;
var isAjaxRunning = false;
function fetch_orders_continuous() {
if (document.hidden) {
scheduleNextFetch(5000);
return;
}
if (isAjaxRunning) return;
isAjaxRunning = true;
$.ajax({
url: '" . admin_url('admin-ajax.php') . "',
type: 'POST',
data: {
action: 'bks_get_live_orders',
status: currentActiveStatus,
default_status: filterContainer.data('default-status')
},
success: function(response) {
if(response.success) {
container.html(response.data.table_html);
var currentScrollLeft = filterContainer.scrollLeft();
filterContainer.html(response.data.filter_html);
filterContainer.scrollLeft(currentScrollLeft);
}
},
complete: function() {
isAjaxRunning = false;
scheduleNextFetch(10000);
}
});
}
function scheduleNextFetch(delay) {
clearTimeout(timer);
timer = setTimeout(fetch_orders_continuous, delay);
}
fetch_orders_continuous();
$(document).on('click', '.bks-filter-btn', function() {
currentActiveStatus = $(this).data('filter');
container.html('<div style=\"padding:30px; text-align:center; color:#888;\">Đang tải đơn hàng...</div>');
fetch_orders_continuous();
});
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
fetch_orders_continuous();
}
});
});
";
wp_add_inline_script('bks-ajax-tracker', $js_code);
}
add_action('wp_enqueue_scripts', 'bks_register_tracker_scripts');
// 3. Xử lý dữ liệu trả về: Loại bỏ đơn huỷ/hoàn thành khỏi tab "Tất cả"
function bks_handle_live_orders_ajax() {
if ( ! class_exists( 'WooCommerce' ) ) {
wp_send_json_error('WooCommerce chưa được kích hoạt.');
}
global $wpdb;
$status_requested = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : 'any';
$default_status = isset($_POST['default_status']) ? sanitize_text_field($_POST['default_status']) : 'any';
$today_start = date('Y-m-d 00:00:00');
// Kiểm tra cấu hình HPOS
$is_hpos = function_exists( 'wc_get_container' ) && wc_get_container()->get( \Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled();
if ( $is_hpos ) {
$table_name = $wpdb->prefix . 'wc_orders';
$status_col = 'status';
$date_col = 'date_updated_gmt';
$where_type = "WHERE type = 'shop_order'";
} else {
$table_name = $wpdb->posts;
$status_col = 'post_status';
$date_col = 'post_modified_gmt';
$where_type = "WHERE post_type = 'shop_order'";
}
// Đếm số lượng đơn hàng nhóm theo trạng thái
$raw_counts = $wpdb->get_results("SELECT $status_col AS status, COUNT(*) AS count FROM $table_name $where_type GROUP BY $status_col", ARRAY_A);
$counts = array();
foreach ( $raw_counts as $row ) {
$clean_status = str_replace('wc-', '', $row['status']);
$counts[$clean_status] = (int) $row['count'];
}
// Đếm riêng lượng đơn huỷ và hoàn thành trong ngày
$today_gmt = gmdate('Y-m-d 00:00:00');
$counts['cancelled'] = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $table_name $where_type AND $status_col = %s AND $date_col >= %s", $is_hpos ? 'cancelled' : 'wc-cancelled', $today_gmt));
$counts['completed'] = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $table_name $where_type AND $status_col = %s AND $date_col >= %s", $is_hpos ? 'completed' : 'wc-completed', $today_gmt));
// Đếm tổng số đơn hiển thị ở tab "Tất cả" (Loại trừ completed và cancelled để khớp logic ẩn đơn)
$total_visible = 0;
$all_statuses_list = wc_get_order_statuses();
foreach ( $all_statuses_list as $key => $name ) {
$slug = str_replace('wc-', '', $key);
if ( $slug !== 'completed' && $slug !== 'cancelled' && isset($counts[$slug]) ) {
$total_visible += $counts[$slug];
}
}
// Render HTML cho thanh Tabs bộ lọc kèm số lượng chuẩn
$filter_active_class = ($status_requested === $default_status) ? ' active' : '';
$filter_html = '<button class="bks-filter-btn' . $filter_active_class . '" data-filter="' . esc_attr($default_status) . '">Tất cả (' . $total_visible . ')</button>';
foreach ( $all_statuses_list as $key => $name ) {
$status_slug = str_replace('wc-', '', $key);
if ( $default_status !== 'any' && strpos($default_status, $status_slug) === false ) {
continue;
}
$count_badge = isset($counts[$status_slug]) ? $counts[$status_slug] : 0;
$active_class = ($status_requested === $status_slug) ? ' active' : '';
if ( $status_slug === 'cancelled' || $status_slug === 'completed' ) {
$display_name = esc_html($name) . ' (Trong ngày) (' . $count_badge . ')';
} else {
$display_name = esc_html($name) . ' (' . $count_badge . ')';
}
$filter_html .= '<button class="bks-filter-btn' . $active_class . '" data-filter="' . esc_attr($status_slug) . '">' . $display_name . '</button>';
}
// Thiết lập điều kiện truy vấn database cho bảng dữ liệu
$args = array(
'limit' => 30,
'orderby' => 'date_modified',
'order' => 'DESC',
);
if ( $status_requested === 'any' ) {
// TỐI ƯU: Nếu chọn tab "Tất cả", lấy tất cả trạng thái nhưng LOẠI TRỪ hẳn completed và cancelled khỏi câu lệnh SQL
$all_slugs = array_map(function($status) { return str_replace('wc-', '', $status); }, array_keys($all_statuses_list));
$args['status'] = array_diff($all_slugs, array('completed', 'cancelled'));
} elseif ( $status_requested === 'cancelled' ) {
$args['status'] = 'cancelled';
$args['date_modified'] = '>=' . $today_start;
} elseif ( $status_requested === 'completed' ) {
$args['status'] = 'completed';
$args['date_modified'] = '>=' . $today_start;
} else {
$args['status'] = $status_requested;
}
$query = new WC_Order_Query($args);
$orders = $query->get_orders();
$status_colors = array(
'pending' => array('bg' => '#e6f0fa', 'text' => '#005a9c'), // Xanh dương
'on-hold' => array('bg' => '#fbeaeA', 'text' => '#a80000'), // Đỏ
'processing' => array('bg' => '#f3f2f1', 'text' => '#605e5c'), // Xám
'transfer' => array('bg' => '#f3f2f1', 'text' => '#0073aa'), // Xanh dương
'cook' => array('bg' => '#fffae6', 'text' => '#b38600'), // Vàng nhạt
'cook-done' => array('bg' => '#fff0e6', 'text' => '#d96a00'), // Cam
'shipper' => array('bg' => '#ed4700', 'text' => '#fff0e6'), // Cam đậm
'cancelled' => array('bg' => '#fde7e9', 'text' => '#a80000'), // Đỏ đậm
'completed' => array('bg' => '#e6f9ed', 'text' => '#107c41'), // Xanh lá
);
// Giao diện bảng
$table_html = '<div style="overflow-x:auto;">';
$table_html .= '<table style="width: 95%;border-collapse: collapse;margin-left: 10px;">';
$table_html .= '<tr style="background:#f8f9fa; text-align:left; border-bottom: 2px solid #eaeaea;">';
$table_html .= '<th style="padding:12px 10px;">Đơn</th>';
$table_html .= '<th style="padding:12px 10px;">Giờ</th>';
$table_html .= '<th style="padding:12px 10px;">T.Tin</th>';
$table_html .= '<th style="padding:12px 10px; text-align:center;width:25%;">T.Thái</th>';
$table_html .= '</tr>';
if ( ! empty( $orders ) ) {
foreach ( $orders as $order ) {
$current_status = $order->get_status();
$order_id = $order->get_id();
$order_key = $order->get_order_key();
$date_modified = $order->get_date_modified();
$time_updated = $date_modified ? $date_modified->date('H:i - d/m') : '---';
$created = $order->get_date_created() ? $order->get_date_created()->date_i18n('d/m/Y H:i') : '';
$order_admin_url = admin_url( 'post.php?post=' . $order_id . '&action=edit' );
if ( $is_hpos ) {
$order_admin_url = admin_url( 'admin.php?page=wc-orders&action=edit&id=' . $order_id );
}
$button = '';
if ( $order->has_status( array( 'cook' ) ) ) {
$pdf_url = get_site_url( null, 'wcpdf/packing-slip/' . $order_id . '/' . $order_key . '/pdf', 'http' );
$button = '<br><a target="_blank" href="' . esc_url( $pdf_url ) . '" class="prs" style="display:inline-block; margin-top:5px; padding:2px 6px; background:#0073aa; color:#fff; font-size:11px; border-radius:3px; text-decoration:none;">📄 Phiếu chế biến</a>';
}
$colors = isset($status_colors[$current_status]) ? $status_colors[$current_status] : array('bg' => '#f3f2f1', 'text' => '#605e5c');
// Cấu trúc hàng dữ liệu chuẩn chỉnh trên di động
$table_html .= '<tr>';
$table_html .= '<td style="padding:12px 10px; font-weight:bold; color:#333;">#' . $order_id . '</td>';
$table_html .= '<td style="padding:12px 10px; color:#666; font-size:13px;">' . esc_html( $time_updated ) . '</td>';
$table_html .= '<td style="padding:12px 10px;">';
$table_html .= '<a href="https://95deli.fship.vn/dashboard/details?order_id='.$order_id.'" style="color:#0073aa; text-decoration:none; font-weight:500;" target="_blank"><i class="fa-solid fa-circle-info lin"></i></a>';
/* $html .= $button; */
$table_html .= '</td>';
$table_html .= '<td style="padding:12px 10px; text-align:center;">';
$table_html .= '<span style="display: inline-block;padding:5px 10px; border-radius:20px;font-weight:600; background-color:' . $colors['bg'] . '; color:' . $colors['text'] . ';width:-webkit-fill-available;white-space: nowrap;">';
$table_html .= wc_get_order_status_name( $current_status );
$table_html .= '</span>';
$table_html .= '</td>';
$table_html .= '<tr style="border-bottom: 4px solid #ddd;"><td colspan="4"><div class="flex justify-between items-end">'.$created.''.$button.'<div style="text-align:right;">Cập nhật '.$time_updated.'</div></div></td</tr>';
$table_html .= '</tr>';
}
} else {
// SỬA LỖI: Bọc chuỗi thông báo trống vào đúng cấu trúc tr -> td để không làm lệch bảng
$table_html .= '<tr style="background: #fff;"><td colspan="4" style="padding: 30px; text-align:center; color:#888;">Không tìm thấy đơn hàng khả dụng nào ở trạng thái này.</td></tr>';
}
$table_html .= '</table>';
$table_html .= '</div>';
wp_send_json_success(array(
'table_html' => $table_html,
'filter_html' => $filter_html
));
}
add_action('wp_ajax_bks_get_live_orders', 'bks_handle_live_orders_ajax');
add_action('wp_ajax_nopriv_bks_get_live_orders', 'bks_handle_live_orders_ajax');
2Nhận đơn – Bộ lọc cao cấp
Thông tin
1. Nhân đơn (Loại bỏ shortcode)
2. Bộ lọc đơn (Chỉ hiển thị đơn trong ngày cho HOÀN TẤT + HỦY)
1. Nhân đơn (Loại bỏ shortcode)
2. Bộ lọc đơn (Chỉ hiển thị đơn trong ngày cho HOÀN TẤT + HỦY)
// 1. Hàm hiển thị cấu trúc HTML trực tiếp thay cho Shortcode (Chỉ hoạt động trên Mobile & Tablet)
function bks_render_order_tracker_html($status_requested = 'any') {
// Nếu KHÔNG PHẢI mobile/tablet thì chặn hiển thị để nhẹ server
if ( ! wp_is_mobile() ) {
echo '<!-- Tính năng này chỉ khả dụng trên thiết bị di động -->';
return;
}
// Tự động kích hoạt gọi script AJAX cho trang có chèn hàm này
wp_enqueue_script('bks-ajax-tracker');
$all_statuses = wc_get_order_statuses();
// Giao diện Thanh bộ lọc trạng thái bằng Tab kiểu di động
$html = '<div class="bks-status-filter-wrapper">';
$html .= '<button class="bks-filter-btn active" data-filter="' . esc_attr($status_requested) . '">Tất cả</button>';
foreach ( $all_statuses as $key => $name ) {
$status_slug = str_replace('wc-', '', $key);
if ( $status_requested !== 'any' && strpos($status_requested, $status_slug) === false ) {
continue;
}
// Thêm hậu tố trực quan cho các tab đặc biệt chỉ hiển thị trong ngày
if ( $status_slug === 'cancelled' ) {
$display_name = esc_html($name) . '';
} elseif ( $status_slug === 'completed' ) {
$display_name = esc_html($name) . '';
} else {
$display_name = esc_html($name);
}
$html .= '<button class="bks-filter-btn" data-filter="' . esc_attr($status_slug) . '">' . $display_name . '</button>';
}
$html .= '</div>';
// Thẻ div chứa danh sách đơn hàng được cập nhật qua AJAX Pull
$html .= '<div id="bks-order-list-container" data-status="' . esc_attr($status_requested) . '">Đang tải đơn hàng...</div>';
echo $html;
}
// 2. Đăng ký Script AJAX cập nhật liên tục thông minh và xử lý sự kiện bấm Tab lọc
function bks_register_tracker_scripts() {
if ( ! wp_is_mobile() ) {
return;
}
wp_register_script('bks-ajax-tracker', '', [], '', true);
$js_code = "
jQuery(document).ready(function($) {
var container = $('#bks-order-list-container');
if (!container.length) return;
var currentActiveStatus = container.data('status');
var timer = null;
var isAjaxRunning = false;
function fetch_orders_continuous() {
if (document.hidden) {
scheduleNextFetch(5000);
return;
}
if (isAjaxRunning) return;
isAjaxRunning = true;
$.ajax({
url: '" . admin_url('admin-ajax.php') . "',
type: 'POST',
data: {
action: 'bks_get_live_orders',
status: currentActiveStatus
},
success: function(response) {
if(response.success) {
container.html(response.data);
}
},
complete: function() {
isAjaxRunning = false;
scheduleNextFetch(100000);
}
});
}
function scheduleNextFetch(delay) {
clearTimeout(timer);
timer = setTimeout(fetch_orders_continuous, delay);
}
fetch_orders_continuous();
$(document).on('click', '.bks-filter-btn', function() {
$('.bks-filter-btn').removeClass('active').css({'background': '#f8f9fa', 'color': '#666', 'border-color': '#eee'});
$(this).addClass('active').css({'background': '#fff', 'color': '#333', 'border-color': '#ccc'});
currentActiveStatus = $(this).data('filter');
container.html('Đang tải đơn hàng...');
fetch_orders_continuous();
});
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
fetch_orders_continuous();
}
});
});
";
wp_add_inline_script('bks-ajax-tracker', $js_code);
}
add_action('wp_enqueue_scripts', 'bks_register_tracker_scripts');
// 3. Xử lý dữ liệu trả về từ Database dựa theo Tab trạng thái chọn + Lọc đơn huỷ trong ngày
function bks_handle_live_orders_ajax() {
$status_requested = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : 'any';
if ( $status_requested !== 'any' && strpos($status_requested, ',') !== false ) {
$status_filter = array_map('trim', explode(',', $status_requested));
} else {
$status_filter = $status_requested;
}
$args = array(
'limit' => 20,
'orderby' => 'date_modified',
'order' => 'DESC',
);
$today_start = date('Y-m-d 00:00:00'); // Mốc thời gian 0h00 đầu ngày hôm nay
// Xử lý bộ lọc SQL tối ưu khi nhân viên bấm đích danh từng Tab lẻ
if ( $status_filter === 'cancelled' ) {
$args['status'] = 'cancelled';
$args['date_modified'] = '>=' . $today_start;
} elseif ( $status_filter === 'completed' ) {
$args['status'] = 'completed';
$args['date_modified'] = '>=' . $today_start;
} else {
$args['status'] = $status_filter;
}
$query = new WC_Order_Query($args);
$orders = $query->get_orders();
// Bảng định nghĩa màu sắc đầy đủ cho mọi trạng thái (Đã thêm lại Completed)
$status_colors = array(
'pending' => array('bg' => '#e6f0fa', 'text' => '#005a9c'), // Xanh dương
'on-hold' => array('bg' => '#fbeaeA', 'text' => '#a80000'), // Đỏ
'processing' => array('bg' => '#f3f2f1', 'text' => '#605e5c'), // Xám
'transfer' => array('bg' => '#f3f2f1', 'text' => '#0073aa'), // Xanh dương
'cook' => array('bg' => '#fffae6', 'text' => '#b38600'), // Vàng nhạt
'cook-done' => array('bg' => '#fff0e6', 'text' => '#d96a00'), // Cam
'shipper' => array('bg' => '#ed4700', 'text' => '#fff0e6'), // Cam đậm
'cancelled' => array('bg' => '#fde7e9', 'text' => '#a80000'), // Đỏ đậm
'completed' => array('bg' => '#e6f9ed', 'text' => '#107c41'), // Xanh lá
);
// Giao diện bảng
$html = '<div style="overflow-x:auto;">';
$html .= '<table style="width: 95%;border-collapse: collapse;margin-left: 10px;">';
$html .= '<tr style="background:#f8f9fa; text-align:left; border-bottom: 2px solid #eaeaea;">';
$html .= '<th style="padding:12px 10px;">Đơn</th>';
$html .= '<th style="padding:12px 10px;">Giờ</th>';
$html .= '<th style="padding:12px 10px;">T.Tin</th>';
$html .= '<th style="padding:12px 10px; text-align:center;width:25%;">T.Thái</th>';
$html .= '</tr>';
$has_visible_order = false;
if ( ! empty( $orders ) ) {
foreach ( $orders as $order ) {
$current_status = $order->get_status();
// KIỂM TRA CHO TAB "TẤT CẢ" (status=any): Loại bỏ đơn hủy và đơn hoàn thành của ngày hôm trước
if ( $status_filter === 'any' ) {
if ( $current_status === 'cancelled' || $current_status === 'completed' ) {
$date_modified_timestamp = $order->get_date_modified() ? $order->get_date_modified()->getTimestamp() : 0;
if ( $date_modified_timestamp < $today_timestamp ) {
continue; // Bỏ qua, không in hàng này ra bảng
}
}
}
$has_visible_order = true;
$order_id = $order->get_id();
$order_key = $order->get_order_key();
$created = $order->get_date_created() ? $order->get_date_created()->date_i18n('d/m/Y H:i') : '';
$date_modified = $order->get_date_modified();
$time_updated = $date_modified ? $date_modified->date('H:i') : '---';
$button = '';
if ( $order->has_status( array( 'cook' ) ) ) {
$pdf_url = get_site_url( null, 'wcpdf/packing-slip/' . $order_id . '/' . $order_key . '/pdf', 'http' );
$button = '<br><a target="_blank" href="' . esc_url( $pdf_url ) . '" class="prs" style="display:inline-block; margin-top:5px; padding:2px 6px; background:#0073aa; color:#fff; font-size:11px; border-radius:3px; text-decoration:none;">📄 Phiếu chế biến</a>';
}
$colors = isset($status_colors[$current_status]) ? $status_colors[$current_status] : array('bg' => '#f3f2f1', 'text' => '#605e5c');
$html .= '<tr>';
$html .= '<td style="padding:12px 10px; font-weight:bold; color:#333;">#' . $order_id . '</td>';
$html .= '<td style="padding:12px 10px; color:#666; font-size:13px;">' . esc_html( $time_updated ) . '</td>';
$html .= '<td style="padding:12px 10px;">';
$html .= '<a href="https://95deli.fship.vn/dashboard/details?order_id='.$order_id.'" style="color:#0073aa; text-decoration:none; font-weight:500;" target="_blank"><i class="fa-solid fa-circle-info lin"></i></a>';
/*$html .= $button; */
/* $html .= '</td>';
$html .= '<td style="padding:12px 10px; text-align:center;">';
$html .= '<span style="display: inline-block;padding:5px 10px; border-radius:20px;font-weight:600; background-color:' . $colors['bg'] . '; color:' . $colors['text'] . ';width:-webkit-fill-available;white-space: nowrap;">';
$html .= wc_get_order_status_name( $current_status );
$html .= '</span>';
$html .= '</td>';
$html .= '<tr style="border-bottom: 4px solid #ddd;"><td colspan="4"><div class="flex justify-between items-end">'.$created.''.$button.'<div style="text-align:right;">Cập nhật '.$time_updated.'</div></div></td</tr>';
$html .= '</tr>';
}
} else {
$html .= '<tr><td colspan="4" style="padding: 30px; text-align:center; color:#888;">Không có đơn hàng nào ở trạng thái này.</td></tr>';
}
$html .= '</table>';
$html .= '</div>';
wp_send_json_success($html);
}
add_action('wp_ajax_bks_get_live_orders', 'bks_handle_live_orders_ajax');
add_action('wp_ajax_nopriv_bks_get_live_orders', 'bks_handle_live_orders_ajax');
3Nhận đơn + Bộ lọc
Thông tin
1. Nhân đơn
2. Bộ lọc đơn
3. Có shortcode [cap_nhat_don_hang]
1. Nhân đơn
2. Bộ lọc đơn
3. Có shortcode [cap_nhat_don_hang]
// 1. Tạo Shortcode hỗ trợ bộ lọc trạng thái (Chỉ hoạt động trên Mobile & Tablet)
function bks_order_tracker_shortcode($atts) {
if ( ! wp_is_mobile() ) {
return '<!-- Tính năng này chỉ khả dụng trên thiết bị di động -->';
}
$atts = shortcode_atts(array(
'status' => 'any',
), $atts, 'cap_nhat_don_hang');
wp_enqueue_script('bks-ajax-tracker');
// Lấy danh sách tất cả các trạng thái có trong hệ thống để tạo thanh bộ lọc
$all_statuses = wc_get_order_statuses();
// Giao diện Thanh bộ lọc trạng thái bằng Tab kiểu di động
$html = '<div class="bks-status-filter-wrapper" style="margin-bottom: 15px; overflow-x: auto; white-space: nowrap; padding-bottom: 8px; -webkit-overflow-scrolling: touch;">';
$html .= '<button class="bks-filter-btn active" data-filter="' . esc_attr($atts['status']) . '" style="padding: 6px 14px; margin-right: 8px; border: 1px solid #ccc; background: #fff; border-radius: 16px; font-size: 13px; font-weight: bold; color: #333; cursor: pointer;">Tất cả</button>';
foreach ( $all_statuses as $key => $name ) {
$status_slug = str_replace('wc-', '', $key);
// Nếu shortcode chỉ định 1 nhóm trạng thái cụ thể, chỉ hiển thị các tab thuộc nhóm đó
if ( $atts['status'] !== 'any' && strpos($atts['status'], $status_slug) === false ) {
continue;
}
$html .= '<button class="bks-filter-btn" data-filter="' . esc_attr($status_slug) . '" style="padding: 6px 14px; margin-right: 8px; border: 1px solid #eee; background: #f8f9fa; border-radius: 16px; font-size: 13px; color: #666; cursor: pointer;">' . esc_html($name) . '</button>';
}
$html .= '</div>';
// Bảng chứa dữ liệu đơn hàng
$html .= '<div id="bks-order-list-container" data-status="' . esc_attr($atts['status']) . '">Đang tải đơn hàng...</div>';
return $html;
}
add_shortcode('cap_nhat_don_hang', 'bks_order_tracker_shortcode');
// 2. Đăng ký Script AJAX cập nhật liên tục thông minh và xử lý sự kiện bấm Tab lọc
function bks_register_tracker_scripts() {
if ( ! wp_is_mobile() ) {
return;
}
wp_register_script('bks-ajax-tracker', '', [], '', true);
$js_code = "
jQuery(document).ready(function($) {
var container = $('#bks-order-list-container');
if (!container.length) return;
// Mặc định lấy trạng thái từ shortcode cấu hình ban đầu
var currentActiveStatus = container.data('status');
var timer = null;
var isAjaxRunning = false;
function fetch_orders_continuous() {
if (document.hidden) {
scheduleNextFetch(5000);
return;
}
if (isAjaxRunning) return;
isAjaxRunning = true;
$.ajax({
url: '" . admin_url('admin-ajax.php') . "',
type: 'POST',
data: {
action: 'bks_get_live_orders',
status: currentActiveStatus
},
success: function(response) {
if(response.success) {
container.html(response.data);
}
},
complete: function() {
isAjaxRunning = false;
scheduleNextFetch(100000); // Tự động cập nhật lại sau 10 giây
}
});
}
function scheduleNextFetch(delay) {
clearTimeout(timer);
timer = setTimeout(fetch_orders_continuous, delay);
}
// Kích hoạt chạy lần đầu tiên
fetch_orders_continuous();
// Xử lý sự kiện khi người dùng bấm vào các Tab bộ lọc trạng thái
$(document).on('click', '.bks-filter-btn', function() {
$('.bks-filter-btn').removeClass('active').css({'background': '#f8f9fa', 'color': '#666', 'border-color': '#eee'});
$(this).addClass('active').css({'background': '#fff', 'color': '#333', 'border-color': '#ccc'});
// Thay đổi trạng thái cần lọc hiện tại
currentActiveStatus = $(this).data('filter');
// Đưa bảng về trạng thái chờ tải và gọi AJAX ngay lập tức
container.html('Đang tải đơn hàng...');
fetch_orders_continuous();
});
// Tự động chạy lại khi bật điện thoại lên / quay lại tab trình duyệt
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
fetch_orders_continuous();
}
});
});
";
wp_add_inline_script('bks-ajax-tracker', $js_code);
}
add_action('wp_enqueue_scripts', 'bks_register_tracker_scripts');
// 3. Xử lý dữ liệu trả về từ Database dựa theo Tab trạng thái đang được chọn
function bks_handle_live_orders_ajax() {
$status_requested = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : 'any';
if ( $status_requested !== 'any' && strpos($status_requested, ',') !== false ) {
$status_filter = array_map('trim', explode(',', $status_requested));
} else {
$status_filter = $status_requested;
}
$args = array(
'limit' => 20,
'orderby' => 'date_modified',
'order' => 'DESC',
'status' => $status_filter,
);
$query = new WC_Order_Query($args);
$orders = $query->get_orders();
// Bảng định nghĩa màu sắc đầy đủ cho mọi trạng thái (Đã thêm lại Completed)
$status_colors = array(
'cook' => array('bg' => '#fffae6', 'text' => '#b38600'), // Vàng nhạt
'shipper' => array('bg' => '#e6f9ed', 'text' => '#107c41'), // Xanh lá
'cook-done' => array('bg' => '#fff0e6', 'text' => '#d96a00'), // Cam
'pending' => array('bg' => '#e6f0fa', 'text' => '#005a9c'), // Xanh dương
'on-hold' => array('bg' => '#fbeaeA', 'text' => '#a80000'), // Đỏ
'processing' => array('bg' => '#f3f2f1', 'text' => '#605e5c'), // Xám
'failed' => array('bg' => '#fde7e9', 'text' => '#a80000'), // Đỏ đậm
);
// Giao diện bảng
$html = '<div style="overflow-x:auto;">';
$html .= '<table style="width:100%; border-collapse: collapse; font-family: sans-serif; font-size: 14px; box-shadow: 0 2px 5px rgba(0,0,0,0.05);">';
$html .= '<tr style="background:#f8f9fa; text-align:left; border-bottom: 2px solid #eaeaea;">';
$html .= '<th style="padding:12px 10px;">Đơn</th>';
$html .= '<th style="padding:12px 10px;">Giờ</th>';
$html .= '<th style="padding:12px 10px;">T.Tin</th>';
$html .= '<th style="padding:12px 10px; text-align:center;width:25%;">T.Thái</th>';
$html .= '</tr>';
if ( ! empty( $orders ) ) {
foreach ( $orders as $order ) {
$current_status = $order->get_status();
$order_id = $order->get_id();
$order_key = $order->get_order_key();
$created = $order->get_date_created() ? $order->get_date_created()->date_i18n('d/m/Y H:i') : '';
$date_modified = $order->get_date_modified();
$time_updated = $date_modified ? $date_modified->date('H:i') : '---';
if ( $order->has_status( array( 'cook' ) ) ) {
$pdf_url = get_site_url( null, 'wcpdf/packing-slip/' . $order_id . '/' . $order_key . '/pdf', 'http' );
$button = '<br><a target="_blank" href="' . esc_url( $pdf_url ) . '" class="prs" style="display:inline-block; margin-top:5px; padding:2px 6px; background:#0073aa; color:#fff; font-size:11px; border-radius:3px; text-decoration:none;">📄 Phiếu chế biến</a>';
}
$colors = isset($status_colors[$current_status]) ? $status_colors[$current_status] : array('bg' => '#f3f2f1', 'text' => '#605e5c');
$html .= '<tr>';
$html .= '<td style="padding:12px 10px; font-weight:bold; color:#333;">#' . $order_id . '</td>';
$html .= '<td style="padding:12px 10px; color:#666; font-size:13px;">' . esc_html( $time_updated ) . '</td>';
$html .= '<td style="padding:12px 10px;">';
$html .= '<a href="https://95deli.fship.vn/dashboard/details?order_id='.$order_id.'" style="color:#0073aa; text-decoration:none; font-weight:500;" target="_blank"><i class="fa-solid fa-circle-info lin"></i></a>';
$html .= '</td>';
$html .= '<td style="padding:12px 10px; text-align:center;">';
$html .= '<span style="display:inline-block; padding:5px 12px; border-radius:20px; font-size:12px; font-weight:600; background-color:' . $colors['bg'] . '; color:' . $colors['text'] . ';width:-webkit-fill-available;white-space: nowrap;">';
$html .= wc_get_order_status_name( $current_status );
$html .= '</span>';
$html .= '</td>';
$html .= '<tr style="border-bottom: 4px solid #eee;"><td colspan="4"><div class="flex justify-between items-end">'.$created.''.$button.'<div style="text-align:right;">Cập nhật '.$time_updated.'</div></div></td</tr>';
$html .= '</tr>';
}
} else {
$html .= '<tr><td colspan="4" style="padding: 30px; text-align:center; color:#888;">Không có đơn hàng nào ở trạng thái này.</td></tr>';
}
$html .= '</table>';
$html .= '</div>';
wp_send_json_success($html);
}
add_action('wp_ajax_bks_get_live_orders', 'bks_handle_live_orders_ajax');
add_action('wp_ajax_nopriv_bks_get_live_orders', 'bks_handle_live_orders_ajax');
*/
//** DOASHBOASD **//
/*// 1. Tạo Shortcode hỗ trợ bộ lọc trạng thái
function bks_order_tracker_shortcode($atts) {
$atts = shortcode_atts(array(
'status' => 'any',
), $atts, 'cap_nhat_don_hang');
wp_enqueue_script('bks-ajax-tracker');
return '<div id="bks-order-list-container" data-status="' . esc_attr($atts['status']) . '">Đang tải đơn hàng...</div>';
}
add_shortcode('cap_nhat_don_hang', 'bks_order_tracker_shortcode');
// 2. Đăng ký Script AJAX cập nhật liên tục
function bks_register_tracker_scripts() {
wp_register_script('bks-ajax-tracker', '', [], '', true);
$js_code = "
jQuery(document).ready(function($) {
function fetch_orders_continuous() {
var container = $('#bks-order-list-container');
if (!container.length) return;
var orderStatus = container.data('status');
$.ajax({
url: '" . admin_url('admin-ajax.php') . "',
type: 'POST',
data: {
action: 'bks_get_live_orders',
status: orderStatus
},
success: function(response) {
if(response.success) {
container.html(response.data);
}
}
});
}
fetch_orders_continuous();
setInterval(fetch_orders_continuous, 10000); // Tự động cập nhật mỗi 10 giây
});
";
wp_add_inline_script('bks-ajax-tracker', $js_code);
}
add_action('wp_enqueue_scripts', 'bks_register_tracker_scripts');
// 3. Xử lý dữ liệu trả về và Phân biệt màu sắc theo trạng thái
function bks_handle_live_orders_ajax() {
if ( ! class_exists( 'WooCommerce' ) ) {
wp_send_json_error('WooCommerce chưa được kích hoạt.');
}
$status_requested = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : 'any';
if ( $status_requested !== 'any' && strpos($status_requested, ',') !== false ) {
$status_filter = array_map('trim', explode(',', $status_requested));
} else {
$status_filter = $status_requested;
}
$args = array(
'limit' =>-1,
'orderby' => 'date',
'order' => 'DESC',
'status' => $status_filter,
);
$query = new WC_Order_Query($args);
$orders = $query->get_orders();
// Định nghĩa bảng màu cho từng trạng thái đơn hàng WooCommerce
$status_colors = array(
'pending' => array('bg' => '#fffae6', 'text' => '#b38600'), // Vàng nhạt - Chờ thanh toán
'processing' => array('bg' => '#e6f9ed', 'text' => '#107c41'), // Xanh lá - Đang xử lý
'on-hold' => array('bg' => '#fff0e6', 'text' => '#d96a00'), // Cam - Tạm giữ
'completed' => array('bg' => '#e6f0fa', 'text' => '#005a9c'), // Xanh dương - Đã hoàn thành
'cancelled' => array('bg' => '#fbeaeA', 'text' => '#a80000'), // Đỏ - Đã hủy
'refunded' => array('bg' => '#f3f2f1', 'text' => '#605e5c'), // Xám - Đã hoàn tiền
'failed' => array('bg' => '#fde7e9', 'text' => '#a80000'), // Đỏ đậm - Thất bại
);
// Giao diện bảng
$html = '<table style="width:100%; border-collapse: collapse; font-family: sans-serif; font-size: 14px; box-shadow: 0 2px 5px rgba(0,0,0,0.05);">';
$html .= '<tr style="background:#f8f9fa; text-align:left; border-bottom: 2px solid #eaeaea;">';
$html .= '<th style="padding:12px 10px;">Mã Đơn</th>';
$html .= '<th style="padding:12px 10px;">Khách Hàng</th>';
$html .= '<th style="padding:12px 10px; text-align:center;">Trạng Thái</th>';
$html .= '<th style="padding:12px 10px; text-align:right;">Tổng Tiền</th>';
$html .= '</tr>';
if ( ! empty( $orders ) ) {
foreach ( $orders as $order ) {
$current_status = $order->get_status();
{$statuses = array( 'cook' ); // Define order statuses
if ($order->has_status($statuses)) {
$button = '<a target="_blank" href="' .get_site_url( null, 'wcpdf/packing-slip/'.$order_id.'/'.$order_key.'/pdf', 'http' ). '" class="prs">Phiếu chế biến</a>';}}
{$statuses = array( 'shipper' ); // Define order statuses
if ($order->has_status($statuses)) {
$button = '';}}
// Lấy màu sắc tương ứng, nếu không có trong danh sách thì dùng màu xám mặc định
$colors = isset($status_colors[$current_status]) ? $status_colors[$current_status] : array('bg' => '#f3f2f1', 'text' => '#605e5c');
$html .= '<tr style="border-bottom: 1px solid #eee; transition: background 0.3s;">';
$html .= '<td style="padding:12px 10px; font-weight:bold; color:#333;">#' . $order->get_id() . '</td>';
$html .= '<td style="padding:12px 10px; color:#555;">' . esc_html( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ) . ''.$button.'</td>';
// Cột Trạng thái với Badge màu sắc động
$html .= '<td style="padding:12px 10px; text-align:center;">';
$html .= '<span style="display:inline-block; padding:5px 12px; border-radius:20px; font-size:12px; font-weight:600; background-color:' . $colors['bg'] . '; color:' . $colors['text'] . ';">';
$html .= wc_get_order_status_name( $current_status );
$html .= '</span>';
$html .= '</td>';
$html .= '<td style="padding:12px 10px; text-align:right; font-weight:bold; color:#111;">' . $order->get_formatted_order_total() . '</td>';
$html .= '</tr>';
}
} else {
$html .= '<tr><td colspan="4" style="padding: 30px; text-align:center; color:#888;">Không tìm thấy đơn hàng nào với trạng thái này.</td></tr>';
}
$html .= '</table>';
wp_send_json_success($html);
}
add_action('wp_ajax_bks_get_live_orders', 'bks_handle_live_orders_ajax');
add_action('wp_ajax_nopriv_bks_get_live_orders', 'bks_handle_live_orders_ajax');
