JezK
Edit File: order-tracking.php
<?php /** * Plugin Name: Order Tracking * Description: Premium WooCommerce Order Tracking * Version: 4.5.0 * Author: Advaxe * Text Domain: advaxe-order-tracking */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } // --------------------------------------------------------- // ১. অ্যাডমিন মেনু এবং সেটিংস তৈরি // --------------------------------------------------------- add_action( 'admin_menu', 'pot_add_admin_menu' ); function pot_add_admin_menu() { $page_hook = add_menu_page( 'Order Tracking Settings', 'Order Tracking', 'manage_options', 'premium-order-tracking', 'pot_admin_page_html', 'dashicons-location', 56 ); add_action( "admin_print_scripts-{$page_hook}", 'pot_admin_scripts' ); add_action( "admin_print_styles-{$page_hook}", 'pot_admin_styles' ); } function pot_admin_scripts() { wp_enqueue_script( 'wp-color-picker' ); wp_add_inline_script( 'wp-color-picker', 'jQuery(document).ready(function($){ $(".pot-color-picker").wpColorPicker(); });' ); } function pot_admin_styles() { wp_enqueue_style( 'wp-color-picker' ); } add_action( 'admin_init', 'pot_register_settings' ); function pot_register_settings() { register_setting( 'pot_settings_group', 'pot_tracking_method' ); register_setting( 'pot_settings_group', 'pot_theme_color' ); } function pot_admin_page_html() { if ( ! current_user_can( 'manage_options' ) ) { return; } ?> <div class="wrap"> <h1>Premium Order Tracking Settings</h1> <form action="options.php" method="post"> <?php settings_fields( 'pot_settings_group' ); do_settings_sections( 'pot_settings_group' ); $current_method = get_option( 'pot_tracking_method', 'id_contact' ); $current_color = get_option( 'pot_theme_color', '#F85506' ); ?> <table class="form-table"> <tr valign="top"> <th scope="row">Tracking Method (কীভাবে ট্র্যাক করবেন?)</th> <td> <select name="pot_tracking_method" style="width: 350px; padding: 5px;"> <option value="id_contact" <?php selected( $current_method, 'id_contact' ); ?>>Order ID + (Email অথবা Phone Number)</option> <option value="contact_only" <?php selected( $current_method, 'contact_only' ); ?>>শুধুমাত্র Email অথবা Phone Number (লিস্ট ভিউ)</option> </select> <p class="description">গ্রাহকরা ট্র্যাকিং পেজে কোন ফর্মটি দেখতে পাবে তা সিলেক্ট করুন।</p> </td> </tr> <tr valign="top"> <th scope="row">Theme Color (থিম কালার)</th> <td> <input type="text" name="pot_theme_color" value="<?php echo esc_attr( $current_color ); ?>" class="pot-color-picker" data-default-color="#F85506" /> <p class="description">ফর্মের বাটন ও বর্ডারের জন্য আপনার ওয়েবসাইটের থিম কালার সিলেক্ট করুন। (অর্ডার স্ট্যাটাসের কালার অটোমেটিক্যালি সেট হবে)</p> </td> </tr> </table> <?php submit_button(); ?> </form> </div> <?php } // --------------------------------------------------------- // ২. শর্টকোড এবং ফ্রন্টএন্ড ডিজাইন (Premium UI) // --------------------------------------------------------- add_shortcode( 'premium_order_tracking', 'pot_tracking_shortcode_output' ); function pot_tracking_shortcode_output() { if ( ! class_exists( 'WooCommerce' ) ) { return '<p style="color:red;">এই প্লাগিনটি কাজ করার জন্য WooCommerce ইন্সটল থাকতে হবে।</p>'; } ob_start(); $method = get_option( 'pot_tracking_method', 'id_contact' ); $theme_color = get_option( 'pot_theme_color', '#F85506' ); // CSS Design ?> <style> :root { --pot-theme-color: <?php echo esc_attr( $theme_color ); ?>; --pot-theme-color-hover: <?php echo esc_attr( $theme_color ); ?>E6; --pot-theme-color-light: <?php echo esc_attr( $theme_color ); ?>1A; } .pot-container { max-width: 800px; margin: 40px auto; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } .pot-form-box { background: #ffffff; padding: 30px; border-radius: 12px; box-shadow: 0 8px 20px rgba(0,0,0,0.08); margin-bottom: 30px; border: 1px solid #eaeaea; } .pot-form-box h3 { margin-top: 0; color: #333; font-weight: 600; margin-bottom: 20px; text-align: center; } .pot-form-group { margin-bottom: 15px; } .pot-form-group label { display: block; margin-bottom: 8px; color: #555; font-weight: 500; } .pot-form-group input { width: 100%; padding: 12px 15px; border: 1px solid #ddd; border-radius: 8px; font-size: 15px; box-sizing: border-box; transition: 0.3s; } .pot-form-group input:focus { border-color: var(--pot-theme-color); box-shadow: 0 0 5px var(--pot-theme-color-light); outline: none; } .pot-btn { background: var(--pot-theme-color); color: #fff; border: none; padding: 12px 25px; border-radius: 8px; font-size: 16px; cursor: pointer; width: 100%; font-weight: 600; transition: background 0.3s; } .pot-btn:hover { background: var(--pot-theme-color-hover); } .pot-error { color: #d32f2f; background: #ffebee; padding: 15px; border-radius: 8px; margin-bottom: 20px; text-align: center; font-weight: 500; border: 1px solid #ffcdd2; } /* List View Styles */ .pot-order-card { background: #fff; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); margin-bottom: 20px; border: 1px solid #eaeaea; overflow: hidden; } .pot-order-summary { padding: 20px; display: flex; justify-content: space-between; align-items: center; background: #fdfdfd; border-bottom: 1px solid #eaeaea; flex-wrap: wrap; gap: 15px; } .pot-summary-left h4 { margin: 0 0 5px 0; color: #333; font-size: 18px; } .pot-summary-left p { margin: 0; color: #777; font-size: 14px; } .pot-summary-right { display: flex; align-items: center; gap: 15px; } /* Dynamic Status Badges */ .pot-status-badge { display: inline-block; padding: 5px 12px; border-radius: 20px; font-size: 13px; font-weight: 600; border: 1px solid transparent; } .status-completed { background: #d4edda; color: #155724; border-color: #c3e6cb; } .status-processing { background: #cce5ff; color: #004085; border-color: #b8daff; } .status-on-hold { background: #fff3cd; color: #856404; border-color: #ffeeba; } .status-cancelled, .status-failed { background: #f8d7da; color: #721c24; border-color: #f5c6cb; } .status-refunded { background: #e2e3e5; color: #383d41; border-color: #d6d8db; } .status-pending { background: #e2e3e5; color: #6c757d; border-color: #d6d8db; } .status-default { background: var(--pot-theme-color-light); color: var(--pot-theme-color); border-color: var(--pot-theme-color); } .pot-view-btn { background: var(--pot-theme-color); color: #fff; border: none; padding: 8px 20px; border-radius: 6px; cursor: pointer; font-weight: 600; font-size: 14px; transition: 0.3s; } .pot-view-btn:hover { background: var(--pot-theme-color-hover); box-shadow: 0 4px 8px var(--pot-theme-color-light); } /* Details Hidden Section */ .pot-order-details { padding: 25px; display: none; background: #ffffff; } .pot-details-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 25px; } .pot-detail-item strong { color: #333; display: block; margin-bottom: 5px; } .pot-detail-item p { margin: 0; color: #555; font-size: 14px; line-height: 1.6; } .pot-products { margin-top: 20px; } .pot-product-item { display: flex; align-items: center; border-bottom: 1px solid #eee; padding: 12px 0; } .pot-product-item:last-child { border-bottom: none; } .pot-product-img img { width: 55px; height: 55px; object-fit: cover; border-radius: 6px; border: 1px solid #ddd; } .pot-product-info { margin-left: 15px; flex-grow: 1; } .pot-product-title { font-weight: 600; color: #333; margin: 0 0 4px 0; font-size: 15px; } /* Variation styles added */ .pot-product-meta { font-size: 13px; color: #777; margin: 0; } .pot-variation-info { display: block; margin-bottom: 4px; font-weight: 500; color: #444; } .pot-notes { background: #fff8e1; border-left: 4px solid var(--pot-theme-color); padding: 15px; border-radius: 4px; margin-top: 20px; } .pot-notes h4 { margin-top: 0; margin-bottom: 10px; color: #333; font-size: 15px; } .pot-note-single { font-size: 13px; color: #555; margin-bottom: 8px; border-bottom: 1px dashed #ddd; padding-bottom: 8px; } .pot-note-single:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; } .pot-total-amount { margin-top: 20px; text-align: right; font-size: 18px; font-weight: bold; color: var(--pot-theme-color); border-top: 2px dashed #eee; padding-top: 15px; } /* Responsive CSS for Mobile */ @media (max-width: 600px) { .pot-order-summary { flex-direction: column; align-items: flex-start; gap: 15px; } .pot-summary-right { width: 100%; justify-content: space-between; gap: 10px; } .pot-details-grid { grid-template-columns: 1fr; } .pot-view-btn { padding: 6px 15px; font-size: 13px; margin-top: 0; } } </style> <div class="pot-container"> <!-- Tracking Form --> <div class="pot-form-box"> <h3>Track Your Order</h3> <form method="POST" action=""> <?php if ( $method === 'id_contact' ) : ?> <div class="pot-form-group"> <label>Order ID</label> <input type="text" name="pot_order_id" placeholder="অর্ডার আইডি দিন (যেমন: 10254)" required> </div> <div class="pot-form-group"> <label>Email or Phone Number</label> <input type="text" name="pot_contact" placeholder="অর্ডারের ইমেইল অথবা ফোন নাম্বার দিন" required> </div> <?php else : ?> <div class="pot-form-group"> <label>Email or Phone Number</label> <input type="text" name="pot_contact" placeholder="আপনার ইমেইল অথবা ফোন নাম্বার দিন" required> </div> <?php endif; ?> <input type="hidden" name="pot_action" value="track_order"> <button type="submit" class="pot-btn">Track Order</button> </form> </div> <!-- Processing Logic & Output --> <?php if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_POST['pot_action'] ) && $_POST['pot_action'] === 'track_order' ) { $orders = []; $error_msg = ''; $contact = sanitize_text_field( wp_unslash( $_POST['pot_contact'] ) ); $contact = trim( $contact ); if ( $method === 'id_contact' ) { $order_id = sanitize_text_field( wp_unslash( $_POST['pot_order_id'] ) ); $order_id = str_replace( '#', '', trim( $order_id ) ); $order = wc_get_order( $order_id ); if ( $order ) { $billing_email = $order->get_billing_email(); $billing_phone = $order->get_billing_phone(); $clean_db_phone = str_replace( array(' ', '-', '+'), '', $billing_phone ); $clean_input_contact = str_replace( array(' ', '-', '+'), '', $contact ); if ( strcasecmp( $billing_email, $contact ) == 0 || ( !empty($clean_input_contact) && strpos( $clean_db_phone, $clean_input_contact ) !== false ) ) { $orders[] = $order; } else { $error_msg = "এই অর্ডার আইডির সাথে ইমেইল বা ফোন নাম্বারটি মিলছে না।"; } } else { $error_msg = "ভুল অর্ডার আইডি। অনুগ্রহ করে সঠিক অর্ডার আইডি দিন।"; } } else { $args = array( 'limit' => -1, 'orderby' => 'date', 'order' => 'DESC', ); if ( is_email( $contact ) ) { $args['billing_email'] = $contact; $orders = wc_get_orders( $args ); if ( empty( $orders ) ) { unset($args['billing_email']); $args['meta_key'] = '_billing_email'; $args['meta_value'] = $contact; $orders = wc_get_orders( $args ); } } else { $args['billing_phone'] = $contact; $orders = wc_get_orders( $args ); if ( empty( $orders ) ) { unset($args['billing_phone']); $args['meta_key'] = '_billing_phone'; $args['meta_value'] = '%' . $contact . '%'; $args['meta_compare'] = 'LIKE'; $orders = wc_get_orders( $args ); } } if ( empty( $orders ) ) { $error_msg = "দুঃখিত, চেকআউটের সময় দেওয়া এই ইমেইল বা ফোন নাম্বারের কোনো অর্ডার পাওয়া যায়নি।"; } } // Display Results if ( ! empty( $error_msg ) ) { echo '<div class="pot-error">' . esc_html( $error_msg ) . '</div>'; } elseif ( ! empty( $orders ) ) { echo '<h4 style="margin-bottom: 20px; color: #333;">আপনার অর্ডার সমূহ (' . count($orders) . ' টি পাওয়া গেছে):</h4>'; foreach ( $orders as $order ) { $order_id = $order->get_id(); $status_slug = $order->get_status(); $status_name = wc_get_order_status_name( $status_slug ); $allowed_statuses = ['completed', 'processing', 'on-hold', 'cancelled', 'failed', 'refunded', 'pending']; $status_class = in_array($status_slug, $allowed_statuses) ? 'status-' . $status_slug : 'status-default'; $customer_name = $order->get_formatted_billing_full_name(); $address = $order->get_formatted_shipping_address() ? $order->get_formatted_shipping_address() : $order->get_formatted_billing_address(); $order_date = date_i18n( get_option( 'date_format' ), strtotime( $order->get_date_created() ) ); $notes = wc_get_order_notes( array( 'order_id' => $order_id, 'type' => 'customer', )); ?> <div class="pot-order-card"> <!-- Summary (List View) --> <div class="pot-order-summary"> <div class="pot-summary-left"> <h4>Order #<?php echo $order->get_order_number(); ?></h4> <p>Placed on: <?php echo esc_html( $order_date ); ?> | Total: <?php echo wp_kses_post( $order->get_formatted_order_total() ); ?></p> </div> <div class="pot-summary-right"> <span class="pot-status-badge <?php echo esc_attr( $status_class ); ?>"> <?php echo esc_html( $status_name ); ?> </span> <button type="button" class="pot-view-btn" onclick="togglePotDetails('<?php echo $order_id; ?>')">View Details</button> </div> </div> <!-- Hidden Details View --> <div class="pot-order-details" id="pot-details-<?php echo $order_id; ?>"> <div class="pot-details-grid"> <div class="pot-detail-item"> <strong>Customer Details:</strong> <p><?php echo esc_html( $customer_name ); ?></p> <p><?php echo esc_html( $order->get_billing_phone() ); ?></p> <p><?php echo esc_html( $order->get_billing_email() ); ?></p> </div> <div class="pot-detail-item"> <strong>Shipping Address:</strong> <p><?php echo wp_kses_post( $address ); ?></p> </div> </div> <!-- Products List --> <div class="pot-products"> <strong>Order Items:</strong> <?php foreach ( $order->get_items() as $item_id => $item ) : $product = $item->get_product(); $image_url = wc_placeholder_img_src(); $product_title = $item->get_name(); if ( $product ) { // Product title (Parent name is fetched to avoid duplicate variable names) $product_title = $product->get_title(); // Image Logic (Variation image fallback to Parent image) $image_id = $product->get_image_id(); if ( ! $image_id && $product->is_type( 'variation' ) ) { $image_id = get_post_thumbnail_id( $product->get_parent_id() ); } if ( $image_id ) { $image_url = wp_get_attachment_image_url( $image_id, 'thumbnail' ); } } // Variation / Meta Details Extraction $meta_data = $item->get_formatted_meta_data( '_', true ); $meta_html = ''; if ( ! empty( $meta_data ) ) { $meta_strings = array(); foreach ( $meta_data as $meta ) { $meta_strings[] = wp_strip_all_tags( $meta->display_key ) . ': ' . wp_strip_all_tags( $meta->display_value ); } $meta_html = implode( ' | ', $meta_strings ); } ?> <div class="pot-product-item"> <div class="pot-product-img"> <img src="<?php echo esc_url( $image_url ); ?>" alt="Product"> </div> <div class="pot-product-info"> <h4 class="pot-product-title"><?php echo esc_html( $product_title ); ?></h4> <p class="pot-product-meta"> <?php if ( ! empty( $meta_html ) ) : ?> <span class="pot-variation-info"><?php echo esc_html( $meta_html ); ?></span> <?php endif; ?> Qty: <?php echo esc_html( $item->get_quantity() ); ?> | Price: <?php echo wp_kses_post( wc_price( $item->get_total() ) ); ?> </p> </div> </div> <?php endforeach; ?> </div> <!-- Order Notes --> <?php if ( ! empty( $notes ) ) : ?> <div class="pot-notes"> <h4>Order Updates / Notes:</h4> <?php foreach ( $notes as $note ) : ?> <div class="pot-note-single"> <strong><?php echo date_i18n( wc_date_format(), strtotime( $note->date_created ) ); ?>:</strong> <?php echo wp_kses_post( $note->content ); ?> </div> <?php endforeach; ?> </div> <?php endif; ?> <div class="pot-total-amount"> Grand Total: <?php echo wp_kses_post( $order->get_formatted_order_total() ); ?> </div> </div> </div> <?php } } } ?> </div> <!-- JavaScript for Toggle Feature --> <script> function togglePotDetails(orderId) { var detailsDiv = document.getElementById('pot-details-' + orderId); if (detailsDiv.style.display === 'none' || detailsDiv.style.display === '') { detailsDiv.style.display = 'block'; } else { detailsDiv.style.display = 'none'; } } </script> <?php return ob_get_clean(); }