- AjaxSearch component: debounced 300ms, calls /woo/search/* endpoints via rpc() - ProductMapping client action: 3 tabs (Mapped, Unmatched, Conflicts) - Mapped tab: live search, bulk unmap/sync, per-row price/inventory sync toggles - Unmatched tab: split Odoo|WC panels, click-to-select, Map/Create/Ignore actions - Conflicts tab: Use Odoo / Use WC per-row and bulk resolve - Top bar: instance selector, Fetch Products, Sync Now, live stats - woo_dashboard.xml updated with ir.actions.client records - woo_menus.xml pointed at new client action - CSS: full layout styles, badges, split view, progress bar, buttons Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
48 lines
1.3 KiB
JavaScript
48 lines
1.3 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { Component, useState } from "@odoo/owl";
|
|
import { rpc } from "@web/core/network/rpc";
|
|
|
|
/**
|
|
* AjaxSearch — reusable debounced search component.
|
|
*
|
|
* Props:
|
|
* endpoint {String} The /woo/search/* URL to POST to.
|
|
* instanceId {Number} woo.instance ID (optional).
|
|
* onResults {Function} Callback receives the results array.
|
|
* placeholder {String} Input placeholder text.
|
|
*/
|
|
export class AjaxSearch extends Component {
|
|
static template = "fusion_woocommerce.AjaxSearch";
|
|
static props = ["endpoint", "onResults", "*"];
|
|
|
|
setup() {
|
|
this.state = useState({ query: "" });
|
|
this._debounceTimer = null;
|
|
}
|
|
|
|
onInput(ev) {
|
|
const query = ev.target.value;
|
|
this.state.query = query;
|
|
|
|
clearTimeout(this._debounceTimer);
|
|
this._debounceTimer = setTimeout(() => {
|
|
this._doSearch(query);
|
|
}, 300);
|
|
}
|
|
|
|
async _doSearch(query) {
|
|
try {
|
|
const params = { query };
|
|
if (this.props.instanceId) {
|
|
params.instance_id = this.props.instanceId;
|
|
}
|
|
const results = await rpc(this.props.endpoint, params);
|
|
this.props.onResults(results || []);
|
|
} catch (err) {
|
|
console.error("[AjaxSearch] search error:", err);
|
|
this.props.onResults([]);
|
|
}
|
|
}
|
|
}
|