fix: customer search & linking fully implemented
- Add ajax_search_wc_customers() – searches user_login, user_email, display_name AND first_name/last_name meta so any query returns hits - Add ajax_unlink_customer() to remove an existing Odoo link - Replace placeholder Customers tab with two-panel UI: Step 1 search WC customers → Step 2 search Odoo partners independently (WC email and Odoo email do not need to match) - Results table shows current link status; inline Link/Unlink actions update the row in-place without page reload - Admin JS fully wired: both search inputs respond to Enter key and button - Add two-panel layout CSS and results table styles Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,3 +24,47 @@
|
|||||||
}
|
}
|
||||||
.woodoo-badge--green { background: #d1fae5; color: #065f46; }
|
.woodoo-badge--green { background: #d1fae5; color: #065f46; }
|
||||||
.woodoo-badge--red { background: #fee2e2; color: #991b1b; }
|
.woodoo-badge--red { background: #fee2e2; color: #991b1b; }
|
||||||
|
.woodoo-badge--grey { background: #f3f4f6; color: #6b7280; }
|
||||||
|
|
||||||
|
/* ── Two-panel customer linker ────────────────────────────────────────── */
|
||||||
|
.woodoo-linker-panel {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
.woodoo-linker-col {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
background: #f9fafb;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.woodoo-linker-col h3 {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #2271b1;
|
||||||
|
}
|
||||||
|
.woodoo-search-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.woodoo-search-row .regular-text { flex: 1; margin: 0; }
|
||||||
|
.woodoo-results-list { margin-top: 4px; }
|
||||||
|
.woodoo-no-results { color: #6b7280; font-style: italic; margin: 8px 0 0; }
|
||||||
|
.woodoo-linking-for {
|
||||||
|
font-size: .875rem;
|
||||||
|
background: #eff6ff;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Results table ────────────────────────────────────────────────────── */
|
||||||
|
.woodoo-link-table { font-size: .8125rem; }
|
||||||
|
.woodoo-link-table th { padding: 6px 8px; font-weight: 600; background: #f3f4f6; }
|
||||||
|
.woodoo-link-table td { padding: 6px 8px; vertical-align: middle; }
|
||||||
|
.woodoo-link-table tr:hover td { background: #fafafa; }
|
||||||
|
|||||||
@@ -2,15 +2,16 @@
|
|||||||
jQuery( function ( $ ) {
|
jQuery( function ( $ ) {
|
||||||
|
|
||||||
const cfg = window.WooDooAdmin || {};
|
const cfg = window.WooDooAdmin || {};
|
||||||
|
const i18n = cfg.i18n || {};
|
||||||
|
const ajaxUrl = cfg.ajax_url;
|
||||||
|
const nonce = cfg.nonce;
|
||||||
|
|
||||||
// ── Tab navigation ──────────────────────────────────────────────────
|
// ── Tab navigation ──────────────────────────────────────────────────
|
||||||
$( '.woodoo-settings .nav-tab' ).on( 'click', function ( e ) {
|
$( '.woodoo-settings .nav-tab' ).on( 'click', function ( e ) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const target = $( this ).attr( 'href' );
|
const target = $( this ).attr( 'href' );
|
||||||
|
|
||||||
$( '.woodoo-settings .nav-tab' ).removeClass( 'nav-tab-active' );
|
$( '.woodoo-settings .nav-tab' ).removeClass( 'nav-tab-active' );
|
||||||
$( this ).addClass( 'nav-tab-active' );
|
$( this ).addClass( 'nav-tab-active' );
|
||||||
|
|
||||||
$( '.woodoo-tab' ).hide();
|
$( '.woodoo-tab' ).hide();
|
||||||
$( target ).show();
|
$( target ).show();
|
||||||
} );
|
} );
|
||||||
@@ -20,52 +21,251 @@ jQuery( function ( $ ) {
|
|||||||
const $btn = $( this );
|
const $btn = $( this );
|
||||||
const $result = $( '#woodoo-test-result' );
|
const $result = $( '#woodoo-test-result' );
|
||||||
|
|
||||||
$btn.prop( 'disabled', true ).text( cfg.i18n.testing );
|
$btn.prop( 'disabled', true ).text( i18n.testing );
|
||||||
$result.html( '' );
|
$result.html( '' );
|
||||||
|
|
||||||
$.post( cfg.ajax_url, {
|
ajax( 'woodoo_test_connection', {} )
|
||||||
action : 'woodoo_test_connection',
|
|
||||||
nonce : cfg.nonce,
|
|
||||||
} )
|
|
||||||
.done( function ( res ) {
|
.done( function ( res ) {
|
||||||
if ( res.success ) {
|
if ( res.success ) {
|
||||||
const d = res.data;
|
const d = res.data;
|
||||||
const cls = d.success ? 'success' : 'error';
|
const cls = d.success ? 'green' : 'red';
|
||||||
$result.html(
|
$result.html( badge( cls, d.message ) );
|
||||||
'<span class="woodoo-badge woodoo-badge--' + ( d.success ? 'green' : 'red' ) + '">' +
|
|
||||||
escHtml( d.message ) + '</span>'
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$result.html( '<span style="color:red;">' + escHtml( res.data || cfg.i18n.error ) + '</span>' );
|
$result.html( badge( 'red', res.data || i18n.error ) );
|
||||||
}
|
}
|
||||||
} )
|
} )
|
||||||
.fail( function () {
|
.fail( function () { $result.html( badge( 'red', i18n.error ) ); } )
|
||||||
$result.html( '<span style="color:red;">' + escHtml( cfg.i18n.error ) + '</span>' );
|
|
||||||
} )
|
|
||||||
.always( function () {
|
.always( function () {
|
||||||
$btn.prop( 'disabled', false ).text( 'Test Connection' );
|
$btn.prop( 'disabled', false ).text( 'Test Connection' );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// ── Partner search on user profile ──────────────────────────────────
|
// ══════════════════════════════════════════════════════════════════════
|
||||||
|
// CUSTOMER LINKING TAB
|
||||||
|
// ══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
let selectedWcUser = null; // { id, display_name, email }
|
||||||
|
|
||||||
|
// ── Step 1: search WooCommerce customers ─────────────────────────────
|
||||||
|
function searchWcCustomers() {
|
||||||
|
const q = $( '#woo-customer-search' ).val().trim();
|
||||||
|
if ( q.length < 2 ) return;
|
||||||
|
|
||||||
|
const $results = $( '#woo-customer-results' );
|
||||||
|
$results.html( '<em>' + esc( i18n.searching ) + '</em>' );
|
||||||
|
$( '#woodoo-odoo-col' ).hide();
|
||||||
|
selectedWcUser = null;
|
||||||
|
|
||||||
|
ajax( 'woodoo_search_wc_customers', { query: q } )
|
||||||
|
.done( function ( res ) {
|
||||||
|
if ( ! res.success || ! res.data.length ) {
|
||||||
|
$results.html( '<p class="woodoo-no-results">' + esc( i18n.no_wc_customers ) + '</p>' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = '<table class="woodoo-link-table widefat"><thead><tr>' +
|
||||||
|
'<th>Name</th><th>WC Email</th><th>Odoo Partner</th><th></th>' +
|
||||||
|
'</tr></thead><tbody>';
|
||||||
|
|
||||||
|
res.data.forEach( function ( u ) {
|
||||||
|
const linked = u.partner_id
|
||||||
|
? '<span class="woodoo-badge woodoo-badge--green">' + esc( u.partner_name || '#' + u.partner_id ) + '</span>'
|
||||||
|
: '<span class="woodoo-badge woodoo-badge--grey">' + esc( i18n.not_linked ) + '</span>';
|
||||||
|
|
||||||
|
html +=
|
||||||
|
'<tr data-user-id="' + u.id + '">' +
|
||||||
|
'<td><a href="' + u.edit_url + '" target="_blank">' + esc( u.display_name ) + '</a></td>' +
|
||||||
|
'<td>' + esc( u.email ) + '</td>' +
|
||||||
|
'<td class="woodoo-partner-status">' + linked + '</td>' +
|
||||||
|
'<td class="woodoo-link-actions">' +
|
||||||
|
'<button type="button" class="button button-small woodoo-select-wc-user"' +
|
||||||
|
' data-id="' + u.id + '"' +
|
||||||
|
' data-name="' + escAttr( u.display_name ) + '"' +
|
||||||
|
' data-email="' + escAttr( u.email ) + '">' +
|
||||||
|
'Link Odoo Partner</button>';
|
||||||
|
|
||||||
|
if ( u.partner_id ) {
|
||||||
|
html += ' <button type="button" class="button button-small woodoo-unlink-user"' +
|
||||||
|
' data-id="' + u.id + '" style="color:#dc2626;">Unlink</button>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</td></tr>';
|
||||||
|
} );
|
||||||
|
|
||||||
|
html += '</tbody></table>';
|
||||||
|
$results.html( html );
|
||||||
|
} )
|
||||||
|
.fail( function () { $results.html( '<p>' + esc( i18n.error ) + '</p>' ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
$( '#woo-customer-search-btn' ).on( 'click', searchWcCustomers );
|
||||||
|
$( '#woo-customer-search' ).on( 'keypress', function ( e ) {
|
||||||
|
if ( e.which === 13 ) { e.preventDefault(); searchWcCustomers(); }
|
||||||
|
} );
|
||||||
|
|
||||||
|
// ── Select a WC customer → open Odoo partner search panel ───────────
|
||||||
|
$( document ).on( 'click', '.woodoo-select-wc-user', function () {
|
||||||
|
const $btn = $( this );
|
||||||
|
selectedWcUser = {
|
||||||
|
id : $btn.data( 'id' ),
|
||||||
|
name : $btn.data( 'name' ),
|
||||||
|
email : $btn.data( 'email' ),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Highlight selected row
|
||||||
|
$( '#woo-customer-results tr' ).css( 'background', '' );
|
||||||
|
$btn.closest( 'tr' ).css( 'background', '#eff6ff' );
|
||||||
|
|
||||||
|
// Show step 2 panel
|
||||||
|
$( '#woodoo-linking-for' ).html(
|
||||||
|
'Linking: <strong>' + esc( selectedWcUser.name ) +
|
||||||
|
'</strong> (' + esc( selectedWcUser.email ) + ')'
|
||||||
|
);
|
||||||
|
$( '#woodoo-odoo-col' ).show();
|
||||||
|
$( '#woodoo-odoo-partner-search' ).val( '' ).focus();
|
||||||
|
$( '#woodoo-odoo-partner-results' ).html( '' );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// ── Step 2: search Odoo partners ─────────────────────────────────────
|
||||||
|
function searchOdooPartners() {
|
||||||
|
if ( ! selectedWcUser ) return;
|
||||||
|
|
||||||
|
const q = $( '#woodoo-odoo-partner-search' ).val().trim();
|
||||||
|
if ( q.length < 2 ) return;
|
||||||
|
|
||||||
|
const $results = $( '#woodoo-odoo-partner-results' );
|
||||||
|
$results.html( '<em>' + esc( i18n.searching ) + '</em>' );
|
||||||
|
|
||||||
|
ajax( 'woodoo_search_partners', { query: q } )
|
||||||
|
.done( function ( res ) {
|
||||||
|
if ( ! res.success || ! res.data.length ) {
|
||||||
|
$results.html( '<p class="woodoo-no-results">' + esc( i18n.no_odoo_partners ) + '</p>' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = '<table class="woodoo-link-table widefat"><thead><tr>' +
|
||||||
|
'<th>Name</th><th>Odoo Email</th><th>Type</th><th></th>' +
|
||||||
|
'</tr></thead><tbody>';
|
||||||
|
|
||||||
|
res.data.forEach( function ( p ) {
|
||||||
|
html +=
|
||||||
|
'<tr>' +
|
||||||
|
'<td><strong>' + esc( p.name ) + '</strong></td>' +
|
||||||
|
'<td>' + esc( p.email || '—' ) + '</td>' +
|
||||||
|
'<td>' + ( p.is_company ? 'Company' : 'Individual' ) + '</td>' +
|
||||||
|
'<td><button type="button" class="button button-primary button-small woodoo-confirm-link"' +
|
||||||
|
' data-partner-id="' + p.id + '"' +
|
||||||
|
' data-partner-name="' + escAttr( p.name ) + '">' +
|
||||||
|
'Link</button></td>' +
|
||||||
|
'</tr>';
|
||||||
|
} );
|
||||||
|
|
||||||
|
html += '</tbody></table>';
|
||||||
|
$results.html( html );
|
||||||
|
} )
|
||||||
|
.fail( function () { $results.html( '<p>' + esc( i18n.error ) + '</p>' ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
$( '#woodoo-odoo-partner-search-btn' ).on( 'click', searchOdooPartners );
|
||||||
|
$( '#woodoo-odoo-partner-search' ).on( 'keypress', function ( e ) {
|
||||||
|
if ( e.which === 13 ) { e.preventDefault(); searchOdooPartners(); }
|
||||||
|
} );
|
||||||
|
|
||||||
|
// ── Confirm link: save WC user ↔ Odoo partner ────────────────────────
|
||||||
|
$( document ).on( 'click', '.woodoo-confirm-link', function () {
|
||||||
|
if ( ! selectedWcUser ) return;
|
||||||
|
|
||||||
|
const $btn = $( this );
|
||||||
|
const partnerId = $btn.data( 'partner-id' );
|
||||||
|
const partnerName = $btn.data( 'partner-name' );
|
||||||
|
|
||||||
|
$btn.prop( 'disabled', true ).text( 'Saving…' );
|
||||||
|
|
||||||
|
ajax( 'woodoo_link_customer', {
|
||||||
|
user_id : selectedWcUser.id,
|
||||||
|
partner_id : partnerId,
|
||||||
|
} )
|
||||||
|
.done( function ( res ) {
|
||||||
|
if ( res.success ) {
|
||||||
|
// Update the partner status cell in the WC customers table
|
||||||
|
const $row = $( '#woo-customer-results tr[data-user-id="' + selectedWcUser.id + '"]' );
|
||||||
|
$row.find( '.woodoo-partner-status' ).html(
|
||||||
|
badge( 'green', partnerName )
|
||||||
|
);
|
||||||
|
$row.find( '.woodoo-link-actions' ).html(
|
||||||
|
'<button type="button" class="button button-small woodoo-select-wc-user"' +
|
||||||
|
' data-id="' + selectedWcUser.id + '"' +
|
||||||
|
' data-name="' + escAttr( selectedWcUser.name ) + '"' +
|
||||||
|
' data-email="' + escAttr( selectedWcUser.email ) + '">' +
|
||||||
|
'Change Link</button>' +
|
||||||
|
' <button type="button" class="button button-small woodoo-unlink-user"' +
|
||||||
|
' data-id="' + selectedWcUser.id + '" style="color:#dc2626;">Unlink</button>'
|
||||||
|
);
|
||||||
|
$row.css( 'background', '#ecfdf5' );
|
||||||
|
|
||||||
|
// Success feedback in step 2
|
||||||
|
$( '#woodoo-odoo-partner-results' ).prepend(
|
||||||
|
'<p style="color:#065f46;font-weight:600;">✓ ' +
|
||||||
|
esc( i18n.link_done ) + ': ' + esc( selectedWcUser.name ) +
|
||||||
|
' → ' + esc( partnerName ) + '</p>'
|
||||||
|
);
|
||||||
|
$btn.text( 'Linked!' );
|
||||||
|
} else {
|
||||||
|
$btn.prop( 'disabled', false ).text( 'Link' );
|
||||||
|
alert( res.data || i18n.error );
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
.fail( function () {
|
||||||
|
$btn.prop( 'disabled', false ).text( 'Link' );
|
||||||
|
alert( i18n.error );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// ── Unlink ────────────────────────────────────────────────────────────
|
||||||
|
$( document ).on( 'click', '.woodoo-unlink-user', function () {
|
||||||
|
if ( ! confirm( i18n.unlink_confirm ) ) return;
|
||||||
|
|
||||||
|
const $btn = $( this );
|
||||||
|
const userId = $btn.data( 'id' );
|
||||||
|
$btn.prop( 'disabled', true );
|
||||||
|
|
||||||
|
ajax( 'woodoo_unlink_customer', { user_id: userId } )
|
||||||
|
.done( function ( res ) {
|
||||||
|
if ( res.success ) {
|
||||||
|
const $row = $( '#woo-customer-results tr[data-user-id="' + userId + '"]' );
|
||||||
|
$row.find( '.woodoo-partner-status' ).html( badge( 'grey', i18n.not_linked ) );
|
||||||
|
$row.find( '.woodoo-link-actions button.woodoo-unlink-user' ).remove();
|
||||||
|
$row.css( 'background', '' );
|
||||||
|
if ( selectedWcUser && selectedWcUser.id == userId ) {
|
||||||
|
$( '#woodoo-odoo-col' ).hide();
|
||||||
|
selectedWcUser = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$btn.prop( 'disabled', false );
|
||||||
|
alert( res.data || i18n.error );
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
.fail( function () { $btn.prop( 'disabled', false ); } );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// ══════════════════════════════════════════════════════════════════════
|
||||||
|
// USER PROFILE PAGE – partner search
|
||||||
|
// ══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
$( '#woodoo-partner-search-btn' ).on( 'click', function () {
|
$( '#woodoo-partner-search-btn' ).on( 'click', function () {
|
||||||
const $input = $( '#woodoo-partner-search-input' );
|
const $input = $( '#woodoo-partner-search-input' );
|
||||||
const $results = $( '#woodoo-partner-search-results' );
|
const $results = $( '#woodoo-partner-search-results' );
|
||||||
const query = $input.val().trim();
|
const q = $input.val().trim();
|
||||||
const userId = $input.data( 'user-id' );
|
const userId = $input.data( 'user-id' );
|
||||||
|
|
||||||
if ( ! query ) return;
|
if ( ! q ) return;
|
||||||
|
|
||||||
$results.html( '<em>' + escHtml( cfg.i18n.searching ) + '</em>' );
|
$results.html( '<em>' + esc( i18n.searching ) + '</em>' );
|
||||||
|
|
||||||
$.post( cfg.ajax_url, {
|
ajax( 'woodoo_search_partners', { query: q } )
|
||||||
action : 'woodoo_search_partners',
|
|
||||||
nonce : cfg.nonce,
|
|
||||||
query : query,
|
|
||||||
} )
|
|
||||||
.done( function ( res ) {
|
.done( function ( res ) {
|
||||||
if ( ! res.success || ! res.data.length ) {
|
if ( ! res.success || ! res.data.length ) {
|
||||||
$results.html( '<em>No partners found.</em>' );
|
$results.html( '<em>' + esc( i18n.no_odoo_partners ) + '</em>' );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,24 +273,21 @@ jQuery( function ( $ ) {
|
|||||||
res.data.forEach( function ( p ) {
|
res.data.forEach( function ( p ) {
|
||||||
html +=
|
html +=
|
||||||
'<li style="padding:4px 0;border-bottom:1px solid #eee;">' +
|
'<li style="padding:4px 0;border-bottom:1px solid #eee;">' +
|
||||||
'<strong>' + escHtml( p.name ) + '</strong>' +
|
'<strong>' + esc( p.name ) + '</strong>' +
|
||||||
( p.email ? ' <' + escHtml( p.email ) + '>' : '' ) +
|
( p.email ? ' <' + esc( p.email ) + '>' : '' ) +
|
||||||
' <button type="button" class="button button-small woodoo-pick-partner"' +
|
' <button type="button" class="button button-small woodoo-pick-partner"' +
|
||||||
' data-id="' + parseInt( p.id, 10 ) + '"' +
|
' data-id="' + p.id + '"' +
|
||||||
' data-name="' + escAttr( p.name ) + '"' +
|
' data-name="' + escAttr( p.name ) + '"' +
|
||||||
' data-user-id="' + parseInt( userId, 10 ) + '">' +
|
' data-user-id="' + userId + '">' +
|
||||||
'Select</button>' +
|
'Select</button>' +
|
||||||
'</li>';
|
'</li>';
|
||||||
} );
|
} );
|
||||||
html += '</ul>';
|
html += '</ul>';
|
||||||
$results.html( html );
|
$results.html( html );
|
||||||
} )
|
} )
|
||||||
.fail( function () {
|
.fail( function () { $results.html( '<em>' + esc( i18n.error ) + '</em>' ); } );
|
||||||
$results.html( '<em>' + escHtml( cfg.i18n.error ) + '</em>' );
|
|
||||||
} );
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// ── Pick partner from search results ────────────────────────────────
|
|
||||||
$( document ).on( 'click', '.woodoo-pick-partner', function () {
|
$( document ).on( 'click', '.woodoo-pick-partner', function () {
|
||||||
const $btn = $( this );
|
const $btn = $( this );
|
||||||
const partnerId = $btn.data( 'id' );
|
const partnerId = $btn.data( 'id' );
|
||||||
@@ -100,22 +297,25 @@ jQuery( function ( $ ) {
|
|||||||
$( '#woodoo_odoo_partner_id' ).val( partnerId );
|
$( '#woodoo_odoo_partner_id' ).val( partnerId );
|
||||||
$( '#woodoo-partner-name-display' ).text( name ).show();
|
$( '#woodoo-partner-name-display' ).text( name ).show();
|
||||||
$( '#woodoo-partner-search-results' ).html(
|
$( '#woodoo-partner-search-results' ).html(
|
||||||
'<span style="color:green;">' + escHtml( cfg.i18n.link_done ) + ' → ' + escHtml( name ) + ' (#' + partnerId + ')</span>'
|
'<span style="color:green;">' + esc( i18n.link_done ) +
|
||||||
|
' → ' + esc( name ) + ' (#' + partnerId + ')</span>'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Persist immediately if we have the AJAX handler
|
|
||||||
if ( userId ) {
|
if ( userId ) {
|
||||||
$.post( cfg.ajax_url, {
|
ajax( 'woodoo_link_customer', { user_id: userId, partner_id: partnerId } );
|
||||||
action : 'woodoo_link_customer',
|
|
||||||
nonce : cfg.nonce,
|
|
||||||
user_id : userId,
|
|
||||||
partner_id : partnerId,
|
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// ── Utility ──────────────────────────────────────────────────────────
|
// ── Utilities ─────────────────────────────────────────────────────────
|
||||||
function escHtml( str ) {
|
function ajax( action, data ) {
|
||||||
|
return $.post( ajaxUrl, Object.assign( { action, nonce }, data ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
function badge( color, text ) {
|
||||||
|
return '<span class="woodoo-badge woodoo-badge--' + color + '">' + esc( text ) + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function esc( str ) {
|
||||||
return String( str )
|
return String( str )
|
||||||
.replace( /&/g, '&' )
|
.replace( /&/g, '&' )
|
||||||
.replace( /</g, '<' )
|
.replace( /</g, '<' )
|
||||||
@@ -124,7 +324,7 @@ jQuery( function ( $ ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function escAttr( str ) {
|
function escAttr( str ) {
|
||||||
return escHtml( str ).replace( /'/g, ''' );
|
return esc( str ).replace( /'/g, ''' );
|
||||||
}
|
}
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ class WooDoo_Admin {
|
|||||||
// AJAX handlers (admin only)
|
// AJAX handlers (admin only)
|
||||||
add_action( 'wp_ajax_woodoo_test_connection', [ __CLASS__, 'ajax_test_connection' ] );
|
add_action( 'wp_ajax_woodoo_test_connection', [ __CLASS__, 'ajax_test_connection' ] );
|
||||||
add_action( 'wp_ajax_woodoo_search_partners', [ __CLASS__, 'ajax_search_partners' ] );
|
add_action( 'wp_ajax_woodoo_search_partners', [ __CLASS__, 'ajax_search_partners' ] );
|
||||||
|
add_action( 'wp_ajax_woodoo_search_wc_customers',[ __CLASS__, 'ajax_search_wc_customers' ] );
|
||||||
add_action( 'wp_ajax_woodoo_link_customer', [ __CLASS__, 'ajax_link_customer' ] );
|
add_action( 'wp_ajax_woodoo_link_customer', [ __CLASS__, 'ajax_link_customer' ] );
|
||||||
|
add_action( 'wp_ajax_woodoo_unlink_customer', [ __CLASS__, 'ajax_unlink_customer' ] );
|
||||||
|
|
||||||
// WC customer list column
|
// WC customer list column
|
||||||
add_filter( 'manage_users_columns', [ __CLASS__, 'add_users_column' ] );
|
add_filter( 'manage_users_columns', [ __CLASS__, 'add_users_column' ] );
|
||||||
@@ -93,8 +95,15 @@ class WooDoo_Admin {
|
|||||||
'i18n' => [
|
'i18n' => [
|
||||||
'testing' => __( 'Testing…', 'woodoo' ),
|
'testing' => __( 'Testing…', 'woodoo' ),
|
||||||
'searching' => __( 'Searching…', 'woodoo' ),
|
'searching' => __( 'Searching…', 'woodoo' ),
|
||||||
|
'no_wc_customers' => __( 'No customers found.', 'woodoo' ),
|
||||||
|
'no_odoo_partners' => __( 'No Odoo partners found.', 'woodoo' ),
|
||||||
'link_done' => __( 'Linked!', 'woodoo' ),
|
'link_done' => __( 'Linked!', 'woodoo' ),
|
||||||
|
'unlink_confirm' => __( 'Remove the Odoo link for this customer?', 'woodoo' ),
|
||||||
|
'unlinked' => __( 'Unlinked', 'woodoo' ),
|
||||||
'error' => __( 'Error. Check console.', 'woodoo' ),
|
'error' => __( 'Error. Check console.', 'woodoo' ),
|
||||||
|
'search_odoo' => __( 'Search Odoo partners…', 'woodoo' ),
|
||||||
|
'linked_to' => __( 'Linked to', 'woodoo' ),
|
||||||
|
'not_linked' => __( 'Not linked', 'woodoo' ),
|
||||||
],
|
],
|
||||||
] );
|
] );
|
||||||
}
|
}
|
||||||
@@ -230,12 +239,35 @@ class WooDoo_Admin {
|
|||||||
<!-- Customer Linking Tab -->
|
<!-- Customer Linking Tab -->
|
||||||
<div id="tab-customers" class="woodoo-tab" style="display:none;">
|
<div id="tab-customers" class="woodoo-tab" style="display:none;">
|
||||||
<h2><?php esc_html_e( 'Customer Linking', 'woodoo' ); ?></h2>
|
<h2><?php esc_html_e( 'Customer Linking', 'woodoo' ); ?></h2>
|
||||||
<p><?php esc_html_e( 'Link WooCommerce customers to their Odoo partner record. You can also edit this from each user\'s profile page.', 'woodoo' ); ?></p>
|
<p><?php esc_html_e( 'Search a WooCommerce customer, then search and pick their matching Odoo partner. Emails do not need to match.', 'woodoo' ); ?></p>
|
||||||
|
|
||||||
<div class="woodoo-customer-linker">
|
<div class="woodoo-linker-panel">
|
||||||
<label><?php esc_html_e( 'Search WooCommerce Customer:', 'woodoo' ); ?></label>
|
<div class="woodoo-linker-col">
|
||||||
<input type="text" id="woo-customer-search" class="regular-text" placeholder="<?php esc_attr_e( 'Customer name or email…', 'woodoo' ); ?>">
|
<h3><?php esc_html_e( '1. Find WooCommerce Customer', 'woodoo' ); ?></h3>
|
||||||
<div id="woo-customer-results"></div>
|
<div class="woodoo-search-row">
|
||||||
|
<input type="text" id="woo-customer-search"
|
||||||
|
class="regular-text"
|
||||||
|
placeholder="<?php esc_attr_e( 'Name or email…', 'woodoo' ); ?>">
|
||||||
|
<button type="button" id="woo-customer-search-btn" class="button button-secondary">
|
||||||
|
<?php esc_html_e( 'Search', 'woodoo' ); ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="woo-customer-results" class="woodoo-results-list"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="woodoo-linker-col" id="woodoo-odoo-col" style="display:none;">
|
||||||
|
<h3><?php esc_html_e( '2. Find Odoo Partner', 'woodoo' ); ?></h3>
|
||||||
|
<p id="woodoo-linking-for" class="woodoo-linking-for"></p>
|
||||||
|
<div class="woodoo-search-row">
|
||||||
|
<input type="text" id="woodoo-odoo-partner-search"
|
||||||
|
class="regular-text"
|
||||||
|
placeholder="<?php esc_attr_e( 'Name or email…', 'woodoo' ); ?>">
|
||||||
|
<button type="button" id="woodoo-odoo-partner-search-btn" class="button button-secondary">
|
||||||
|
<?php esc_html_e( 'Search', 'woodoo' ); ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="woodoo-odoo-partner-results" class="woodoo-results-list"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -399,4 +431,81 @@ class WooDoo_Admin {
|
|||||||
'partner_name' => $partners[0]['name'],
|
'partner_name' => $partners[0]['name'],
|
||||||
] );
|
] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function ajax_unlink_customer(): void {
|
||||||
|
check_ajax_referer( 'woodoo_admin', 'nonce' );
|
||||||
|
if ( ! current_user_can( 'edit_users' ) ) wp_send_json_error( 'Forbidden', 403 );
|
||||||
|
|
||||||
|
$user_id = absint( $_POST['user_id'] ?? 0 );
|
||||||
|
if ( ! $user_id ) wp_send_json_error( 'Invalid user ID' );
|
||||||
|
|
||||||
|
delete_user_meta( $user_id, 'woodoo_odoo_partner_id' );
|
||||||
|
delete_user_meta( $user_id, 'woodoo_odoo_partner_name' );
|
||||||
|
|
||||||
|
wp_send_json_success( [ 'user_id' => $user_id ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search WooCommerce customers by name or email.
|
||||||
|
* Searches user_login, user_email, display_name in the users table,
|
||||||
|
* AND first_name / last_name in user meta — so the email in WC doesn't
|
||||||
|
* need to match Odoo at all.
|
||||||
|
*/
|
||||||
|
public static function ajax_search_wc_customers(): void {
|
||||||
|
check_ajax_referer( 'woodoo_admin', 'nonce' );
|
||||||
|
if ( ! current_user_can( 'edit_users' ) ) wp_send_json_error( 'Forbidden', 403 );
|
||||||
|
|
||||||
|
$q = sanitize_text_field( wp_unslash( $_POST['query'] ?? '' ) );
|
||||||
|
if ( strlen( $q ) < 2 ) wp_send_json_error( 'Query too short' );
|
||||||
|
|
||||||
|
// Search main user table columns
|
||||||
|
$query1 = new WP_User_Query( [
|
||||||
|
'search' => '*' . $q . '*',
|
||||||
|
'search_columns' => [ 'user_login', 'user_email', 'display_name' ],
|
||||||
|
'number' => 20,
|
||||||
|
'fields' => 'all',
|
||||||
|
] );
|
||||||
|
|
||||||
|
// Search first_name / last_name meta fields
|
||||||
|
$query2 = new WP_User_Query( [
|
||||||
|
'number' => 20,
|
||||||
|
'fields' => 'all',
|
||||||
|
'meta_query' => [
|
||||||
|
'relation' => 'OR',
|
||||||
|
[ 'key' => 'first_name', 'value' => $q, 'compare' => 'LIKE' ],
|
||||||
|
[ 'key' => 'last_name', 'value' => $q, 'compare' => 'LIKE' ],
|
||||||
|
],
|
||||||
|
] );
|
||||||
|
|
||||||
|
// Merge and deduplicate
|
||||||
|
$all = array_merge( $query1->get_results(), $query2->get_results() );
|
||||||
|
$seen = [];
|
||||||
|
$users = [];
|
||||||
|
foreach ( $all as $user ) {
|
||||||
|
if ( isset( $seen[ $user->ID ] ) ) continue;
|
||||||
|
$seen[ $user->ID ] = true;
|
||||||
|
$users[] = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $users ) ) {
|
||||||
|
wp_send_json_success( [] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
foreach ( $users as $user ) {
|
||||||
|
$partner_id = (int) get_user_meta( $user->ID, 'woodoo_odoo_partner_id', true );
|
||||||
|
$partner_name = get_user_meta( $user->ID, 'woodoo_odoo_partner_name', true );
|
||||||
|
|
||||||
|
$results[] = [
|
||||||
|
'id' => $user->ID,
|
||||||
|
'display_name' => $user->display_name,
|
||||||
|
'email' => $user->user_email,
|
||||||
|
'partner_id' => $partner_id ?: null,
|
||||||
|
'partner_name' => $partner_name ?: null,
|
||||||
|
'edit_url' => esc_url( get_edit_user_link( $user->ID ) ),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_send_json_success( $results );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user