Merge pull request #4447 from expsa/revert-4446-eltayar
Revert "🚀 [FEATURE] Enhanced Search Support for Many2Many/One2Many Fields in fims_general_search_tree_view"
This commit is contained in:
commit
b2543772f8
|
|
@ -3,17 +3,12 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
|
||||
var ListRenderer = require('web.ListRenderer');
|
||||
var ListController = require('web.ListController');
|
||||
var relational_fields = require('web.relational_fields');
|
||||
var core = require('web.core');
|
||||
var _t = core._t;
|
||||
var concurrency = require('web.concurrency');
|
||||
var rpc = require('web.rpc');
|
||||
var session = require('web.session');
|
||||
|
||||
// Get relational field classes
|
||||
var FieldOne2Many = relational_fields.FieldOne2Many;
|
||||
var FieldMany2Many = relational_fields.FieldMany2Many;
|
||||
|
||||
ListController.include({
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
|
@ -387,7 +382,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
|
||||
if (state.isFiltered && state.filteredCount >= 0) {
|
||||
this.$('.oe_search_count')
|
||||
.text(_t('عدد السجلات: ') + state.filteredCount)
|
||||
.text(_t('Records: ') + state.filteredCount)
|
||||
.removeClass('text-danger')
|
||||
.show();
|
||||
}
|
||||
|
|
@ -415,7 +410,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
}
|
||||
|
||||
var countText = isFiltered ?
|
||||
_t('عدد السجلات: ') + savedCount : '';
|
||||
_t('Records: ') + savedCount : '';
|
||||
|
||||
var html =
|
||||
'<div class="oe_search_container d-flex align-items-center">' +
|
||||
|
|
@ -517,305 +512,8 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
|
|||
}
|
||||
});
|
||||
|
||||
// ============= NEW: Relational Fields Support =============
|
||||
|
||||
// Mixin for common relational field functionality
|
||||
var RelationalFieldMixin = {
|
||||
|
||||
_searchState: null,
|
||||
_searchMutex: null,
|
||||
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this._initRelationalSearch();
|
||||
},
|
||||
|
||||
_initRelationalSearch: function() {
|
||||
this._searchState = {
|
||||
timer: null,
|
||||
value: '',
|
||||
isFiltered: false,
|
||||
filteredCount: 0,
|
||||
originalRecords: null,
|
||||
allRecords: null,
|
||||
lastSearchValue: ''
|
||||
};
|
||||
this._searchMutex = new concurrency.Mutex();
|
||||
},
|
||||
|
||||
_renderView: function() {
|
||||
var self = this;
|
||||
return this._super.apply(this, arguments).then(function(result) {
|
||||
if (self._shouldAddRelationalSearchBox()) {
|
||||
self._addRelationalSearchBox();
|
||||
self._storeOriginalRecords();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
_shouldAddRelationalSearchBox: function() {
|
||||
return this.renderer &&
|
||||
this.renderer.$el &&
|
||||
this.renderer.$el.find('.o_list_view').length > 0 &&
|
||||
!this.renderer.$el.find('.oe_relational_search_container').length &&
|
||||
this.value &&
|
||||
this.value.data &&
|
||||
this.value.data.length > 0;
|
||||
},
|
||||
|
||||
_storeOriginalRecords: function() {
|
||||
if (this.value && this.value.data && !this._searchState.originalRecords) {
|
||||
this._searchState.originalRecords = JSON.parse(JSON.stringify(this.value.data));
|
||||
this._searchState.allRecords = JSON.parse(JSON.stringify(this.value.data));
|
||||
}
|
||||
},
|
||||
|
||||
_addRelationalSearchBox: function() {
|
||||
var self = this;
|
||||
var savedValue = this._searchState.value || '';
|
||||
var savedCount = this._searchState.filteredCount || 0;
|
||||
var isFiltered = this._searchState.isFiltered || false;
|
||||
|
||||
var countText = isFiltered ?
|
||||
_t('عدد السجلات: ') + savedCount : '';
|
||||
|
||||
var html =
|
||||
'<div class="oe_relational_search_container d-flex align-items-center mb-2" style="padding: 8px; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border: 1px solid #dee2e6; border-radius: 6px; gap: 8px;">' +
|
||||
'<input type="text" class="oe_relational_search_input form-control flex-grow-1" ' +
|
||||
'placeholder="' + _.escape(_t('البحث في السجلات المرتبطة...')) + '" ' +
|
||||
'value="' + _.escape(savedValue) + '" ' +
|
||||
'autocomplete="off" style="height: 32px; border-radius: 4px;">' +
|
||||
'<button class="btn btn-secondary oe_relational_clear_search" style="display: ' +
|
||||
(savedValue ? 'inline-block' : 'none') + '; height: 32px; min-width: 60px;">' +
|
||||
'<i class="fa fa-times mr-1"></i>' + _t('مسح') +
|
||||
'</button>' +
|
||||
'<span class="oe_relational_search_count badge badge-success" style="display: ' +
|
||||
(isFiltered ? 'inline-block' : 'none') + '; font-size: 0.9em;">' +
|
||||
countText +
|
||||
'</span>' +
|
||||
'<span class="oe_relational_search_loading" style="display: none; color: #007bff;">' +
|
||||
'<i class="fa fa-spinner fa-spin"></i>' +
|
||||
'</span>' +
|
||||
'</div>';
|
||||
|
||||
var $listContainer = this.renderer.$el.find('.o_list_view').first();
|
||||
$listContainer.prepend($(html));
|
||||
|
||||
// Bind events
|
||||
this.renderer.$el.find('.oe_relational_search_input').on('keyup', function(e) {
|
||||
self._onRelationalSearchKeyUp(e);
|
||||
});
|
||||
|
||||
this.renderer.$el.find('.oe_relational_search_input').on('input', function(e) {
|
||||
self._onRelationalSearchInput(e);
|
||||
});
|
||||
|
||||
this.renderer.$el.find('.oe_relational_clear_search').on('click', function(e) {
|
||||
self._onRelationalClearSearch(e);
|
||||
});
|
||||
|
||||
this.renderer.$el.find('.oe_relational_search_input').on('keydown', function(e) {
|
||||
self._onRelationalSearchKeyDown(e);
|
||||
});
|
||||
},
|
||||
|
||||
_onRelationalSearchInput: function(e) {
|
||||
var currentValue = $(e.currentTarget).val();
|
||||
var hasValue = !!currentValue.trim();
|
||||
|
||||
this.renderer.$el.find('.oe_relational_clear_search').toggle(hasValue);
|
||||
|
||||
if (!hasValue) {
|
||||
this.renderer.$el.find('.oe_relational_search_count').hide();
|
||||
}
|
||||
},
|
||||
|
||||
_onRelationalSearchKeyUp: function(e) {
|
||||
var self = this;
|
||||
var value = $(e.currentTarget).val().trim();
|
||||
var ignoreKeys = [13, 27, 16, 17, 18, 91, 93, 37, 38, 39, 40,
|
||||
33, 34, 35, 36, 9, 20, 144, 145,
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123];
|
||||
|
||||
if (ignoreKeys.indexOf(e.which) !== -1) return;
|
||||
if (value === this._searchState.lastSearchValue) return;
|
||||
|
||||
this._searchState.lastSearchValue = value;
|
||||
|
||||
if (this._searchState.timer) {
|
||||
clearTimeout(this._searchState.timer);
|
||||
}
|
||||
|
||||
this._searchState.timer = setTimeout(function() {
|
||||
self._executeRelationalSearch(value);
|
||||
}, 300);
|
||||
},
|
||||
|
||||
_onRelationalSearchKeyDown: function(e) {
|
||||
if (e.which === 13) {
|
||||
e.preventDefault();
|
||||
if (this._searchState.timer) {
|
||||
clearTimeout(this._searchState.timer);
|
||||
}
|
||||
var value = $(e.currentTarget).val().trim();
|
||||
this._executeRelationalSearch(value);
|
||||
} else if (e.which === 27) {
|
||||
e.preventDefault();
|
||||
this._onRelationalClearSearch();
|
||||
}
|
||||
},
|
||||
|
||||
_onRelationalClearSearch: function() {
|
||||
this.renderer.$el.find('.oe_relational_search_input').val('').focus();
|
||||
this.renderer.$el.find('.oe_relational_clear_search').hide();
|
||||
this.renderer.$el.find('.oe_relational_search_count').hide();
|
||||
this._searchState.lastSearchValue = '';
|
||||
this._clearRelationalSearch();
|
||||
},
|
||||
|
||||
_executeRelationalSearch: function(value) {
|
||||
var self = this;
|
||||
|
||||
return this._searchMutex.exec(function() {
|
||||
if (!value || value.length === 0) {
|
||||
return self._clearRelationalSearch();
|
||||
}
|
||||
|
||||
return self._applyRelationalSearch(value);
|
||||
});
|
||||
},
|
||||
|
||||
_applyRelationalSearch: function(value) {
|
||||
var self = this;
|
||||
|
||||
if (!this._searchState.allRecords || this._searchState.allRecords.length === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var normalizedSearch = this._normalizeText(value.toLowerCase());
|
||||
var searchValues = [value.toLowerCase(), normalizedSearch].filter(function(v, i, arr) {
|
||||
return arr.indexOf(v) === i; // Remove duplicates
|
||||
});
|
||||
|
||||
var filteredRecords = this._searchState.allRecords.filter(function(record) {
|
||||
return self._recordMatchesSearch(record, searchValues);
|
||||
});
|
||||
|
||||
this._searchState.filteredCount = filteredRecords.length;
|
||||
this._searchState.isFiltered = true;
|
||||
this._searchState.value = value;
|
||||
|
||||
// Update the UI
|
||||
this._updateRelationalSearchUI(filteredRecords.length);
|
||||
|
||||
// Update the field's value with filtered data
|
||||
var newValue = _.clone(this.value);
|
||||
newValue.data = filteredRecords;
|
||||
newValue.count = filteredRecords.length;
|
||||
|
||||
// Re-render with filtered data
|
||||
return this._setValue(newValue, {forceChange: true}).then(function() {
|
||||
return Promise.resolve();
|
||||
});
|
||||
},
|
||||
|
||||
_recordMatchesSearch: function(record, searchValues) {
|
||||
var self = this;
|
||||
var recordData = record.data || {};
|
||||
|
||||
// Get searchable fields from the record
|
||||
var searchableValues = [];
|
||||
|
||||
// Add all text-like fields
|
||||
Object.keys(recordData).forEach(function(fieldName) {
|
||||
var value = recordData[fieldName];
|
||||
if (value !== null && value !== undefined) {
|
||||
if (typeof value === 'string') {
|
||||
searchableValues.push(value.toLowerCase());
|
||||
searchableValues.push(self._normalizeText(value.toLowerCase()));
|
||||
} else if (typeof value === 'number') {
|
||||
searchableValues.push(value.toString());
|
||||
} else if (Array.isArray(value) && value.length === 2) {
|
||||
// Many2one field format [id, display_name]
|
||||
if (typeof value[1] === 'string') {
|
||||
searchableValues.push(value[1].toLowerCase());
|
||||
searchableValues.push(self._normalizeText(value[1].toLowerCase()));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check if any search value matches any record value
|
||||
return searchValues.some(function(searchValue) {
|
||||
return searchableValues.some(function(recordValue) {
|
||||
return recordValue.indexOf(searchValue) !== -1;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_clearRelationalSearch: function() {
|
||||
if (!this._searchState.originalRecords) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
this._searchState.value = '';
|
||||
this._searchState.isFiltered = false;
|
||||
this._searchState.filteredCount = 0;
|
||||
this._searchState.lastSearchValue = '';
|
||||
|
||||
// Restore original records
|
||||
var newValue = _.clone(this.value);
|
||||
newValue.data = JSON.parse(JSON.stringify(this._searchState.originalRecords));
|
||||
newValue.count = this._searchState.originalRecords.length;
|
||||
|
||||
// Hide search UI elements
|
||||
if (this.renderer && this.renderer.$el) {
|
||||
this.renderer.$el.find('.oe_relational_search_count').hide();
|
||||
this.renderer.$el.find('.oe_relational_search_loading').hide();
|
||||
}
|
||||
|
||||
// Re-render with original data
|
||||
return this._setValue(newValue, {forceChange: true});
|
||||
},
|
||||
|
||||
_updateRelationalSearchUI: function(count) {
|
||||
if (this.renderer && this.renderer.$el) {
|
||||
this.renderer.$el.find('.oe_relational_search_count')
|
||||
.text(_t('عدد السجلات: ') + count)
|
||||
.removeClass('text-danger')
|
||||
.show();
|
||||
this.renderer.$el.find('.oe_relational_clear_search').show();
|
||||
}
|
||||
},
|
||||
|
||||
_normalizeText: function(text) {
|
||||
if (!text) return '';
|
||||
return text
|
||||
.replace(/[\u064B-\u065F]/g, '') // Remove Arabic diacritics
|
||||
.replace(/[٠-٩]/g, function(d) { // Convert Arabic-Indic digits
|
||||
return String.fromCharCode(d.charCodeAt(0) - 0x0660 + 0x0030);
|
||||
})
|
||||
.replace(/[۰-۹]/g, function(d) { // Convert Extended Arabic-Indic digits
|
||||
return String.fromCharCode(d.charCodeAt(0) - 0x06F0 + 0x0030);
|
||||
})
|
||||
.replace(/[آأإا]/g, 'ا') // Normalize Alef variations
|
||||
.replace(/ة/g, 'ه') // Normalize Taa Marbouta
|
||||
.replace(/[يئى]/g, 'ي') // Normalize Yaa variations
|
||||
.trim();
|
||||
}
|
||||
};
|
||||
|
||||
// Apply the mixin to FieldOne2Many
|
||||
FieldOne2Many.include(RelationalFieldMixin);
|
||||
|
||||
// Apply the mixin to FieldMany2Many
|
||||
FieldMany2Many.include(RelationalFieldMixin);
|
||||
|
||||
return {
|
||||
ListController: ListController,
|
||||
ListRenderer: ListRenderer,
|
||||
FieldOne2Many: FieldOne2Many,
|
||||
FieldMany2Many: FieldMany2Many
|
||||
ListRenderer: ListRenderer
|
||||
};
|
||||
});
|
||||
Loading…
Reference in New Issue