Ultra-defensive code with complete error handling, fallbacks, and client-side search option
This commit is contained in:
parent
14d5cdfcea
commit
3ab8990e62
|
|
@ -16,7 +16,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
var self = this;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
self.$('.o_list_table').addClass('o_list_table_ungrouped');
|
||||
if (self.arch.tag == 'tree' && self.$el.hasClass('o_list_view')) {
|
||||
if (self.arch && self.arch.tag === 'tree' && self.$el && 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; background-color: #f8f9fa; padding: 10px; border-radius: 4px;">' +
|
||||
|
|
@ -32,11 +32,13 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
self._initializeSearch();
|
||||
}
|
||||
}
|
||||
}).catch(function(error) {
|
||||
console.error('Error in _renderView:', error);
|
||||
return $.when();
|
||||
});
|
||||
},
|
||||
|
||||
_initializeSearch: function() {
|
||||
var self = this;
|
||||
this._searchTimeout = null;
|
||||
this._currentSearchValue = '';
|
||||
this._isSearching = false;
|
||||
|
|
@ -53,7 +55,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
clearTimeout(this._searchTimeout);
|
||||
}
|
||||
|
||||
// Store current value to prevent clearing
|
||||
// Store current value
|
||||
this._currentSearchValue = value;
|
||||
|
||||
// Add delay to avoid too many requests
|
||||
|
|
@ -70,7 +72,6 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
|
||||
_performSearch: function (searchValue) {
|
||||
var self = this;
|
||||
// Keep original value for search, don't lowercase here
|
||||
var value = (searchValue || '').trim();
|
||||
|
||||
// Prevent concurrent searches
|
||||
|
|
@ -86,13 +87,19 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Check if we have required state
|
||||
if (!this.state || !this.state.model) {
|
||||
console.error('Missing state or model information');
|
||||
this._showError(_t('Search not available in this view'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading indicator
|
||||
this.$el.find('.oe_search_loading').show();
|
||||
this.$el.find('.oe_search_count').hide();
|
||||
this._isSearching = true;
|
||||
|
||||
// Store original records if not already stored
|
||||
// Check different possible locations for records
|
||||
var currentRecords = this._getCurrentRecords();
|
||||
if (!this._originalRecords && currentRecords) {
|
||||
this._originalRecords = currentRecords.slice();
|
||||
|
|
@ -102,93 +109,151 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
var model = this.state.model;
|
||||
var fields = this._getSearchableFields();
|
||||
|
||||
if (fields.length === 0) {
|
||||
if (!fields || fields.length === 0) {
|
||||
this._isSearching = false;
|
||||
this.$el.find('.oe_search_loading').hide();
|
||||
console.warn('No searchable fields found for model:', model);
|
||||
this._showError(_t('No searchable fields available'));
|
||||
|
||||
// Fallback to client-side search if no searchable fields
|
||||
this._performClientSideSearch(value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build search domain
|
||||
var domain = this._buildSearchDomain(value, fields);
|
||||
var searchDomain = this._buildSearchDomain(value, fields);
|
||||
|
||||
// Add existing domain if any
|
||||
var baseDomain = this.state.domain || [];
|
||||
if (baseDomain.length > 0) {
|
||||
// Combine base domain with search domain
|
||||
domain = baseDomain.concat(domain);
|
||||
if (!searchDomain || searchDomain.length === 0) {
|
||||
this._isSearching = false;
|
||||
this.$el.find('.oe_search_loading').hide();
|
||||
this._showError(_t('Unable to build search query'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get only visible and stored fields to avoid computed fields issues
|
||||
var fieldsToRead = this._getFieldsToRead();
|
||||
// Get base domain
|
||||
var baseDomain = [];
|
||||
if (this.state.domain && Array.isArray(this.state.domain)) {
|
||||
baseDomain = this.state.domain.slice();
|
||||
}
|
||||
|
||||
// Build order string safely
|
||||
// Combine domains
|
||||
var finalDomain = baseDomain.concat(searchDomain);
|
||||
|
||||
// Get fields to read
|
||||
var fieldsToRead = this._getFieldsToRead();
|
||||
if (!fieldsToRead || fieldsToRead.length === 0) {
|
||||
fieldsToRead = ['id', 'display_name'];
|
||||
}
|
||||
|
||||
// Build order
|
||||
var orderBy = this._getOrderBy();
|
||||
|
||||
console.log('Search query:', value);
|
||||
console.log('Searching with domain:', JSON.stringify(domain));
|
||||
console.log('Fields to read:', fieldsToRead);
|
||||
console.log('Model:', model);
|
||||
// Clean context - remove restrictions that might limit search
|
||||
var searchContext = _.extend({}, this.state.context || session.user_context, {
|
||||
active_test: false, // Include inactive records
|
||||
bin_size: true // Optimize binary fields
|
||||
});
|
||||
|
||||
// Perform RPC search with specific fields only - NO LIMIT
|
||||
console.log('=== Search Debug Info ===');
|
||||
console.log('Model:', model);
|
||||
console.log('Search value:', value);
|
||||
console.log('Search domain:', JSON.stringify(finalDomain));
|
||||
console.log('Fields to read:', fieldsToRead);
|
||||
console.log('Context:', searchContext);
|
||||
|
||||
// Perform RPC search
|
||||
this._rpc({
|
||||
model: model,
|
||||
method: 'search_read',
|
||||
args: [domain],
|
||||
args: [finalDomain],
|
||||
kwargs: {
|
||||
fields: fieldsToRead, // Only read necessary fields
|
||||
limit: false, // No limit - get all records
|
||||
fields: fieldsToRead,
|
||||
limit: false,
|
||||
offset: 0,
|
||||
context: this.state.context || session.user_context,
|
||||
context: searchContext,
|
||||
order: orderBy
|
||||
}
|
||||
}).then(function(result) {
|
||||
console.log('Search results:', result.length, 'records found');
|
||||
console.log('Search completed. Found:', result.length, 'records');
|
||||
if (result.length > 0) {
|
||||
console.log('Sample result:', result[0]);
|
||||
console.log('First result example:', result[0]);
|
||||
}
|
||||
self._updateListWithSearchResults(result, value);
|
||||
}).catch(function(error) {
|
||||
console.error('Search error:', error);
|
||||
self._showError(_t('Search failed. Please try again.'));
|
||||
console.error('Search RPC error:', error);
|
||||
self._showError(_t('Search failed. Error: ') + (error.message || error.toString()));
|
||||
|
||||
// Fallback to client-side search
|
||||
console.log('Falling back to client-side search...');
|
||||
self._performClientSideSearch(value);
|
||||
}).finally(function() {
|
||||
self._isSearching = false;
|
||||
self.$el.find('.oe_search_loading').hide();
|
||||
// Restore search value
|
||||
self.$el.find('.oe_search_input').val(self._currentSearchValue);
|
||||
});
|
||||
},
|
||||
|
||||
_performClientSideSearch: function(value) {
|
||||
// Fallback: Client-side search in visible records
|
||||
console.log('Performing client-side search for:', value);
|
||||
|
||||
var normalizedValue = this._normalizeArabic(value).toLowerCase();
|
||||
var visibleCount = 0;
|
||||
var totalCount = 0;
|
||||
|
||||
this.$el.find('.o_data_row').each(function() {
|
||||
var $row = $(this);
|
||||
totalCount++;
|
||||
|
||||
var rowText = $row.text().toLowerCase();
|
||||
var normalizedRowText = this._normalizeArabic(rowText).toLowerCase();
|
||||
|
||||
var isMatch = rowText.indexOf(value.toLowerCase()) > -1 ||
|
||||
normalizedRowText.indexOf(normalizedValue) > -1;
|
||||
|
||||
$row.toggle(isMatch);
|
||||
if (isMatch) visibleCount++;
|
||||
}.bind(this));
|
||||
|
||||
var $countEl = this.$el.find('.oe_search_count');
|
||||
if (visibleCount > 0) {
|
||||
$countEl.text(_t('Found: ') + visibleCount + '/' + totalCount + _t(' visible records')).show();
|
||||
} else {
|
||||
$countEl.text(_t('No matches in visible records')).show();
|
||||
}
|
||||
},
|
||||
|
||||
_getCurrentRecords: function() {
|
||||
// Try different locations where records might be stored
|
||||
if (this.state.data && this.state.data.records) {
|
||||
if (this.state && this.state.data && this.state.data.records) {
|
||||
return this.state.data.records;
|
||||
}
|
||||
if (this.state.records) {
|
||||
if (this.state && this.state.records) {
|
||||
return this.state.records;
|
||||
}
|
||||
if (this.recordsData) {
|
||||
return this.recordsData;
|
||||
}
|
||||
if (this.records) {
|
||||
return this.records;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_setCurrentRecords: function(records) {
|
||||
// Set records in the appropriate location
|
||||
if (this.state.data) {
|
||||
if (this.state && this.state.data) {
|
||||
this.state.data.records = records;
|
||||
} else if (this.state) {
|
||||
if (!this.state.data) {
|
||||
this.state.data = {};
|
||||
}
|
||||
this.state.data.records = records;
|
||||
} else if (this.state.records !== undefined) {
|
||||
this.state.records = records;
|
||||
} else {
|
||||
// Create data structure if it doesn't exist
|
||||
this.state.data = { records: records };
|
||||
}
|
||||
},
|
||||
|
||||
_getOrderBy: function() {
|
||||
var orderBy = false;
|
||||
if (this.state.orderedBy && this.state.orderedBy.length > 0) {
|
||||
if (this.state && this.state.orderedBy && Array.isArray(this.state.orderedBy) && this.state.orderedBy.length > 0) {
|
||||
var order = this.state.orderedBy[0];
|
||||
if (order && order.name) {
|
||||
orderBy = order.name;
|
||||
|
|
@ -202,34 +267,42 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
|
||||
_getFieldsToRead: function() {
|
||||
var self = this;
|
||||
var fields = ['id']; // Always include ID
|
||||
var fields = ['id'];
|
||||
|
||||
// Get only visible, stored fields from columns
|
||||
// Check if columns exist
|
||||
if (!this.columns || !Array.isArray(this.columns)) {
|
||||
console.warn('No columns found in view');
|
||||
return ['id', 'display_name', 'name'];
|
||||
}
|
||||
|
||||
// Check if state.fields exists
|
||||
if (!this.state || !this.state.fields) {
|
||||
console.warn('No fields metadata found');
|
||||
return ['id', 'display_name', 'name'];
|
||||
}
|
||||
|
||||
// Get visible fields from columns
|
||||
_.each(this.columns, function(column) {
|
||||
if (!column.invisible && column.attrs && column.attrs.name) {
|
||||
if (column && !column.invisible && column.attrs && column.attrs.name) {
|
||||
var fieldName = column.attrs.name;
|
||||
var field = self.state.fields[fieldName];
|
||||
|
||||
// Only include stored fields or safe field types
|
||||
if (field) {
|
||||
// Skip computed fields unless they're stored
|
||||
// Skip computed non-stored fields
|
||||
if (field.compute && !field.store) {
|
||||
console.log('Skipping computed field:', fieldName);
|
||||
return; // Skip this field
|
||||
console.log('Skipping computed non-stored field:', fieldName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Include all other fields
|
||||
fields.push(fieldName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Always try to include display_name if available
|
||||
// Add essential fields if available
|
||||
if (self.state.fields.display_name && !fields.includes('display_name')) {
|
||||
fields.push('display_name');
|
||||
}
|
||||
|
||||
// Always try to include name if available
|
||||
if (self.state.fields.name && !fields.includes('name')) {
|
||||
fields.push('name');
|
||||
}
|
||||
|
|
@ -238,59 +311,60 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
},
|
||||
|
||||
_buildSearchDomain: function(value, fields) {
|
||||
if (!value || !fields || fields.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var domain = [];
|
||||
var orConditions = [];
|
||||
|
||||
// Normalize search value for Arabic
|
||||
// Normalize for Arabic
|
||||
var normalizedValue = this._normalizeArabic(value);
|
||||
|
||||
_.each(fields, function(field) {
|
||||
// For char, text, html, and selection fields
|
||||
if (!field || !field.name || !field.type) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Text fields
|
||||
if (['char', 'text', 'html', 'selection'].includes(field.type)) {
|
||||
// Use ilike for case-insensitive search
|
||||
orConditions.push([field.name, 'ilike', value]);
|
||||
// Also search with normalized value if different (for Arabic)
|
||||
if (normalizedValue !== value && normalizedValue) {
|
||||
if (normalizedValue && normalizedValue !== value) {
|
||||
orConditions.push([field.name, 'ilike', normalizedValue]);
|
||||
}
|
||||
}
|
||||
// For many2one fields - search in the display_name
|
||||
// Many2one fields - special handling
|
||||
else if (field.type === 'many2one') {
|
||||
// Many2one search needs special handling
|
||||
// Try searching by ID if value is numeric
|
||||
// Search by ID if numeric
|
||||
if (!isNaN(value)) {
|
||||
orConditions.push([field.name, '=', parseInt(value)]);
|
||||
}
|
||||
// Also try text search in the related field's name
|
||||
orConditions.push([field.name, 'ilike', value]);
|
||||
// Note: searching text in many2one requires special syntax
|
||||
// We'll search by ID only for now
|
||||
}
|
||||
// For number fields (if value is numeric)
|
||||
// Number fields
|
||||
else if (['integer', 'float', 'monetary'].includes(field.type)) {
|
||||
if (!isNaN(value) && value !== '') {
|
||||
var numValue = parseFloat(value);
|
||||
// Exact match for integers
|
||||
if (field.type === 'integer') {
|
||||
orConditions.push([field.name, '=', parseInt(value)]);
|
||||
} else {
|
||||
// For float/monetary, exact match
|
||||
orConditions.push([field.name, '=', numValue]);
|
||||
orConditions.push([field.name, '=', parseFloat(value)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// For boolean fields
|
||||
// Boolean fields
|
||||
else if (field.type === 'boolean') {
|
||||
var boolValue = value.toLowerCase();
|
||||
if (boolValue === 'true' || boolValue === 'yes' || boolValue === '1' || boolValue === 'نعم') {
|
||||
if (['true', 'yes', '1', 'نعم', 'صح'].includes(boolValue)) {
|
||||
orConditions.push([field.name, '=', true]);
|
||||
} else if (boolValue === 'false' || boolValue === 'no' || boolValue === '0' || boolValue === 'لا') {
|
||||
} else if (['false', 'no', '0', 'لا', 'خطأ'].includes(boolValue)) {
|
||||
orConditions.push([field.name, '=', false]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Create OR domain
|
||||
// Build OR domain
|
||||
if (orConditions.length > 0) {
|
||||
// Add '|' operators for OR condition
|
||||
for (var i = 1; i < orConditions.length; i++) {
|
||||
domain.push('|');
|
||||
}
|
||||
|
|
@ -303,43 +377,49 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
_getSearchableFields: function() {
|
||||
var self = this;
|
||||
var fields = [];
|
||||
var addedFields = {}; // Track added fields to avoid duplicates
|
||||
var addedFields = {};
|
||||
|
||||
// Priority 1: Get visible stored fields from columns
|
||||
_.each(this.columns, function(column) {
|
||||
if (!column.invisible && column.attrs && column.attrs.name) {
|
||||
var fieldName = column.attrs.name;
|
||||
var field = self.state.fields[fieldName];
|
||||
|
||||
if (field && !addedFields[fieldName]) {
|
||||
// Only searchable in stored fields or specific field types
|
||||
var isSearchable = (
|
||||
field.searchable !== false &&
|
||||
(field.store !== false || field.type === 'many2one') &&
|
||||
(!field.compute || field.store) // Skip non-stored computed fields
|
||||
);
|
||||
// Check requirements
|
||||
if (!this.state || !this.state.fields) {
|
||||
console.warn('No state.fields available');
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get fields from columns if available
|
||||
if (this.columns && Array.isArray(this.columns)) {
|
||||
_.each(this.columns, function(column) {
|
||||
if (column && !column.invisible && column.attrs && column.attrs.name) {
|
||||
var fieldName = column.attrs.name;
|
||||
var field = self.state.fields[fieldName];
|
||||
|
||||
if (isSearchable) {
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
type: field.type,
|
||||
string: field.string || fieldName
|
||||
});
|
||||
addedFields[fieldName] = true;
|
||||
if (field && !addedFields[fieldName]) {
|
||||
// Check if searchable
|
||||
var isSearchable = (
|
||||
field.searchable !== false &&
|
||||
(field.store !== false || field.type === 'many2one') &&
|
||||
(!field.compute || field.store)
|
||||
);
|
||||
|
||||
if (isSearchable) {
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
type: field.type,
|
||||
string: field.string || fieldName
|
||||
});
|
||||
addedFields[fieldName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Priority 2: Add common searchable fields if not enough fields
|
||||
// Add common fields if not enough
|
||||
if (fields.length < 3) {
|
||||
var commonFields = ['name', 'display_name', 'reference', 'code', 'ref', 'description'];
|
||||
_.each(commonFields, function(fieldName) {
|
||||
if (self.state.fields[fieldName] && !addedFields[fieldName]) {
|
||||
var field = self.state.fields[fieldName];
|
||||
if (field.searchable !== false &&
|
||||
field.store !== false &&
|
||||
(!field.compute || field.store)) {
|
||||
var field = self.state.fields[fieldName];
|
||||
if (field && !addedFields[fieldName]) {
|
||||
if (field.searchable !== false && field.store !== false && (!field.compute || field.store)) {
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
type: field.type,
|
||||
|
|
@ -351,75 +431,73 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
});
|
||||
}
|
||||
|
||||
console.log('Searchable fields:', fields);
|
||||
console.log('Searchable fields found:', fields.length, 'fields');
|
||||
return fields;
|
||||
},
|
||||
|
||||
_updateListWithSearchResults: function(records, searchValue) {
|
||||
var self = this;
|
||||
|
||||
// Update the count display
|
||||
if (!records) {
|
||||
records = [];
|
||||
}
|
||||
|
||||
var count = records.length;
|
||||
var $countEl = this.$el.find('.oe_search_count');
|
||||
|
||||
if (count > 0) {
|
||||
var message = _t('Found: ') + count + _t(' records');
|
||||
// Show warning if too many records
|
||||
if (count > 1000) {
|
||||
message += ' ' + _t('(Large result set, may affect performance)');
|
||||
message += ' ' + _t('(Large result set)');
|
||||
}
|
||||
$countEl.text(message).show();
|
||||
|
||||
// Update the view with new records
|
||||
this._setCurrentRecords(records);
|
||||
this._searchMode = true;
|
||||
|
||||
// Re-render the body with new records
|
||||
// Wrap in try-catch and handle both Promise and non-Promise returns
|
||||
try {
|
||||
var renderResult = this._renderBody();
|
||||
if (renderResult && typeof renderResult.then === 'function') {
|
||||
renderResult.then(function() {
|
||||
// Restore search input value
|
||||
self.$el.find('.oe_search_input').val(self._currentSearchValue);
|
||||
self.$el.find('.oe_clear_search').show();
|
||||
$countEl.show();
|
||||
}).catch(function(error) {
|
||||
console.error('Error rendering search results:', error);
|
||||
self._showError(_t('Error displaying results'));
|
||||
});
|
||||
} else {
|
||||
// If _renderBody doesn't return a promise, just update UI
|
||||
self.$el.find('.oe_search_input').val(self._currentSearchValue);
|
||||
self.$el.find('.oe_clear_search').show();
|
||||
$countEl.show();
|
||||
// Try to render if method exists
|
||||
if (typeof this._renderBody === 'function') {
|
||||
try {
|
||||
var renderResult = this._renderBody();
|
||||
if (renderResult && typeof renderResult.then === 'function') {
|
||||
renderResult.then(function() {
|
||||
self._restoreSearchUI();
|
||||
}).catch(function(error) {
|
||||
console.error('Render error:', error);
|
||||
self._restoreSearchUI();
|
||||
});
|
||||
} else {
|
||||
self._restoreSearchUI();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Render error:', error);
|
||||
self._restoreSearchUI();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error calling _renderBody:', error);
|
||||
self._showError(_t('Error displaying results'));
|
||||
} else {
|
||||
console.warn('_renderBody method not found');
|
||||
self._restoreSearchUI();
|
||||
}
|
||||
|
||||
} else {
|
||||
$countEl.text(_t('No records found')).show();
|
||||
this._showNoResults(searchValue);
|
||||
}
|
||||
},
|
||||
|
||||
_restoreSearchUI: function() {
|
||||
this.$el.find('.oe_search_input').val(this._currentSearchValue);
|
||||
this.$el.find('.oe_clear_search').show();
|
||||
},
|
||||
|
||||
_showNoResults: function(searchValue) {
|
||||
// Hide all data rows
|
||||
this.$el.find('.o_data_row').hide();
|
||||
|
||||
// Remove existing no results message
|
||||
this.$el.find('.oe_no_results').remove();
|
||||
|
||||
// Add no results message
|
||||
var colspan = this.$el.find('thead tr:first th').length || 1;
|
||||
var noResultsHtml = '<tr class="oe_no_results">' +
|
||||
'<td colspan="' + colspan + '" class="text-center text-muted" style="padding: 40px;">' +
|
||||
'<i class="fa fa-search" style="font-size: 3em; margin-bottom: 10px; display: block; opacity: 0.3;"></i>' +
|
||||
'<h4>' + _t('No records found') + '</h4>' +
|
||||
'<p>' + _t('No results for: ') + '<strong>' + _.escape(searchValue) + '</strong></p>' +
|
||||
'<p class="text-muted">' + _t('Try different keywords or check your filters') + '</p>' +
|
||||
'<p>' + _t('Search term: ') + '<strong>' + _.escape(searchValue) + '</strong></p>' +
|
||||
'</td></tr>';
|
||||
this.$el.find('tbody').append(noResultsHtml);
|
||||
},
|
||||
|
|
@ -436,69 +514,62 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
_clearSearch: function() {
|
||||
var self = this;
|
||||
|
||||
// Clear input
|
||||
this.$el.find('.oe_search_input').val('');
|
||||
this.$el.find('.oe_clear_search').hide();
|
||||
this.$el.find('.oe_search_count').hide();
|
||||
this.$el.find('.oe_search_loading').hide();
|
||||
|
||||
// Remove no results message
|
||||
this.$el.find('.oe_no_results').remove();
|
||||
|
||||
// Reset search state
|
||||
this._currentSearchValue = '';
|
||||
this._searchMode = false;
|
||||
|
||||
// If we have original records, restore them
|
||||
// Restore original records
|
||||
if (this._originalRecords && this._originalRecords.length > 0) {
|
||||
this._setCurrentRecords(this._originalRecords);
|
||||
|
||||
// Wrap _renderBody in try-catch for safety
|
||||
try {
|
||||
var renderResult = this._renderBody();
|
||||
if (renderResult && typeof renderResult.then === 'function') {
|
||||
renderResult.then(function() {
|
||||
self._originalRecords = null;
|
||||
}).catch(function(error) {
|
||||
console.error('Error restoring original records:', error);
|
||||
// Just show all rows as fallback
|
||||
if (typeof this._renderBody === 'function') {
|
||||
try {
|
||||
var renderResult = this._renderBody();
|
||||
if (renderResult && typeof renderResult.then === 'function') {
|
||||
renderResult.then(function() {
|
||||
self._originalRecords = null;
|
||||
}).catch(function(error) {
|
||||
console.error('Clear search render error:', error);
|
||||
self.$el.find('.o_data_row').show();
|
||||
self._originalRecords = null;
|
||||
});
|
||||
} else {
|
||||
self.$el.find('.o_data_row').show();
|
||||
self._originalRecords = null;
|
||||
});
|
||||
} else {
|
||||
// If _renderBody doesn't return a promise
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Clear search error:', error);
|
||||
self.$el.find('.o_data_row').show();
|
||||
self._originalRecords = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error calling _renderBody:', error);
|
||||
// Just show all rows as fallback
|
||||
} else {
|
||||
self.$el.find('.o_data_row').show();
|
||||
self._originalRecords = null;
|
||||
}
|
||||
} else {
|
||||
// Just show all rows
|
||||
this.$el.find('.o_data_row').show();
|
||||
}
|
||||
},
|
||||
|
||||
_normalizeArabic: function(text) {
|
||||
if (!text) return text;
|
||||
if (!text || typeof text !== 'string') return '';
|
||||
|
||||
// Normalizing Arabic text by removing common variations
|
||||
// Don't lowercase here as it's for the search query
|
||||
return text
|
||||
.replace(/[\u064B-\u065F]/g, '') // Remove diacritics
|
||||
.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
|
||||
.replace(/[\u0660-\u0669]/g, (d) => String.fromCharCode(d.charCodeAt(0) - 0x0660 + 0x0030))
|
||||
.replace(/[\u06F0-\u06F9]/g, (d) => String.fromCharCode(d.charCodeAt(0) - 0x06F0 + 0x0030))
|
||||
.replace(/[\u0622\u0623\u0625\u0627]/g, 'ا')
|
||||
.replace(/[\u0629]/g, 'ه')
|
||||
.replace(/[\u064A\u0626\u0649]/g, 'ي')
|
||||
.replace(/[\u0624\u0648]/g, 'و');
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
// Clear timeout if exists
|
||||
if (this._searchTimeout) {
|
||||
clearTimeout(this._searchTimeout);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue