Fix search logic - use RPC instead of trigger_up to prevent input clearing
This commit is contained in:
parent
777d8e130e
commit
ab69c6eb62
|
|
@ -4,11 +4,12 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
var ListRenderer = require('web.ListRenderer');
|
||||
var core = require('web.core');
|
||||
var _t = core._t;
|
||||
var session = require('web.session');
|
||||
|
||||
ListRenderer.include({
|
||||
events: _.extend({
|
||||
'keyup .oe_search_input': '_onKeyUp',
|
||||
'change .oe_search_input': '_onSearchChange'
|
||||
'click .oe_clear_search': '_onClearSearch'
|
||||
}, ListRenderer.prototype.events),
|
||||
|
||||
_renderView: function () {
|
||||
|
|
@ -18,32 +19,32 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
if (self.arch.tag == 'tree' && self.$el.hasClass('o_list_view')) {
|
||||
// Check if the search input already exists
|
||||
if (!self.$el.find('.oe_search_input').length) {
|
||||
var search = '<div class="oe_search_container" style="display: flex; align-items: center; margin: 8px;">' +
|
||||
'<input type="text" class="oe_search_input" placeholder="' + _t('Search in all records...') + '" style="flex: 1;">' +
|
||||
var search = '<div class="oe_search_container" style="display: flex; align-items: center; margin: 8px; background-color: #f8f9fa; padding: 10px; border-radius: 4px;">' +
|
||||
'<input type="text" class="oe_search_input" placeholder="' + _t('Search in all records...') + '" style="flex: 1; border: 1px solid #ccc; height: 32px; padding: 0 12px; border-radius: 4px;">' +
|
||||
'<button class="btn btn-sm btn-secondary oe_clear_search ml-2" style="display: none;">' + _t('Clear') + '</button>' +
|
||||
'<span class="oe_search_count ml-2" style="display: none;"></span>' +
|
||||
'<span class="oe_search_count ml-2" style="display: none; color: #6c757d; font-size: 0.9em;"></span>' +
|
||||
'</div>';
|
||||
self.$el.find('table').addClass('oe_table_search');
|
||||
var $search = $(search);
|
||||
$search.find('.oe_search_input').css({
|
||||
'border': '1px solid #ccc',
|
||||
'height': '32px',
|
||||
'padding': '0 12px',
|
||||
'border-radius': '4px'
|
||||
});
|
||||
$search.find('.oe_clear_search').on('click', function() {
|
||||
self._clearSearch();
|
||||
});
|
||||
self.$el.prepend($search);
|
||||
self.$el.prepend($(search));
|
||||
|
||||
// Store original domain for restoration
|
||||
self._originalDomain = self.state.domain || [];
|
||||
self._searchTimeout = null;
|
||||
// Initialize search functionality
|
||||
self._initializeSearch();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_initializeSearch: function() {
|
||||
var self = this;
|
||||
this._searchTimeout = null;
|
||||
this._currentSearchValue = '';
|
||||
this._isSearching = false;
|
||||
|
||||
// Store the original records
|
||||
this._originalRecords = null;
|
||||
this._searchMode = false;
|
||||
},
|
||||
|
||||
_onKeyUp: function (event) {
|
||||
var self = this;
|
||||
var value = $(event.currentTarget).val();
|
||||
|
|
@ -53,64 +54,95 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
clearTimeout(this._searchTimeout);
|
||||
}
|
||||
|
||||
// Store current value to prevent clearing
|
||||
this._currentSearchValue = value;
|
||||
|
||||
// Add delay to avoid too many requests
|
||||
this._searchTimeout = setTimeout(function() {
|
||||
self._performSearch(value);
|
||||
}, 500); // 500ms delay
|
||||
if (!self._isSearching) {
|
||||
self._performSearch(value);
|
||||
}
|
||||
}, 500);
|
||||
},
|
||||
|
||||
_onSearchChange: function (event) {
|
||||
var value = $(event.currentTarget).val();
|
||||
this._performSearch(value);
|
||||
_onClearSearch: function(event) {
|
||||
this._clearSearch();
|
||||
},
|
||||
|
||||
_performSearch: function (searchValue) {
|
||||
var self = this;
|
||||
var value = searchValue.toLowerCase().trim();
|
||||
var value = (searchValue || '').toLowerCase().trim();
|
||||
|
||||
if (!value) {
|
||||
this._clearSearch();
|
||||
// Prevent concurrent searches
|
||||
if (this._isSearching) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show/hide clear button
|
||||
this.$el.find('.oe_clear_search').toggle(!!value);
|
||||
|
||||
if (!value) {
|
||||
this._clearSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
this._isSearching = true;
|
||||
|
||||
// Store original records if not already stored
|
||||
if (!this._originalRecords && this.state.data.records) {
|
||||
this._originalRecords = this.state.data.records.slice();
|
||||
}
|
||||
|
||||
// Get model and fields info
|
||||
var model = this.state.model;
|
||||
var fields = this._getSearchableFields();
|
||||
|
||||
if (fields.length === 0) {
|
||||
this._isSearching = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Build search domain
|
||||
var searchDomain = this._buildSearchDomain(value);
|
||||
var domain = this._buildSearchDomain(value, fields);
|
||||
|
||||
// Combine with original domain
|
||||
var newDomain = this._originalDomain.concat(searchDomain);
|
||||
// Add existing domain if any
|
||||
if (this.state.domain && this.state.domain.length > 0) {
|
||||
domain = this.state.domain.concat([['id', 'in', this.state.res_ids || []]]).concat(domain);
|
||||
}
|
||||
|
||||
// Update the domain and reload
|
||||
this.trigger_up('reload', {
|
||||
domain: newDomain,
|
||||
offset: 0,
|
||||
limit: this.state.limit,
|
||||
keepChanges: false,
|
||||
reload: true,
|
||||
callback: function() {
|
||||
self._updateSearchCount(value);
|
||||
// Perform RPC search
|
||||
this._rpc({
|
||||
model: model,
|
||||
method: 'search_read',
|
||||
args: [domain],
|
||||
kwargs: {
|
||||
fields: this.state.fields_get ? Object.keys(this.state.fields_get) : [],
|
||||
limit: this.state.limit || 80,
|
||||
offset: 0,
|
||||
context: this.state.context,
|
||||
}
|
||||
}).then(function(result) {
|
||||
self._updateListWithSearchResults(result, value);
|
||||
self._isSearching = false;
|
||||
|
||||
// Restore search value (in case it was cleared during reload)
|
||||
self.$el.find('.oe_search_input').val(self._currentSearchValue);
|
||||
}).catch(function(error) {
|
||||
console.error('Search error:', error);
|
||||
self._isSearching = false;
|
||||
self.$el.find('.oe_search_input').val(self._currentSearchValue);
|
||||
});
|
||||
},
|
||||
|
||||
_buildSearchDomain: function(value) {
|
||||
var self = this;
|
||||
_buildSearchDomain: function(value, fields) {
|
||||
var domain = [];
|
||||
var searchableFields = this._getSearchableFields();
|
||||
|
||||
if (searchableFields.length === 0) {
|
||||
return [];
|
||||
}
|
||||
var orConditions = [];
|
||||
|
||||
// Normalize search value for Arabic
|
||||
var normalizedValue = this._normalizeArabic(value);
|
||||
|
||||
// Build OR domain for all searchable fields
|
||||
var orConditions = [];
|
||||
_.each(searchableFields, function(field) {
|
||||
// For char, text, and name fields
|
||||
_.each(fields, function(field) {
|
||||
// For char, text, and html fields
|
||||
if (['char', 'text', 'html'].includes(field.type)) {
|
||||
orConditions.push([field.name, 'ilike', value]);
|
||||
// Also search with normalized value if different
|
||||
|
|
@ -118,13 +150,16 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
orConditions.push([field.name, 'ilike', normalizedValue]);
|
||||
}
|
||||
}
|
||||
// For many2one fields (search in display_name)
|
||||
// For many2one fields
|
||||
else if (field.type === 'many2one') {
|
||||
orConditions.push([field.name + '.display_name', 'ilike', value]);
|
||||
// Search in the display_name of the related field
|
||||
orConditions.push([field.name, 'ilike', value]);
|
||||
}
|
||||
// For number fields (if value is numeric)
|
||||
else if (['integer', 'float', 'monetary'].includes(field.type) && !isNaN(value)) {
|
||||
orConditions.push([field.name, '=', parseFloat(value)]);
|
||||
else if (['integer', 'float', 'monetary'].includes(field.type)) {
|
||||
if (!isNaN(value)) {
|
||||
orConditions.push([field.name, '=', parseFloat(value)]);
|
||||
}
|
||||
}
|
||||
// For selection fields
|
||||
else if (field.type === 'selection') {
|
||||
|
|
@ -148,12 +183,12 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
var self = this;
|
||||
var fields = [];
|
||||
|
||||
// Get visible fields from the list view
|
||||
// Get visible fields from the list view columns
|
||||
_.each(this.columns, function(column) {
|
||||
if (!column.invisible && column.attrs.name) {
|
||||
var fieldName = column.attrs.name;
|
||||
var field = self.state.fields[fieldName];
|
||||
if (field && field.searchable !== false) {
|
||||
if (field && field.searchable !== false && field.store !== false) {
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
type: field.type,
|
||||
|
|
@ -163,15 +198,19 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
}
|
||||
});
|
||||
|
||||
// If no visible fields, get all char/text fields
|
||||
// If no visible fields, get common searchable fields
|
||||
if (fields.length === 0) {
|
||||
_.each(this.state.fields, function(field, fieldName) {
|
||||
if (['char', 'text', 'many2one'].includes(field.type) && field.searchable !== false) {
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
type: field.type,
|
||||
string: field.string
|
||||
});
|
||||
var commonFields = ['name', 'display_name', 'reference', 'code', 'description'];
|
||||
_.each(commonFields, function(fieldName) {
|
||||
if (self.state.fields[fieldName]) {
|
||||
var field = self.state.fields[fieldName];
|
||||
if (field.searchable !== false && field.store !== false) {
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
type: field.type,
|
||||
string: field.string
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -179,6 +218,56 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
return fields;
|
||||
},
|
||||
|
||||
_updateListWithSearchResults: function(records, searchValue) {
|
||||
var self = this;
|
||||
|
||||
// Update the count display
|
||||
var count = records.length;
|
||||
var $countEl = this.$el.find('.oe_search_count');
|
||||
if (searchValue && count >= 0) {
|
||||
$countEl.text(_t('Found: ') + count + _t(' records')).show();
|
||||
} else {
|
||||
$countEl.hide();
|
||||
}
|
||||
|
||||
// Update the table rows - filter displayed records
|
||||
if (records.length > 0) {
|
||||
var recordIds = records.map(function(r) { return r.id; });
|
||||
|
||||
// Hide rows that don't match
|
||||
this.$el.find('.o_data_row').each(function() {
|
||||
var $row = $(this);
|
||||
var recordId = $row.data('id');
|
||||
if (recordId) {
|
||||
var isVisible = recordIds.includes(recordId);
|
||||
$row.toggle(isVisible);
|
||||
}
|
||||
});
|
||||
|
||||
// If we need to load more records, update the state
|
||||
if (count > this.state.data.records.length) {
|
||||
// Need to reload with new records
|
||||
this.state.data.records = records;
|
||||
this._renderBody().then(function() {
|
||||
// Restore search input value
|
||||
self.$el.find('.oe_search_input').val(self._currentSearchValue);
|
||||
self.$el.find('.oe_clear_search').show();
|
||||
self.$el.find('.oe_search_count').text(_t('Found: ') + count + _t(' records')).show();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// No results - hide all rows
|
||||
this.$el.find('.o_data_row').hide();
|
||||
|
||||
// Show no results message
|
||||
if (!this.$el.find('.oe_no_results').length) {
|
||||
var noResultsHtml = '<tr class="oe_no_results"><td colspan="100%" class="text-center text-muted">' +
|
||||
_t('No records found for: ') + _.escape(searchValue) + '</td></tr>';
|
||||
this.$el.find('tbody').append(noResultsHtml);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_clearSearch: function() {
|
||||
var self = this;
|
||||
|
||||
|
|
@ -187,24 +276,22 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
this.$el.find('.oe_clear_search').hide();
|
||||
this.$el.find('.oe_search_count').hide();
|
||||
|
||||
// Restore original domain
|
||||
this.trigger_up('reload', {
|
||||
domain: this._originalDomain,
|
||||
offset: 0,
|
||||
limit: this.state.limit,
|
||||
keepChanges: false,
|
||||
reload: true
|
||||
});
|
||||
},
|
||||
|
||||
_updateSearchCount: function(searchValue) {
|
||||
var count = this.state.count || 0;
|
||||
var $countEl = this.$el.find('.oe_search_count');
|
||||
// Remove no results message
|
||||
this.$el.find('.oe_no_results').remove();
|
||||
|
||||
if (searchValue && count >= 0) {
|
||||
$countEl.text(_t('Found: ') + count + _t(' records')).show();
|
||||
} else {
|
||||
$countEl.hide();
|
||||
// Show all rows again
|
||||
this.$el.find('.o_data_row').show();
|
||||
|
||||
// Reset search state
|
||||
this._currentSearchValue = '';
|
||||
this._searchMode = false;
|
||||
|
||||
// If we have original records, restore them
|
||||
if (this._originalRecords) {
|
||||
this.state.data.records = this._originalRecords;
|
||||
this._renderBody().then(function() {
|
||||
self._originalRecords = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -214,12 +301,12 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
// Normalizing Arabic text by removing common variations
|
||||
return text
|
||||
.replace(/[\u064B-\u065F]/g, '') // Remove diacritics
|
||||
.replace(/[\u0660-\u0669]/g, (d) => String.fromCharCode(d.charCodeAt(0) - 0x0660 + 0x0030)) // Convert Arabic-Indic digits
|
||||
.replace(/[\u06F0-\u06F9]/g, (d) => String.fromCharCode(d.charCodeAt(0) - 0x06F0 + 0x0030)) // Extended Arabic-Indic digits
|
||||
.replace(/[\u0622\u0623\u0625\u0627]/g, 'ا') // Normalize Alef forms
|
||||
.replace(/[\u0629]/g, 'ه') // Normalize Teh Marbuta to Heh
|
||||
.replace(/[\u064A\u0626\u0649]/g, 'ي') // Normalize Yeh forms
|
||||
.replace(/[\u0624\u0648]/g, 'و'); // Normalize Waw variants
|
||||
.replace(/[\u0660-\u0669]/g, (d) => String.fromCharCode(d.charCodeAt(0) - 0x0660 + 0x0030)) // Arabic-Indic digits
|
||||
.replace(/[\u06F0-\u06F9]/g, (d) => String.fromCharCode(d.charCodeAt(0) - 0x06F0 + 0x0030)) // Extended Arabic-Indic
|
||||
.replace(/[\u0622\u0623\u0625\u0627]/g, 'ا') // Normalize Alef
|
||||
.replace(/[\u0629]/g, 'ه') // Teh Marbuta to Heh
|
||||
.replace(/[\u064A\u0626\u0649]/g, 'ي') // Normalize Yeh
|
||||
.replace(/[\u0624\u0648]/g, 'و'); // Normalize Waw
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
|
|
|
|||
Loading…
Reference in New Issue