From 0bf33ec7e9a1d1e8c370d523184887dd53bdc76e Mon Sep 17 00:00:00 2001 From: Guillaume Lajarige Date: Fri, 9 Mar 2018 11:10:03 +0000 Subject: [PATCH] Advanced search: WIP POC, integration with endpoint. SVN:b1162[5400] --- application/nicewebpage.class.inc.php | 1 + js/search/search_form_criteria.js | 61 +++++++++++++--- js/search/search_form_criteria_raw.js | 17 ++++- js/search/search_form_criteria_string.js | 77 ++++++++++++++++++++ js/search/search_form_handler.js | 92 +++++++++++++----------- 5 files changed, 198 insertions(+), 50 deletions(-) create mode 100644 js/search/search_form_criteria_string.js diff --git a/application/nicewebpage.class.inc.php b/application/nicewebpage.class.inc.php index d1a4a65bd..4a3338bd6 100644 --- a/application/nicewebpage.class.inc.php +++ b/application/nicewebpage.class.inc.php @@ -53,6 +53,7 @@ class NiceWebPage extends WebPage $this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_handler.js'); $this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria.js'); $this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_raw.js'); + $this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_string.js'); $this->add_ready_script( <<< EOF //add new widget called TruncatedList to properly display truncated lists when they are sorted diff --git a/js/search/search_form_criteria.js b/js/search/search_form_criteria.js index 0047f9a10..aca7fe7b0 100644 --- a/js/search/search_form_criteria.js +++ b/js/search/search_form_criteria.js @@ -9,16 +9,19 @@ $(function() // default options options: { - get_current_values_callback: 'getCurrentValues', - set_current_values_callback: function(me, oEvent, oData){ console.log('Search form criteria: set_current_values_callback must be overloaded, this is the default callback.'); }, - ref: '', operator: '=', values: [], oql: '', is_removable: true, + + field: { + label: '', + }, is_modified: false, // TODO: change this on value change and remove oql property value }, + + handler: null, // the constructor _create: function() @@ -27,6 +30,9 @@ $(function() this.element.addClass('search_form_criteria'); + // Link search form handler + this.handler = this.element.closest('.search_form_handler'); + // GetData this.element.bind('itop.search.criteria.get_data', function(oEvent, oData){ return me._onGetData(oData); @@ -78,25 +84,25 @@ $(function() }, - // Public methods - getCurrentValues: function() + // Protected methods + _remove: function() { - var aValues = this.options.values; - return aValues; + this.element.remove(); + this.handler.triggerHandler('itop.search.criteria.removed'); }, // Event callbacks _onGetData: function(oData) { - var oData = { + var oCriteriaData = { 'ref': this.options.ref, 'operator': this.options.operator, 'values': this.options.values, 'is_removable': this.options.is_removable, 'oql': this.options.oql, }; - return oData; + return oCriteriaData; }, @@ -104,6 +110,8 @@ $(function() // - Prepare element DOM structure _prepareElement: function() { + var me = this; + // Prepare base DOM structure //this.options.ref+' '+this.options.operator+' '+this.options.values this.element @@ -111,10 +119,19 @@ $(function() .append('
') .append('
'); + // Bind events + // - Toggler + this.element.find('.sfc_toggle, .sfc_title').on('click', function(){ + me.element.find('.sfc_form_group').toggle(); + }); + // Removable / locked decoration if(this.options.is_removable === true) { this.element.append('
'); + this.element.find('.sfc_close').on('click', function(){ + me._remove(); + }); } else { @@ -134,6 +151,32 @@ $(function() }, + // Values helpers + _getValuesAsText: function() + { + var aValues = []; + for(var iValueIdx in this.options.values) + { + aValues.push(this.options.values[iValueIdx].value); + } + + return aValues.join(', '); + }, + _makeOQLExpression: function() + { + var aValues = []; + var sOQL = ''; + + for(var iValueIdx in this.options.values) + { + aValues.push( '\'' + this.options.values[iValueIdx].value + '\'' ); + } + sOQL += '(`' + this.options.ref + '`) ' + this.options.operator + ' ' + aValues.join(', ') + ')'; + + return sOQL; + }, + + // Debug helpers // - Show a trace in the javascript console _trace: function(sMessage, oData) diff --git a/js/search/search_form_criteria_raw.js b/js/search/search_form_criteria_raw.js index 7b628316d..0d0935904 100644 --- a/js/search/search_form_criteria_raw.js +++ b/js/search/search_form_criteria_raw.js @@ -43,11 +43,26 @@ $(function() this._super( key, value ); }, + // DOM element helpers + _prepareElement: function() + { + this._super(); + + // Remove toggler as it's a non sense here + this.element.find('.sfc_toggle').remove(); + }, _setTitle: function(sTitle) { if(sTitle === undefined) { - sTitle = this.options.oql; + if(this.options.oql !== '') + { + sTitle = this.options.oql; + } + else + { + sTitle = this._makeOQLExpression(); + } } this._super(sTitle); }, diff --git a/js/search/search_form_criteria_string.js b/js/search/search_form_criteria_string.js new file mode 100644 index 000000000..5035772c6 --- /dev/null +++ b/js/search/search_form_criteria_string.js @@ -0,0 +1,77 @@ +//iTop Search form criteria string +; +$(function() +{ + // the widget definition, where 'itop' is the namespace, + // 'search_form_criteria_string' the widget name + $.widget( 'itop.search_form_criteria_string', $.itop.search_form_criteria, + { + // default options + options: + { + }, + + // the constructor + _create: function() + { + var me = this; + + this._super(); + this.element.addClass('search_form_criteria_string'); + }, + // called when created, and later when changing options + _refresh: function() + { + + }, + // events bound via _bind are removed automatically + // revert other modifications here + _destroy: function() + { + this.element.removeClass('search_form_criteria_string'); + this._super(); + }, + // _setOptions is called with a hash of all options that are changing + // always refresh when changing options + _setOptions: function() + { + this._superApply(arguments); + }, + // _setOption is called for each individual option that is changing + _setOption: function( key, value ) + { + this._super( key, value ); + }, + + // DOM element helpers + _prepareElement: function() + { + var me = this; + + this._super(); + + // TODO: Refactor this after UI mockups + var oInputElem = $(''); + oInputElem.on('change', function(){ + var sValue = $(this).val(); + + me.options.values = [{ + value: sValue, + label: sValue, + }]; + me._setTitle(); + + me.handler.triggerHandler('itop.search.criteria.value_changed'); + }) + .appendTo(this.element.find('.sfc_form_group')); + }, + _setTitle: function(sTitle) + { + if(sTitle === undefined) + { + sTitle = this.options.field.label + ': ' + this._getValuesAsText(); + } + this._super(sTitle); + }, + }); +}); diff --git a/js/search/search_form_handler.js b/js/search/search_form_handler.js index 5ed8e3335..888867b8f 100644 --- a/js/search/search_form_handler.js +++ b/js/search/search_form_handler.js @@ -51,7 +51,7 @@ $(function() // }, ], }, - 'supported_criterion_types': ['raw'], + 'supported_criterion_types': ['raw', 'string'], 'default_criteria_type': 'raw', }, @@ -60,6 +60,7 @@ $(function() { active_criterion: null, more_criterion: null, + results_area: null, }, // the constructor @@ -113,16 +114,23 @@ $(function() { var me = this; + // Form events + // - Prevent regular form submission (eg. hitting "Enter" in inputs) + this.element.on('submit', function(oEvent){ + oEvent.preventDefault(); + }); + // Criteria events this.element.bind('itop.search.criteria.value_changed', function(oEvent, oData){ me._onCriteriaValueChanged(oData); }); + this.element.bind('itop.search.criteria.removed', function(oEvent, oData){ + me._onCriteriaRemoved(oData); + }); }, // - Update search option of the widget _updateSearch: function() { - var me = this; - // Criterion // - Note: As of today, only a "or" level with a "and" is supported, so the following part // will need some refactoring when introducing new stuff. @@ -170,10 +178,6 @@ $(function() // Prepare content this._prepareExistingCriterion(); this._prepareMoreCriterionMenu(); - - // TODO: Delete this - oCriterionAreaElem.append( $('') ); - this.options.submit_button_selector = '.sf_submit_btn'; }, // - Prepare existing criterion _prepareExistingCriterion: function() @@ -209,7 +213,7 @@ $(function() this.elements.more_criterion.find('> .sf_mc_list').append(oFieldElem); } - // Bind event + // Bind events this.elements.more_criterion.find('.sf_mc_field').on('click', function(){ // Prepare new criterion data var oData = { @@ -235,6 +239,8 @@ $(function() oResultAreaElem = $('
').appendTo(this.element); } oResultAreaElem.addClass('sf_results_area'); + + this.elements.results_area = oResultAreaElem; }, @@ -242,15 +248,24 @@ $(function() // - Add a criteria to the form _addCriteria: function(oData) { - var sRef = oData.ref + var sRef = oData.ref; var sType = this._getCriteriaTypeFromFieldRef(sRef); if(sType !== null) { var sWidgetClass = 'search_form_criteria' + '_' + sType; - oCriteriaElem = $('
') + + // Add some informations from the field + oData.field = { + label: this.options.search.fields[sRef].label, + }; + + // Create DOM element + var oCriteriaElem = $('
') .addClass('sf_criteria') .appendTo(this.elements.active_criterion); + + // Instanciate widget $.itop[sWidgetClass](oData, oCriteriaElem); } else @@ -282,12 +297,16 @@ $(function() return sType; }, // Criteria handlers - // - Event value changed _onCriteriaValueChanged: function(oData) { this._updateSearch(); this._submit(); }, + _onCriteriaRemoved: function(oData) + { + this._updateSearch(); + this._submit(); + }, // Button handlers _onSubmitClick: function(oEvent) @@ -295,15 +314,12 @@ $(function() // Assertion: the search is already up to date this._submit(); }, - _onCancelClick: function(oEvent) - { - // TODO - }, // Submit handlers _submit: function() { + var me = this; var oData = { 'params': JSON.stringify({ 'base_oql': this.options.search.base_oql, @@ -311,51 +327,47 @@ $(function() }), }; + // Show loader + this._showLoader(); + + // Do submit $.post( this.options.endpoint, - oData, - function(oResponse, sStatus, oXHR){ - console.log('POST success', oResponse, sStatus, oXHR); - } + oData ) - .done(function(a,b,c,d){ console.log('POST done', a,b,c,d);}) - .fail(function(a,b,c,d){ console.log('POST fail', a,b,c,d);}) - .always(function(a,b,c,d){ console.log('POST always', a,b,c,d);}); + .done(function(oResponse, sStatus, oXHR){ me._onSubmitSuccess(oResponse); }) + .fail(function(oResponse, sStatus, oXHR){ me._onSubmitFailure(oResponse); }) + .always(function(oResponse, sStatus, oXHR){ me._onSubmitAlways(oResponse); }); }, // - Called on form submit successes - _onSubmitSuccess: function(oData, sFormPath) + _onSubmitSuccess: function(oData) { - // TODO - // if(oData.form.updated_fields !== undefined) - // { - // this.element.find('[data-form-path="' + sFormPath + '"]').trigger('update_form', {updated_fields: oData.form.updated_fields}); - // } + this.elements.results_area.html(oData); }, // - Called on form submit failures - _onSubmitFailure: function(oData, sFormPath) + _onSubmitFailure: function(oData) { - // TODO + // TODO: onSubmitFailure callback }, // - Called after form submits - _onSubmitAlways: function(oData, sFormPath) + _onSubmitAlways: function(oData) { - // TODO - // // Check all touched AFTER ajax is complete, otherwise the renderer will redraw the field in the mean time. - // this.element.find('[data-form-path="' + sFormPath + '"]').trigger('validate'); - // this._enableFormAfterLoading(); + this._hideLoader(); }, // Global helpers // - Show loader - _disableFormBeforeLoading: function() + _showLoader: function() { - // TODO + // TODO: Show loader + this._trace('Show loader'); }, - // - Remove loader - _enableFormAfterLoading: function() + // - Hide loader + _hideLoader: function() { - // TODO + // TODO: Hide loader + this._trace('Hide loader'); },