WOO – Tạo khung đăng nhập popup

Eric (kedochanh)
0
23/10/26
23/10/2026

Nguồn chỉnh sửa

https://www.google.com/search?q=Code+hi%E1%BB%83n+th%E1%BB%8B+s%E1%BB%91+%C4%91i%E1%BB%87n+tho%E1%BA%A1i+kh%C3%A1ch+h%C3%A0ng+trong+woocommerce+%3D%3E+customer+trong+wp-admin&sca_esv=81ee190d3896ba19&rlz=1C1YTUH_viVN1001VN1001&sxsrf=APpeQnsUSALQoHxoAPvGjah7nhRfjl_cwA%3A1792476475481&source=chrome.ob&fbs=ABfTbFVyMZGZf1hfvX9uKjN_-G8cxpBkeIeqYwoCbfNVc4vKE9LbDnU6KQGy7t-K8nDAEfyjbsqYMIS6pkSA1Hjj7Nr9j3KkRcomOAbfl8OIcMY5G0hcFJwwDMesy7aR4ZhachrzHuCJOKY-UXFROayMonzBkES_oApz4i19mA0un_5jp9pBetQC5d3uqkuWiXdoO3yumldP-xmODnbd4v3i3-GyhBgdRA&vsint=&aep=1&ntc=1&cs=0&sa=X&ved=2ahUKEwiY_Nqc98eXAxXG3TgGHVIeJbIQ2J8OegQIEhAD&biw=1280&bih=559&dpr=1.5&mstk=AUtExfCBabZaz7kP8cBo9utqHF2CdoeMfHVpJZmfj0-lrQsJwVafd2N8hs9QQOY82hQVAikvsIyYXmP94fyJoF1X89uOjINZUbhKLv2kL3dGcpuDxW75WqznOY484hO1uS7C-6cLOUNQf0jQoxuEjq5smv_-qBUBrnXB6lLDqlMMyiVKLbycQnm2CMQ17AohOSykvWBSU367EsHBsaln42E6Bwyun7LVSrzORec_OqzFduLlgjDrKkDk8688KcqB1cJSubN0pMJyWhp2L3OkM0_GEla00TeRQkjDgRhNiJYUgeAgYoZBowGU_92oaDBYQ5USYkoJH1Pt7rQ3sg&csuir=1&mtid=PQXXapDaGPK_juMP4vLTkQE&lns_mode=cvst&udm=50
// =========================================================================
// TẠO KHUNG ĐĂNG NHẬP BẰNG POPUP MỚI (HTML + CSS + JS)
// Nguồn chỉnh sửa: vuvo.top => WOO - Tạo khung đăng nhập popup
// =========================================================================
function custom_auth_popup_core_html() {
    global $wp;
    $current_url = home_url( add_query_arg( array(), $wp->request ) );
    ?>
    
 <div class="auth-menu-wrapper">
    <?php if ( ! is_user_logged_in() ) : ?>
        
        <!-- Giao diện CHỈ HIỂN THỊ KHI CHƯA ĐĂNG NHẬP -->
        <!-- Bấm vào thẻ A này sẽ kích hoạt script mở Popup -->
        <a href="#authPopup" class="open-auth-popup data-auth-trigger" id="openAuthPopupLink">
            Đăng nhập / Đăng ký
        </a>

        <!-- Nút kích hoạt dự phòng (Ẩn đi nếu bạn chỉ dùng thẻ A phía trên) -->
        <button id="openAuthPopup" class="btn-trigger-auth data-auth-trigger" style="display:none;">
            Đăng nhập / Đăng ký
        </button>

        <!-- Khung Popup chính -->
        <div id="authPopupModal" class="auth-popup-overlay" style="display:none;">
            <div class="auth-popup-container">
                <span class="auth-popup-close" id="closeAuthPopup">&times;</span>

                <!-- 1. KHUNG ĐĂNG NHẬP -->
                <div id="loginFormArea" class="auth-form-wrapper active">
                    <h2>Đăng nhập hệ thống</h2>
                    <div id="loginPopupErrors" style="color:red; font-size:13px; margin-bottom:10px; text-align:center;"></div>
                    
                    <form method="post" id="mainLoginForm">
                        <div class="input-group">
                            <label for="login_user">Số điện thoại hoặc Email</label>
                            <input type="text" id="login_user" name="username" placeholder="Nhập số điện thoại hoặc email..." required>
                        </div>
                        <div class="input-group">
                            <label for="login_pass">Mật khẩu</label>
                            <div class="password-wrapper">
                                <input type="password" id="login_pass" name="password" placeholder="Nhập mật khẩu..." required>
                                <button type="button" class="toggle-password-visibility" tabindex="-1">
                                    <svg xmlns="http://w3.org" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eye" aria-hidden="true"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0z"></path><circle cx="12" cy="12" r="3"></circle></svg>
                                </button>
                            </div>
                        </div>
                        
                       <div class="auth-addon-link">
                            <!-- SỬA TẠI ĐÂY: Chuyển đổi thành nút bấm kích hoạt AJAX tab con -->
                            <a href="#lost-password" id="openLostPassForm" class="lost-pass-btn">Quên mật khẩu?</a>
                        </div>
                        <?php wp_nonce_field( 'woocommerce-login', 'woocommerce-login-nonce' ); ?>
                        <button type="submit" class="auth-submit-btn" name="login">Đăng nhập ngay</button>
                    </form>
                </div>

                <!-- 2. KHUNG ĐĂNG KÝ -->
                <div id="registerFormArea" class="auth-form-wrapper">
                    <h2>Tạo tài khoản mới</h2>
                    <div id="regPopupErrors" style="font-size:13px; margin-bottom:10px; text-align:center;"></div>
                    
                    <form method="post" id="mainRegisterForm">
                        <div class="input-group">
                            <label for="reg_user">Tên đăng nhập</label>
                            <input type="text" id="reg_user" name="reg_user" placeholder="Ví dụ: nguyenvana" required>
                        </div>
                        <div class="input-group">
                            <label for="reg_phone">Số điện thoại</label>
                            <input type="tel" id="reg_phone" name="reg_phone" placeholder="Ví dụ: 0912345678" required>
                        </div>
                        <div class="input-group">
                            <label for="reg_email">Địa chỉ Email</label>
                            <input type="email" id="reg_email" name="reg_email" placeholder="Ví dụ: email@domain.com" required>
                        </div>
                        <div class="input-group">
                            <label for="reg_pass">Mật khẩu</label>
                            <div class="password-wrapper">
                                <input type="password" id="reg_pass" name="reg_pass" placeholder="Tối thiểu 6 số hoặc chữ..." required>
                                <button type="button" class="toggle-password-visibility" tabindex="-1">
                                    <svg xmlns="http://w3.org" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eye" aria-hidden="true"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0z"></path><circle cx="12" cy="12" r="3"></circle></svg>
                                </button>
                            </div>
                        </div>
                        <div class="input-group">
                            <label for="reg_pass_confirm">Nhập lại mật khẩu</label>
                            <div class="password-wrapper">
                                <input type="password" id="reg_pass_confirm" name="reg_pass_confirm" placeholder="Xác nhận lại mật khẩu..." required>
                                <button type="button" class="toggle-password-visibility" tabindex="-1">
                                    <svg xmlns="http://w3.org" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eye" aria-hidden="true"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0z"></path><circle cx="12" cy="12" r="3"></circle></svg>
                                </button>
                            </div>
                        </div>
                        <button type="submit" class="auth-submit-btn">Đăng ký ngay</button>
                    </form>
                </div>

              <!-- 3. KHUNG KHÔI PHỤC MẬT KHẨU QUA AJAX -->
<div id="lostPasswordFormArea" class="auth-form-wrapper">
    <h2>Khôi phục mật khẩu</h2>
    <p style="font-size: 13px; color: #666; margin-bottom: 15px; text-align: center;">
        Vui lòng nhập Email. Hệ thống sẽ gửi một liên kết đổi mật khẩu qua Email của bạn.
    </p>
    
    <!-- Nơi hiển thị thông báo lỗi hoặc thành công -->
    <div id="lostPopupErrors" style="font-size:13px; margin-bottom:15px; text-align:center; font-weight: 500;"></div>
    
    <form method="post" id="mainLostPasswordForm">
        <div class="input-group">
            <label for="lost_user">Địa chỉ Email</label>
            <input type="text" id="lost_user" name="user_login" placeholder="Nhập tên đăng nhập hoặc email..." required>
        </div>
        
        <button type="submit" class="auth-submit-btn">Gửi liên kết khôi phục</button>
        
        <!-- Nút quay lại tab đăng nhập nhanh -->
        <div style="text-align: center; margin-top: 15px;">
            <a href="#login" id="backToLoginFromLost" style="font-size: 13px; color: #0073aa; text-decoration: none;">← Quay lại Đăng nhập</a>
        </div>
    </form>
</div>


                <!-- THANH ĐIỀU HƯỚNG DƯỚI CÙNG -->
                <div class="auth-popup-footer">
                    <div class="or-separator">— Hoặc —</div>
                    <button id="toggleAuthBtn" class="toggle-switch-btn">Chưa có tài khoản? Đăng ký ngay</button>
                </div>
            </div>
        </div>
    <?php endif; ?>
</div>

    <!-- STYLE CSS ĐÃ TỐI ƯU CHIỀU CAO RESPONSIVE -->
    <style>
        .btn-trigger-auth { padding: 10px 20px; background-color: #446084; color: #fff; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; text-decoration: none; display: inline-block; }
        .btn-trigger-auth:hover { background-color: #334963; color: #fff; }
        .auth-popup-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); z-index: 999999; justify-content: center; align-items: center; padding: 15px; box-sizing: border-box; }
        .auth-popup-container { background-color: #fff; width: 100%; max-width: 420px; max-height: calc(100vh - 40px); overflow-y: auto; padding: 30px 25px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); position: relative; box-sizing: border-box; font-family: sans-serif; scrollbar-width: thin; }
        .auth-popup-close { position: absolute; top: 10px; right: 15px; font-size: 24px; cursor: pointer; color: #aaa; line-height: 1; z-index: 10; }
        .auth-popup-close:hover { color: #333; }
        .auth-form-wrapper { display: none; }
        .auth-form-wrapper.active { display: block; }
        .auth-form-wrapper h2 { font-size: 22px; margin-top: 0; margin-bottom: 20px; color: #333; text-align: center; font-weight: 700; }
        .input-group { margin-bottom: 12px; display: flex; flex-direction: column; }
        .input-group label { font-size: 13px; margin-bottom: 5px; color: #444; text-align: left; font-weight: 600; }
        .input-group input { padding: 10px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; width: 100%; box-sizing: border-box; height: auto !important; }
        .auth-addon-link { text-align: right; margin-top: -5px; margin-bottom: 12px; }
        .lost-pass-btn { font-size: 13px; color: #446084; text-decoration: none; }
        .auth-submit-btn { width: 100%; padding: 11px; background-color: #446084; color: white; border: none; border-radius: 4px; font-size: 15px; font-weight: bold; cursor: pointer; margin-top: 5px; }
        .auth-submit-btn:hover { background-color: #334963; }
        .auth-popup-footer { margin-top: 15px; text-align: center; }
        .or-separator { font-size: 13px; color: #aaa; margin-bottom: 10px; }
        .toggle-switch-btn { background: none; border: 1px solid #446084; color: #446084; padding: 10px 20px; border-radius: 4px; font-size: 14px; font-weight: 600; cursor: pointer; width: 100%; }
        .toggle-switch-btn:hover { background-color: #f4f9fc; }
        .password-wrapper { position: relative; width: 100%; }
        .password-wrapper input { padding-right: 45px !important; }
        .toggle-password-visibility { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); background: none !important; border: none !important; padding: 0 !important; margin: 0 !important; cursor: pointer; color: #888; display: flex; align-items: center; justify-content: center; box-shadow: none !important; height: auto !important; }
        .toggle-password-visibility:hover { color: #333; }
        .toggle-password-visibility svg { display: block; pointer-events: none; }
        .input-group input.input-error { border-color: #ff4d4d !important; background-color: #fff6f6 !important; box-shadow: 0 0 5px rgba(255, 77, 77, 0.3) !important; animation: shakeError 0.3s ease-in-out; }
        @keyframes shakeError { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-6px); } 75% { transform: translateX(6px); } }
        @media (max-width: 480px) { .auth-popup-container { padding: 20px 15px; } .auth-form-wrapper h2 { font-size: 18px; margin-bottom: 15px; } .input-group input { padding: 8px; font-size: 13px; } }
    </style>


     <!-- JAVASCRIPT EXECUTION WITH AJAX -->
    <script>
 document.addEventListener("DOMContentLoaded", function () {
    const modal = document.getElementById("authPopupModal");
    const openBtn = document.getElementById("openAuthPopup");
    const closeBtn = document.getElementById("closeAuthPopup");
    const toggleAuthBtn = document.getElementById("toggleAuthBtn");
    const loginFormArea = document.getElementById("loginFormArea");
    const registerFormArea = document.getElementById("registerFormArea");
    const lostPasswordFormArea = document.getElementById("lostPasswordFormArea");
    const openLostPassForm = document.getElementById("openLostPassForm");

    if (!modal) return;

    // --- HÀM MỞ POPUP CHUNG ---
    function openAuthPopup() {
        modal.style.display = "flex";
    }

    // --- KÍCH HOẠT MỞ POPUP TỪ TẤT CẢ PHẦN TỬ ĐƯỢC CHỈ ĐỊNH ---
    if (openBtn) { 
        openBtn.onclick = function(e) { e.preventDefault(); openAuthPopup(); }; 
    }
    
    document.addEventListener('click', function(e) {
        const targetAnchor = e.target.closest('a[href="#authPopup"], a.open-auth-popup, a[href*="show_auth=1"], #openAuthPopup, .btn-trigger-auth');
        if (targetAnchor) {
            e.preventDefault();
            openAuthPopup();
        }
    });

    // --- TỰ ĐỘNG BẬT POPUP KHI BỊ CHUYỂN HƯỚNG TỪ TRANG TÀI KHOẢN (?show_auth=1) ---
    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.has('show_auth')) {
        openAuthPopup();
        if (window.history && window.history.replaceState) {
            const cleanUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
            window.history.replaceState({}, document.title, cleanUrl);
        }
    }

    // Đóng Popup khi bấm nút X hoặc bấm ra ngoài vùng màn hình popup
    if (closeBtn) { 
        closeBtn.onclick = function() { 
            modal.style.display = "none"; 
            resetToLoginTab(); // Reset trạng thái về tab Đăng nhập khi đóng popup
        }; 
    }
    window.onclick = function(e) { 
        if (e.target === modal) { 
            modal.style.display = "none"; 
            resetToLoginTab();
        } 
    };

    // Hàm đưa popup về tab Đăng nhập ban đầu
    function resetToLoginTab() {
        if(loginFormArea) loginFormArea.classList.add("active");
        if(registerFormArea) registerFormArea.classList.remove("active");
        if(lostPasswordFormArea) lostPasswordFormArea.classList.remove("active");
        if(toggleAuthBtn) {
            toggleAuthBtn.style.display = "block";
            toggleAuthBtn.innerText = "Chưa có tài khoản? Đăng ký ngay";
        }
    }

    // Chuyển đổi qua lại giữa tab Đăng nhập / Đăng ký
    if (toggleAuthBtn) {
        toggleAuthBtn.onclick = function(e) {
            e.preventDefault();
            lostPasswordFormArea.classList.remove("active");
            if (loginFormArea.classList.contains("active")) {
                loginFormArea.classList.remove("active");
                registerFormArea.classList.add("active");
                toggleAuthBtn.innerText = "Đã có tài khoản? Đăng nhập ngay";
            } else {
                registerFormArea.classList.remove("active");
                loginFormArea.classList.add("active");
                toggleAuthBtn.innerText = "Chưa có tài khoản? Đăng ký ngay";
            }
        };
    }


    // Xóa viền đỏ cảnh báo khi người dùng bắt đầu gõ lại dữ liệu vào các ô input
    const inputs = document.querySelectorAll('.input-group input');
    inputs.forEach(input => { input.addEventListener('input', function() { this.classList.remove('input-error'); }); });

    // Bật/tắt hiển thị mật khẩu bằng SVG Eye / Eye-off (Giữ nguyên của bạn)
    const toggleButtons = document.querySelectorAll('.toggle-password-visibility');
    toggleButtons.forEach(btn => {
        btn.addEventListener('click', function(e) {
            e.preventDefault();
            const inputField = this.previousElementSibling;
            if (inputField) {
                if (inputField.type === 'password') {
                    inputField.type = 'text';
                    this.innerHTML = '<svg xmlns="http://w3.org" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eye-off" aria-hidden="true"><path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"></path><path d="M14.084 14.158a3 3 0 0 1-4.242-4.242"></path><path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"></path><path d="m2 2 20 20"></path></svg>';
                } else {
                    inputField.type = 'password';
                    this.innerHTML = '<svg xmlns="http://w3.org" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eye" aria-hidden="true"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0z"></path><circle cx="12" cy="12" r="3"></circle></svg>';
                }
            }
        });
    });

// 1. XỬ LÝ AJAX ĐĂNG NHẬP (BẢN ĐÃ SỬA LỖI ĐƯỜNG DẪN FETCH)
const logForm = document.getElementById("mainLoginForm");
if (logForm) {
    logForm.onsubmit = function(e) {
        e.preventDefault();
        const msgDiv = document.getElementById("loginPopupErrors");
        const userField = document.getElementById("login_user");
        const passField = document.getElementById("login_pass");

        msgDiv.style.color = "#333";
        msgDiv.innerText = "Đang kiểm tra tài khoản...";
        
        userField.classList.remove('input-error');
        passField.classList.remove('input-error');

        const formData = new FormData(logForm);
        formData.append('action', 'custom_ajax_login');

        // SỬA TẠI ĐÂY: Dùng đường dẫn động an toàn chạy được cả trong file .js và file .php
        const ajaxUrl = window.wp_data ? window.wp_data.ajax_url : '/wp-admin/admin-ajax.php';

        fetch(ajaxUrl, {
            method: 'POST',
            body: formData
        })
        .then(response => {
            if (!response.ok) {
                throw new Error("HTTP error " + response.status);
            }
            return response.json();
        })
        .then(data => {
            if (data.success) {
                msgDiv.style.color = "green";
                msgDiv.innerText = data.data.message;
                setTimeout(() => { window.location.reload(); }, 800);
            } else {
                msgDiv.style.color = "red";
                msgDiv.innerText = data.data.message;
                
                // CẢI TIẾN: Chỉ tô đỏ ô bị lỗi dựa theo mã trả về từ code PHP mới
                if (data.data && (data.data.code === 'invalid_username' || data.data.code === 'invalid_email')) {
                    userField.classList.add('input-error');
                } else if (data.data && data.data.code === 'incorrect_password') {
                    passField.classList.add('input-error');
                } else {
                    userField.classList.add('input-error');
                    passField.classList.add('input-error');
                }
            }
        })
        .catch(error => {
            msgDiv.style.color = "red";
            msgDiv.innerText = "Có lỗi xảy ra khi kết nối hệ thống.";
            console.error("Chi tiết lỗi kết nối:", error);
        });
    };
}
           
// 2. XỬ LÝ AJAX ĐĂNG KÝ (CẢI TIẾN KIỂM TRA SỐ ĐIỆN THOẠI VN)
const regForm = document.getElementById("mainRegisterForm");
if (regForm) {
    regForm.onsubmit = function(e) {
        e.preventDefault();
        const msgDiv = document.getElementById("regPopupErrors");
        const phoneField = document.getElementById("reg_phone"); // Đảm bảo ô input số điện thoại có id="reg_phone"
        const passField = document.getElementById("reg_pass");
        const confirmField = document.getElementById("reg_pass_confirm");
        
        // Reset trạng thái lỗi viền đỏ
        if(phoneField) phoneField.classList.remove('input-error');
        passField.classList.remove('input-error');
        confirmField.classList.remove('input-error');
        
        // 1. Kiểm tra định dạng số điện thoại Việt Nam ở Frontend
        if (phoneField) {
            const phoneValue = phoneField.value.trim();
            // Regex chuẩn cho các đầu số di động tại Việt Nam hiện nay (10 số)
            const vnf_regex = /^(03|05|07|08|09)+([0-9]{8})$/i;
            
            if (phoneValue === '') {
                msgDiv.style.color = "red";
                msgDiv.innerText = "Vui lòng nhập số điện thoại!";
                phoneField.classList.add('input-error');
                return;
            } else if (vnf_regex.test(phoneValue) === false) {
                msgDiv.style.color = "red";
                msgDiv.innerText = "Số điện thoại không đúng định dạng !";
                phoneField.classList.add('input-error');
                return;
            }
        }

        // 2. Kiểm tra độ dài mật khẩu
        if (passField.value.length < 6) {
            msgDiv.style.color = "red";
            msgDiv.innerText = "Mật khẩu phải chứa ít nhất 6 ký tự!";
            passField.classList.add('input-error');
            return;
        }

        // 3. Kiểm tra khớp mật khẩu
        if (passField.value !== confirmField.value) {
            msgDiv.style.color = "red";
            msgDiv.innerText = "Mật khẩu nhập lại không khớp!";
            confirmField.classList.add('input-error');
            return;
        }

        msgDiv.style.color = "#333";
        msgDiv.innerText = "Đang xử lý đăng ký...";

        const formData = new FormData(regForm);
        formData.append('action', 'custom_ajax_register_action');

        const ajaxUrl = window.wp_data ? window.wp_data.ajax_url : '/wp-admin/admin-ajax.php';

        fetch(ajaxUrl, {
            method: 'POST',
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                msgDiv.style.color = "green";
                msgDiv.innerText = data.data.message;
                setTimeout(() => { window.location.reload(); }, 1000);
            } else {
                msgDiv.style.color = "red";
                msgDiv.innerText = data.data.message;
                
                // Tô đỏ ô tương ứng dựa theo mã lỗi backend trả về
                if (data.data && data.data.code === 'phone_exists') {
                    if(phoneField) phoneField.classList.add('input-error');
                }
            }
        })
        .catch(error => {
            msgDiv.style.color = "red";
            msgDiv.innerText = "Có lỗi xảy ra, thử lại sau.";
        });
    };
}
	 
// 3. XỬ LÝ AJAX GỬI LINK QUÊN MẬT KHẨU (BẢN CHUẨN HÓA BẢO MẬT)
  // Định nghĩa đường dẫn AJAX an toàn của WordPress
    const ajaxUrl = window.wp_data ? window.wp_data.ajax_url : '/wp-admin/admin-ajax.php';

    //CHUYỂN SANG TAB QUÊN MẬT KHẨU KHI CLICK LINK
    if (openLostPassForm) {
        openLostPassForm.onclick = function (e) {
            e.preventDefault();
            if (loginFormArea) loginFormArea.classList.remove("active");
            if (lostPasswordFormArea) lostPasswordFormArea.classList.add("active");
            if (toggleAuthBtn) toggleAuthBtn.style.display = "none"; // Ẩn nút Đăng ký ở footer đi
        };
    }

    //QUAY LẠI TAB ĐĂNG NHẬP KHI ẤN "QUAY LẠI"
    if (backToLoginFromLost) {
        backToLoginFromLost.onclick = function (e) {
            e.preventDefault();
            if (lostPasswordFormArea) lostPasswordFormArea.classList.remove("active");
            if (loginFormArea) loginFormArea.classList.add("active");
            if (toggleAuthBtn) toggleAuthBtn.style.display = "block"; // Hiện lại nút đăng ký ở footer
            
            // Xóa sạch thông báo cũ khi quay về
            const lostMsgDiv = document.getElementById("lostPopupErrors");
            if (lostMsgDiv) { lostMsgDiv.innerText = ""; }
        };
    }

    //XỬ LÝ AJAX GỬI LINK QUÊN MẬT KHẨU
    const lostForm = document.getElementById("mainLostPasswordForm");
    if (lostForm) {
        lostForm.onsubmit = function (e) {
            e.preventDefault();

            const msgDiv = document.getElementById("lostPopupErrors");
            const lostUserField = document.getElementById("lost_user");

            if (!msgDiv || !lostUserField) return;

            // Trạng thái chờ xử lý ban đầu
            msgDiv.style.color = "#333";
            msgDiv.innerText = "Đang xử lý gửi yêu cầu...";
            lostUserField.classList.remove('input-error');

            const formData = new FormData(lostForm);
            formData.append('action', 'custom_ajax_lost_password');

            fetch(ajaxUrl, {
                method: 'POST',
                body: formData
            })
            .then(response => {
                if (!response.ok) {
                    throw new Error("Lỗi kết nối máy chủ (HTTP " + response.status + ")");
                }
                return response.json();
            })
            .then(data => {
                if (data.success) {
                    msgDiv.style.color = "green";
                    msgDiv.innerText = data.data.message;
                    lostForm.reset(); // Xóa sạch dữ liệu ô input vừa nhập thành công
                } else {
                    msgDiv.style.color = "red";
                    msgDiv.innerText = data.data ? data.data.message : "Yêu cầu khôi phục mật khẩu thất bại.";
                    lostUserField.classList.add('input-error'); // Tô đỏ ô nhập liệu khi có lỗi
                }
            })
            .catch(error => {
                msgDiv.style.color = "red";
                msgDiv.innerText = "Email bị sai hoặc không tồn tại.";
                console.error("Chi tiết lỗi quên mật khẩu:", error);
            });
        };
    }
        });
    </script>
    <?php
}

// =========================================================================
// 2. KHAI BÁO SHORTCODE CHÍNH (TỰ ĐỘNG ẨN KHI ĐÃ ĐĂNG NHẬP)
// =========================================================================
add_shortcode('popup_auth', 'custom_auth_popup_shortcode_v4');
function custom_auth_popup_shortcode_v4() {
    if ( is_user_logged_in() ) {
        return ''; 
    }
    ob_start();
    custom_auth_popup_core_html();
    return ob_get_clean();
}

// =========================================================================
// 3. BACKEND PHP: XỬ LÝ AJAX ĐĂNG KÝ (VAI TRÒ KHÁCH HÀNG + CHẶN TRÙNG SĐT CHUẨN VN)
// =========================================================================
add_action('wp_ajax_nopriv_custom_ajax_register_action', 'custom_ajax_register_handler_v4');
add_action('wp_ajax_custom_ajax_register_action', 'custom_ajax_register_handler_v4');

function custom_ajax_register_handler_v4() {
    $username = isset($_POST['reg_user']) ? sanitize_user($_POST['reg_user']) : '';
    $phone    = isset($_POST['reg_phone']) ? sanitize_text_field($_POST['reg_phone']) : '';
    $email    = isset($_POST['reg_email']) ? sanitize_email($_POST['reg_email']) : '';
    $password = isset($_POST['reg_pass']) ? $_POST['reg_pass'] : '';

    // 1. Kiểm tra rỗng các trường bắt buộc
    if (empty($username) || empty($phone) || empty($email) || empty($password)) {
        wp_send_json_error(array('code' => 'empty_fields', 'message' => 'Vui lòng điền đầy đủ các thông tin bắt buộc.'));
    }

    // 2. Kiểm tra độ dài mật khẩu
    if (strlen($password) < 6) {
        wp_send_json_error(array('code' => 'invalid_password', 'message' => 'Mật khẩu phải chứa ít nhất 6 ký tự.'));
    }

    // 3. Kiểm tra tính hợp lệ của Email
    if (!is_email($email)) {
        wp_send_json_error(array('code' => 'invalid_email', 'message' => 'Địa chỉ email không hợp lệ.'));
    }

    // 4.Kiểm tra định dạng số điện thoại chuẩn Việt Nam (10 số, bắt đầu bằng các đầu số di động hợp lệ)
    $phone = str_replace(array(' ', '.', '-', '+84'), array('', '', '', '0'), $phone);
    if (!preg_match('/^(03|05|07|08|09)+([0-9]{8})$/', $phone)) {
        wp_send_json_error(array('code' => 'invalid_phone', 'message' => 'Số điện thoại không hợp lệ.'));
    }

    // 5. Kiểm tra trùng lặp tài khoản và email
    if (username_exists($username)) {
        wp_send_json_error(array('code' => 'username_exists', 'message' => 'Tên đăng nhập này đã tồn tại.'));
    }
    if (email_exists($email)) {
        wp_send_json_error(array('code' => 'email_exists', 'message' => 'Email này đã được đăng ký.'));
    }

    // 6. Kiểm tra trùng lặp số điện thoại
    $phone_query = new WP_User_Query(array(
        'meta_key'   => 'billing_phone',
        'meta_value' => $phone,
        'number'     => 1,
        'fields'     => 'ID'
    ));
    $existing_phones = $phone_query->get_results();
    
    if (!empty($existing_phones)) {
        wp_send_json_error(array('code' => 'phone_exists', 'message' => 'Số điện thoại này đã được đăng ký.'));
    }

    // 7.Tiến hành tạo user hệ thống WordPress
    $user_id = wp_create_user($username, $password, $email);

    if (is_wp_error($user_id)) {
        wp_send_json_error(array('code' => 'create_failed', 'message' => $user_id->get_error_message()));
    }

    //8. Gán vai trò là Khách hàng (Customer) của WooCommerce
    $user_obj = new WP_User($user_id);
    $user_obj->set_role('customer');

    // Lưu meta trường Số điện thoại chuẩn hóa billing/shipping cho WooCommerce
    update_user_meta($user_id, 'billing_phone', $phone);
    update_user_meta($user_id, 'shipping_phone', $phone);

    // Tự động thiết lập cookie đăng nhập luôn tại trang hiện tại sau khi đăng ký
    wp_set_current_user($user_id);
    wp_set_auth_cookie($user_id, true);
    
    wp_send_json_success(array('message' => 'Đăng ký thành công! Đang tải lại trang...'));
}


// 4.BACKEND PHP: XỬ LÝ AJAX ĐĂNG NHẬP (HỖ TRỢ USERNAME, EMAIL, SỐ ĐIỆN THOẠI)
add_action('wp_ajax_nopriv_custom_ajax_login', 'devvn_custom_ajax_login_handler');
add_action('wp_ajax_custom_ajax_login', 'devvn_custom_ajax_login_handler');

function devvn_custom_ajax_login_handler() {
    // 1. Kiểm tra dữ liệu đầu vào
    $username_input = isset($_POST['log']) ? sanitize_text_field($_POST['log']) : (isset($_POST['username']) ? sanitize_text_field($_POST['username']) : '');
    $password       = isset($_POST['pwd']) ? $_POST['pwd'] : (isset($_POST['password']) ? $_POST['password'] : '');
    $remember       = isset($_POST['rememberme']) ? true : false;
    
    // Kiểm tra các trường rỗng
    if (empty($username_input) || empty($password)) {
        wp_send_json_error(array(
            'code'    => 'empty_fields',
            'message' => 'Vui lòng điền đầy đủ tài khoản và mật khẩu.'
        ));
    }

    // Mặc định gán biến final bằng giá trị nhập vào
    $final_username = $username_input;

    // TRƯỜNG HỢP 1: NGƯỜI DÙNG ĐĂNG NHẬP BẰNG EMAIL
    if ( is_email($username_input) ) {
        $user_obj = get_user_by('email', $username_input);
        if ( $user_obj ) {
            $final_username = $user_obj->user_login;
        } else {
            wp_send_json_error(array(
                'code'    => 'invalid_email',
                'message' => 'Địa chỉ Email này chưa được đăng ký tài khoản.'
            ));
        }
    }
    // TRƯỜNG HỢP 2: NGƯỜI DÙNG ĐĂNG NHẬP BẰNG SỐ ĐIỆN THOẠI WOOCOMMERCE
    // Nếu chuỗi nhập vào chỉ chứa ký tự số và dài từ 9 đến 11 ký tự
    elseif (preg_match('/^[0-9]{9,11}$/', $username_input)) {
        $user_query = new WP_User_Query(array(
            'meta_key'   => 'billing_phone',
            'meta_value' => $username_input,
            'number'     => 1,
            'fields'     => 'all'
        ));
        $users = $user_query->get_results();

        if (!empty($users)) {
            // Chuyển đổi Số điện thoại thành Username (user_login) thật trong database để WordPress nhận diện
            $final_username = $users[0]->user_login;
        } else {
            // Trả về lỗi định dạng để JavaScript tô đỏ ngay ô Tài khoản/Số điện thoại
            wp_send_json_error(array(
                'code'    => 'invalid_username',
                'message' => 'Số điện thoại này chưa được đăng ký tài khoản.'
            ));
        }
    }

    // Cấu hình thông tin gửi vào bộ xác thực mặc định của WordPress
    $creds = array(
        'user_login'    => $final_username,
        'user_password' => $password,
        'remember'      => $remember
    );

    // Tiến hành đăng nhập ngầm bảo mật
    $user = wp_signon($creds, false);

    // 3. Trả về kết quả khớp với Logic JavaScript
    if (is_wp_error($user)) {
        $error_code = $user->get_error_code();
        $error_msg  = 'Số điện thoại/email hoặc mật khẩu không chính xác.';

        // Tùy biến thông báo cụ thể bảo mật/thân thiện
        if ($error_code === 'invalid_username' || $error_code === 'invalid_email') {
            $error_msg = 'Số điện thoại và email không tồn tại.';
        } elseif ($error_code === 'incorrect_password') {
            $error_msg = 'Mật khẩu bạn nhập chưa đúng. Vui lòng thử lại.';
        }

        wp_send_json_error(array(
            'code'    => $error_code,
            'message' => $error_msg
        ));
    }

    wp_send_json_success(array(
        'message' => 'Đăng nhập thành công! Đang chuyển hướng...'
    ));
}

// =========================================================================
// 7. BACKEND PHP: CHỈ CHUYỂN HƯỚNG KHI VÀO TRANG MY ACCOUNT MẶC ĐỊNH CỦA WOOCOMMERCE
// =========================================================================
add_action( 'template_redirect', 'devvn_redirect_only_my_account_page' );
function devvn_redirect_only_my_account_page() {
    // CHỈ CHUYỂN HƯỚNG khi truy cập đúng trang tài khoản WooCommerce và người dùng CHƯA đăng nhập
    if ( is_account_page() && ! is_user_logged_in() ) {
        // Lưu lại đường dẫn trang tài khoản khách muốn vào để sau khi đăng nhập xong chuyển hướng ngược lại
        $redirect_url = add_query_arg( 'show_auth', '1', home_url( '/' ) );
        wp_safe_redirect( $redirect_url );
        exit;
    }
}

// Chuyển đổi toàn bộ link "Tài khoản của tôi" mặc định trên hệ thống thành hashtag gọi Popup
add_filter( 'woocommerce_get_myaccount_page_permalink', 'devvn_custom_myaccount_to_popup_link', 999 );
function devvn_custom_myaccount_to_popup_link( $permalink ) {
    if ( ! is_user_logged_in() ) {
        // Nếu chưa đăng nhập, ép link tài khoản chuyển về hashtag để JS kích hoạt popup trực tiếp
        return '#authPopup';
    }
    return $permalink;
}

// =========================================================================
// 8. BACKEND PHP: XỬ LÝ AJAX GỬI LINK KHÔI PHỤC MẬT KHẨU WOOCOMMERCE
// =========================================================================
add_action('wp_ajax_nopriv_custom_ajax_lost_password', 'devvn_custom_ajax_lost_password_handler');
add_action('wp_ajax_custom_ajax_lost_password', 'devvn_custom_ajax_lost_password_handler');

function devvn_custom_ajax_lost_password_handler() {
    // Nhận diện dữ liệu nhập vào (có thể là username hoặc email)
    $user_login = isset($_POST['user_login']) ? sanitize_text_field(trim($_POST['user_login'])) : '';

    if (empty($user_login)) {
        wp_send_json_error(array('message' => 'Vui lòng nhập tên tài khoản hoặc địa chỉ email.'));
    }

    // ƯU TIÊN 1: Gọi hàm xử lý lấy lại mật khẩu lõi của WooCommerce (Gửi email template HTML đẹp mắt)
    if ( class_exists('WC_Shortcode_My_Account') && method_exists('WC_Shortcode_My_Account', 'retrieve_password') ) {
        $_POST['user_login'] = $user_login;
        $result = WC_Shortcode_My_Account::retrieve_password();
        
        if ( $result === true ) {
            wp_send_json_success(array('message' => 'Liên kết đặt lại mật khẩu đã được gửi thành công. Vui lòng kiểm tra hộp thư Email của bạn.'));
        } else {
            wp_send_json_error(array('message' => 'Tên đăng nhập, email hoặc số điện thoại không tồn tại trên hệ thống.'));
        }
    } else {
        // ƯU TIÊN 2: Phương án dự phòng nếu không cài WooCommerce (Sử dụng cổng mail mặc định WordPress core)
        $user_data = is_email($user_login) ? get_user_by('email', $user_login) : get_user_by('login', $user_login);

        if ( ! $user_data ) {
            wp_send_json_error(array('message' => 'Tài khoản hoặc địa chỉ Email này không tồn tại.'));
        }

        $user_login = $user_data->user_login;
        $user_email = $user_data->user_email;
        
        // Tạo khóa bảo mật đặt lại mật khẩu của WordPress
        $key = get_password_reset_key( $user_data );

        if ( is_wp_error( $key ) ) {
            wp_send_json_error(array('message' => 'Hệ thống không thể tạo mã khôi phục mật khẩu lúc này.'));
        }

        // Biên soạn nội dung thư điện tử khôi phục mật khẩu chuẩn hệ thống
        $message = __('Có một yêu cầu thay đổi mật khẩu từ tài khoản của bạn tại:') . "\r\n\r\n";
        $message .= network_home_url( '/' ) . "\r\n\r\n";
        $message .= sprintf(__('Tên đăng nhập: %s'), $user_login) . "\r\n\r\n";
        $message .= __('Nếu đây là sự nhầm lẫn, bạn chỉ cần bỏ qua email này.') . "\r\n\r\n";
        $message .= __('Để tiến hành thay đổi mật khẩu mới, vui lòng nhấp vào đường dẫn bên dưới:') . "\r\n\r\n";
        $message .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . "\r\n";

        $subject = wp_specialchars_decode( sprintf( __('[%s] Khôi phục mật khẩu tài khoản'), wp_title_rss() ) );

        if ( wp_mail( $user_email, $subject, $message ) ) {
            wp_send_json_success(array('message' => 'Một Email khôi phục mật khẩu đã được gửi đi thành công!'));
        } else {
            wp_send_json_error(array('message' => 'Hệ thống gửi thư gặp sự cố. Vui lòng liên hệ quản trị viên để kiểm tra lại SMTP.'));
        }
    }
}


//////////////
// 1. TẠO VÀ DI CHUYỂN CỘT SỐ ĐIỆN THOẠI XUẤT HIỆN NGAY SAU CỘT TÊN
add_filter('manage_users_columns', 'custom_add_phone_user_column');
function custom_add_phone_user_column($columns) {
    $new_columns = array();

    foreach ( $columns as $key => $value ) {
        $new_columns[$key] = $value;
        
        // Khi vòng lặp chạy đến cột 'name' (Tên), ta chèn ngay cột số điện thoại vào sau nó
        if ( $key === 'name' ) {
            $new_columns['billing_phone'] = 'Số điện thoại';
        }
    }

    return $new_columns;
}


// 2. HIỂN THỊ DỮ LIỆU SỐ ĐIỆN THOẠI VÀO CỘT VỪA TẠO
add_filter('manage_users_custom_column', 'custom_show_phone_user_column_value', 10, 3);
function custom_show_phone_user_column_value($output, $column_name, $user_id) {
    // Kiểm tra đúng tên cột billing_phone của WooCommerce
    if ($column_name === 'billing_phone') {
        $phone = get_user_meta($user_id, 'billing_phone', true);
        
        // Nếu có số điện thoại thì hiển thị, không thì để dấu gạch ngang
        if (!empty($phone)) {
            $output = '<strong>' . esc_html($phone) . '</strong>';
        } else {
            $output = '<span style="color:#aaa;">—</span>';
        }
    }
    return $output;
}

// 3. TÍCH HỢP TÍNH NĂNG CHO PHÉP TÌM KIẾM THEO SỐ ĐIỆN THOẠI
add_filter('user_search_columns', 'custom_search_user_by_phone', 10, 3);
function custom_search_user_by_phone($search_columns, $search, $wp_user_query) {
    // Cho phép thanh tìm kiếm trong wp-admin quét qua cả dữ liệu meta dữ liệu số điện thoại
    if (!in_array('billing_phone', $search_columns)) {
        global $wpdb;
        // Hook can thiệp vào câu lệnh truy vấn tìm kiếm thành viên
        $wp_user_query->query_where = str_replace(
            ') AND',
            " OR {$wpdb->users}.ID IN (SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key='billing_phone' AND meta_value LIKE '%" . esc_sql($search) . "%')) AND",
            $wp_user_query->query_where
        );
    }
    return $search_columns;
}

Tags :
Tags: