diff --git a/application/controllers/components/Filter.php b/application/controllers/components/Filter.php index 6d5df98b7..acfea6257 100644 --- a/application/controllers/components/Filter.php +++ b/application/controllers/components/Filter.php @@ -162,13 +162,13 @@ class Filter extends FHC_Controller } /** - * Remove a custom filter by its filter_id + * Remove a custom filter by its filterId */ public function removeCustomFilter() { - $filter_id = $this->input->post('filter_id'); + $filterId = $this->input->post('filterId'); - if ($this->filtercmptlib->removeCustomFilter($filter_id) == true) + if ($this->filtercmptlib->removeCustomFilter($filterId) == true) { $this->outputJsonSuccess('Removed'); } @@ -243,7 +243,7 @@ class Filter extends FHC_Controller array( 'filterUniqueId' => $filterUniqueId, 'filterType' => $filterType, - 'filterId' => $this->input->get('filter_id') + 'filterId' => $this->input->get('filterId') ) ); diff --git a/public/js/RESTClient.js b/public/js/RESTClient.js index df2113076..acf5738a1 100644 --- a/public/js/RESTClient.js +++ b/public/js/RESTClient.js @@ -216,8 +216,6 @@ export const CoreRESTClient = { for (var prop in axiosParameters) axiosCallObj[prop] = axiosParameters[prop]; } - console.log(axiosCallObj); - // Perform the ajax call via axios return axios(axiosCallObj); } diff --git a/public/js/components/Fetch.js b/public/js/components/Fetch.js index 7e867e740..ce09b4efd 100644 --- a/public/js/components/Fetch.js +++ b/public/js/components/Fetch.js @@ -1,57 +1,109 @@ +/** + * Copyright (C) 2022 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * + */ export const CoreFetchCmpt = { + emits: ['dataFetched'], // this component can emit the event dataFetched that it is catched by this component itself + props: { + refresh: { // to refresh this component + type: Boolean + }, + apiFunction: { // the function to call, must return a Promise + required: true, + type: Function + }, + apiFunctionParameters: {} // parameters for the apiFunction, type mixed, optional + }, + watch: { + /** + * If the refresh property is changed then call fetchData + */ + refresh: function (newValue, oldValue) { + this.fetchData(); + } + }, data: function() { return { - loading: false, - error: false, - errorMessage: null + loading: false, // if in loading or not + error: false, // if an error occurred while loading data + errorMessage: null // the error message }; }, - emits: ['dataFetched'], - props: { - apiFunction: Function - }, - created: function () { + created: function() { this.fetchData(); }, methods: { + /** + * + */ fetchData: function() { - // Loader started - this.loading = true; + this.loading = true; // loader started // Checks if the apifunction is a callable function if (typeof this.apiFunction == "function") { // Call the function stored in apiFunction - let apiFunctionResult = this.apiFunction(); + let apiFunctionResult = this.apiFunction(this.apiFunctionParameters); // It is expected that the function returns a Promise if (apiFunctionResult instanceof Promise) { - apiFunctionResult.then(this._success).catch(this._error).then(this._finally); + apiFunctionResult + .then(this.success) // on success + .catch(this.error) // on error + .then(this.finally); // finally in any case } else // otherwise display an error { - this._setError("The called apiFunction does not return a Promise"); + this.setError("The called apiFunction does not return a Promise"); } } else // otherwise display an error { - this._setError("Property apiFunction is not a function"); + this.setError("Property apiFunction is not a function"); } }, - _setError(errorMessage) { - this.loading = false; - this.error = true; - this.errorMessage = errorMessage; + /** + * + */ + setError: function(errorMessage) { + this.loading = false; // loading ended + this.error = true; // error occurred + this.errorMessage = errorMessage; // save the error message }, - _success: function(response) { - this.$emit('dataFetched', response.data); + /** + * + */ + success: function(response) { + this.$emit('dataFetched', response.data); // trigger the event dataFetched }, - _error: function(error) { - this._setError(error.message); + /** + * + */ + error: function(error) { + this.setError(error.message); }, - _finally: function() { - this.loading = false; + /** + * + */ + finally: function() { + this.loading = false; // loading ended } }, template: ` diff --git a/public/js/components/filter/API.js b/public/js/components/filter/API.js new file mode 100644 index 000000000..b4e45941b --- /dev/null +++ b/public/js/components/filter/API.js @@ -0,0 +1,155 @@ +/** + * Copyright (C) 2022 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import {CoreRESTClient} from '../../RESTClient.js'; + +// +const CORE_FILTER_CMPT_TIMEOUT = 7000; + +/** + * + */ +export const CoreFilterAPIs = { + /** + * + */ + saveCustomFilter: function(wsParams) { + return CoreRESTClient.post( + 'components/Filter/saveCustomFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + customFilterName: wsParams.customFilterName + }, + { + timeout: CORE_FILTER_CMPT_TIMEOUT + } + ); + }, + /** + * + */ + applyFilterFields: function(wsParams) { + return CoreRESTClient.post( + 'components/Filter/applyFilterFields', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterFields: wsParams.filterFields + }, + { + timeout: CORE_FILTER_CMPT_TIMEOUT + } + ); + }, + /** + * + */ + addFilterField: function(wsParams) { + return CoreRESTClient.post( + 'components/Filter/addFilterField', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterField: wsParams.filterField + }, + { + timeout: CORE_FILTER_CMPT_TIMEOUT + } + ); + }, + /** + * + */ + addSelectedField: function(wsParams) { + return CoreRESTClient.post( + 'components/Filter/addSelectedField', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + selectedField: wsParams.selectedField + }, + { + timeout: CORE_FILTER_CMPT_TIMEOUT + } + ); + }, + /** + * + */ + removeSelectedField: function(wsParams) { + return CoreRESTClient.post( + 'components/Filter/removeSelectedField', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + selectedField: wsParams.selectedField + }, + { + timeout: CORE_FILTER_CMPT_TIMEOUT + } + ); + }, + /** + * + */ + removeFilterField: function(wsParams) { + return CoreRESTClient.post( + 'components/Filter/removeFilterField', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterField: wsParams.filterField + }, + { + timeout: CORE_FILTER_CMPT_TIMEOUT + } + ); + }, + /** + * + */ + getFilterById: function(wsParams) { + return CoreRESTClient.get( + 'components/Filter/getFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterId: wsParams.filterId + }, + { + timeout: CORE_FILTER_CMPT_TIMEOUT + } + ); + }, + /** + * + */ + getFilter: function(wsParams) { + return CoreRESTClient.get( + 'components/Filter/getFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType + }, + { + timeout: CORE_FILTER_CMPT_TIMEOUT + } + ); + } +} + diff --git a/public/js/components/Filter.js b/public/js/components/filter/Filter.js similarity index 66% rename from public/js/components/Filter.js rename to public/js/components/filter/Filter.js index 5c356b5fb..4b195df1c 100644 --- a/public/js/components/Filter.js +++ b/public/js/components/filter/Filter.js @@ -1,28 +1,63 @@ -import {CoreFetchCmpt} from '../components/Fetch.js'; +/** + * Copyright (C) 2022 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ -const CORE_FILTER_CMPT_TIMEOUT = 7000; +import {CoreFilterAPIs} from './API.js'; +import {CoreRESTClient} from '../../RESTClient.js'; +import {CoreFetchCmpt} from '../../components/Fetch.js'; +/** + * + */ export const CoreFilterCmpt = { emits: ['nwNewEntry'], + components: { + CoreFetchCmpt + }, + props: { + filterType: { + type: String, + required: true + } + }, data() { return { + // FilterCmpt properties fields: null, fieldsToDisplay: null, dataset: null, selectedFields: null, notSelectedFields: null, filterFields: null, - notFilterFields: null + notFilterFields: null, + + // FetchCmpt binded properties + fetchCmptRefresh: false, + fetchCmptApiFunction: null, + fetchCmptApiFunctionParams: null, + fetchCmptDataFetched: null }; }, - components: { - CoreFetchCmpt + created() { + this.getFilter(); // get the filter data }, - created() {}, updated() { let filterCmptTablesorter = $("#filterTableDataset"); - // Checks if the table contains data (rows) + // Checks if the table contains data records if (filterCmptTablesorter.find("tbody:empty").length == 0 && filterCmptTablesorter.find("tr:empty").length == 0) { @@ -37,32 +72,160 @@ export const CoreFilterCmpt = { $.tablesorter.updateAll(filterCmptTablesorter[0].config, true, null); } }, - props: { - filterType: { - type: String, - required: true - } - }, methods: { - saveCustomFilter(el) { + /** + * + */ + getFilter: function() { + // + this.startFetchCmpt(CoreFilterAPIs.getFilter, null, this.render); + }, + /** + * + */ + render: function(response) { - CoreRESTClient.post( - 'components/Filter/saveCustomFilter', + if (CoreRESTClient.hasData(response)) + { + let data = CoreRESTClient.getData(response); + this.dataset = data.dataset; + this.fields = data.fields; + this.selectedFields = data.selectedFields; + this.notSelectedFields = this.fields.filter(x => this.selectedFields.indexOf(x) === -1); + + this.filterFields = []; + let tmpFilterFields = []; + for (let i = 0; i < data.datasetMetadata.length; i++) + { + for (let j = 0; j < data.filters.length; j++) + { + if (data.datasetMetadata[i].name == data.filters[j].name) + { + let filter = data.filters[j]; + filter.type = data.datasetMetadata[i].type; + + this.filterFields.push(filter); + tmpFilterFields.push(filter.name); + break; + } + } + } + + this.notFilterFields = this.fields.filter(x => tmpFilterFields.indexOf(x) === -1); + this.setFieldsToDisplay(data); + this.setSideMenu(data); + } + else + { + console.error(CoreRESTClient.getError(response)); + } + }, + /** + * + */ + setFieldsToDisplay: function(data) { + + let arrayFieldsToDisplay = []; + + if (data.hasOwnProperty("selectedFields") && $.isArray(data.selectedFields)) + { + if (data.hasOwnProperty("columnsAliases") && $.isArray(data.columnsAliases)) + { + for (let sfc = 0; sfc < data.selectedFields.length; sfc++) + { + for (let fc = 0; fc < data.fields.length; fc++) + { + if (data.selectedFields[sfc] == data.fields[fc]) + { + arrayFieldsToDisplay[sfc] = data.columnsAliases[fc]; + } + } + } + } + else + { + arrayFieldsToDisplay = data.selectedFields; + } + } + + this.fieldsToDisplay = arrayFieldsToDisplay; + }, + /** + * + */ + setSideMenu: function(data) { + // Set the menu + let filters = data.sideMenu.filters; + let personalFilters = data.sideMenu.personalFilters; + let filtersArray = []; + + for (let filtersCount = 0; filtersCount < filters.length; filtersCount++) + { + let link = filters[filtersCount].link; + + if (link == null) link = '#'; + + filtersArray[filtersArray.length] = { + link: link + filters[filtersCount].filterId, + description: filters[filtersCount].desc, + sort: filtersCount, + onClickCall: this.handlerGetFilterById + }; + } + + this.$emit( + 'nwNewEntry', + [{ + link: "#", + description: "Filters", + icon: "filter", + children: filtersArray + }] + ); + }, + /** + * Used to start/refresh the FetchCmpt + */ + startFetchCmpt: function(apiFunction, apiFunctionParameters, dataFetchedCallback) { + // Assign the function api of the FetchCmpt binded property + this.fetchCmptApiFunction = apiFunction; + + // In case a null value is provided set the parameters as an empty object + if (apiFunctionParameters == null) apiFunctionParameters = {}; + + // Always needed parameters + apiFunctionParameters.filterUniqueId = FHC_JS_DATA_STORAGE_OBJECT.called_path + "/" + FHC_JS_DATA_STORAGE_OBJECT.called_method; + apiFunctionParameters.filterType = this.filterType; + + // Assign parameters to the FetchCmpt binded properties + this.fetchCmptApiFunctionParams = apiFunctionParameters; + // Assign data fetch callback to the FetchCmpt binded properties + this.fetchCmptDataFetched = dataFetchedCallback; + // Set the FetchCmpt binded property refresh to have the component to refresh + // NOTE: this should be the last one to be called because it triggers the FetchCmpt to start to refresh + this.fetchCmptRefresh === true ? this.fetchCmptRefresh = false : this.fetchCmptRefresh = true; + }, + + // ------------------------------------------------------------------------------------------------------------------ + // Event handlers + + /** + * + */ + handlerSaveCustomFilter: function(event) { + // + this.startFetchCmpt( + CoreFilterAPIs.saveCustomFilter, { - filterUniqueId: this._getCurrentPage(), - filterType: this.filterType, customFilterName: document.getElementById('customFilterName').value }, - { - timeout: CORE_FILTER_CMPT_TIMEOUT - } - ) - .then(function (response) { console.log(response); }) - .catch(function (error) { - console.error(error); - }); + this.getFilter + ); }, - applyFilterFields(el) { + /** + * + */ + handlerApplyFilterFields: function(event) { let filterFields = []; let filterFieldDivs = document.getElementById('filterFields').getElementsByTagName('div'); @@ -100,249 +263,89 @@ export const CoreFilterCmpt = { filterFields.push(filterField); } - CoreRESTClient.post( - 'components/Filter/applyFilterFields', + // + this.startFetchCmpt( + CoreFilterAPIs.applyFilterFields, { - filterUniqueId: this._getCurrentPage(), - filterType: this.filterType, filterFields: filterFields }, - { - timeout: CORE_FILTER_CMPT_TIMEOUT - } - ) - .then(this.fetchFilterData) - .catch(function (error) { - console.error(error); - }); - }, - addFilterField(el) { - - CoreRESTClient.post( - 'components/Filter/addFilterField', - { - filterUniqueId: this._getCurrentPage(), - filterType: this.filterType, - selectedField: el.currentTarget.value - }, - { - timeout: CORE_FILTER_CMPT_TIMEOUT - } - ) - .then(this.fetchFilterData) - .catch(function (error) { - console.error(error); - }); - }, - addSelectedField(el) { - - CoreRESTClient.post( - 'components/Filter/addSelectedField', - { - filterUniqueId: this._getCurrentPage(), - filterType: this.filterType, - selectedField: el.currentTarget.value - }, - { - timeout: CORE_FILTER_CMPT_TIMEOUT - } - ) - .then(this.fetchFilterData) - .catch(function (error) { - console.error(error); - }); - }, - removeSelectedField(el) { - - CoreRESTClient.post( - 'components/Filter/removeSelectedField', - { - filterUniqueId: this._getCurrentPage(), - filterType: this.filterType, - selectedField: el.currentTarget.getAttribute('field-to-remove') - }, - { - timeout: CORE_FILTER_CMPT_TIMEOUT - } - ) - .then(this.fetchFilterData) - .catch(function (error) { - console.error(error); - }); - }, - removeFilterField(el) { - - CoreRESTClient.post( - 'components/Filter/removeFilterField', - { - filterUniqueId: this._getCurrentPage(), - filterType: this.filterType, - filterField: el.currentTarget.getAttribute('field-to-remove') - }, - { - timeout: CORE_FILTER_CMPT_TIMEOUT - } - ) - .then(this.fetchFilterData) - .catch(function (error) { - console.error(error); - }); - }, - fetchFilterDataById(el) { - - var that = this; - - CoreRESTClient.get( - 'components/Filter/getFilter', - { - filterUniqueId: this._getCurrentPage(), - filterType: this.filterType, - filter_id: el.currentTarget.getAttribute("href").substring(1) - }, - { - timeout: CORE_FILTER_CMPT_TIMEOUT - } - ) - .then(function (response) { that.render(response.data); }) - .catch(function (error) { - console.error(error); - }); - }, - fetchFilterData() { - - var that = this; - - CoreRESTClient.get( - 'components/Filter/getFilter', - { - filterUniqueId: this._getCurrentPage(), - filterType: this.filterType // props!! - }, - { - timeout: CORE_FILTER_CMPT_TIMEOUT - } - ) - .then(function (response) { that.render(response.data); }) - .catch(function (error) { - console.error(error); - }); - }, - fetchFilterDataApi() { - - return CoreRESTClient.get( - 'components/Filter/getFilter', - { - filterUniqueId: this._getCurrentPage(), - filterType: this.filterType // props!! - }, - { - timeout: CORE_FILTER_CMPT_TIMEOUT - } + this.getFilter ); }, - _getCurrentPage: function() { - return FHC_JS_DATA_STORAGE_OBJECT.called_path + "/" + FHC_JS_DATA_STORAGE_OBJECT.called_method; - }, - render(response) { - - console.log('render'); - - if (CoreRESTClient.hasData(response)) - { - let data = CoreRESTClient.getData(response); - this.dataset = data.dataset; - this.fields = data.fields; - this.selectedFields = data.selectedFields; - this.notSelectedFields = this.fields.filter(x => this.selectedFields.indexOf(x) === -1); - - this.filterFields = []; - let tmpFilterFields = []; - for (let i = 0; i < data.datasetMetadata.length; i++) + /** + * + */ + handlerAddFilterField: function(event) { + // + this.startFetchCmpt( + CoreFilterAPIs.addFilterField, { - for (let j = 0; j < data.filters.length; j++) - { - if (data.datasetMetadata[i].name == data.filters[j].name) - { - let filter = data.filters[j]; - filter.type = data.datasetMetadata[i].type; - - this.filterFields.push(filter); - tmpFilterFields.push(filter.name); - break; - } - } - } - - this.notFilterFields = this.fields.filter(x => tmpFilterFields.indexOf(x) === -1); - this._setFieldsToDisplay(data); - this._setSideMenu(data); - } - else - { - console.error(CoreRESTClient.getError(response)); - } + selectedField: event.currentTarget.value + }, + this.getFilter + ); }, - _setFieldsToDisplay(data) { - - let arrayFieldsToDisplay = []; - - if (data.hasOwnProperty("selectedFields") && $.isArray(data.selectedFields)) - { - if (data.hasOwnProperty("columnsAliases") && $.isArray(data.columnsAliases)) + /** + * + */ + handlerAddSelectedField: function(event) { + // + this.startFetchCmpt( + CoreFilterAPIs.addSelectedField, { - for (let sfc = 0; sfc < data.selectedFields.length; sfc++) - { - for (let fc = 0; fc < data.fields.length; fc++) - { - if (data.selectedFields[sfc] == data.fields[fc]) - { - arrayFieldsToDisplay[sfc] = data.columnsAliases[fc]; - } - } - } - } - else - { - arrayFieldsToDisplay = data.selectedFields; - } - } - - this.fieldsToDisplay = arrayFieldsToDisplay; + selectedField: event.currentTarget.value + }, + this.getFilter + ); }, - _setSideMenu(data) { - // Set the menu - let filters = data.sideMenu.filters; - let personalFilters = data.sideMenu.personalFilters; - let filtersArray = []; - - for (let filtersCount = 0; filtersCount < filters.length; filtersCount++) - { - let link = filters[filtersCount].link; - - if (link == null) link = '#'; - - filtersArray[filtersArray.length] = { - link: link + filters[filtersCount].filter_id, - description: filters[filtersCount].desc, - sort: filtersCount, - onClickCall: this.fetchFilterDataById - }; - } - - this.$emit( - 'nwNewEntry', - [{ - link: "#", - description: "Filters", - icon: "filter", - children: filtersArray - }] + /** + * + */ + handlerRemoveSelectedField: function(event) { + // + this.startFetchCmpt( + CoreFilterAPIs.removeSelectedField, + { + selectedField: event.currentTarget.getAttribute('field-to-remove') + }, + this.getFilter + ); + }, + /** + * + */ + handlerRemoveFilterField: function(event) { + // + this.startFetchCmpt( + CoreFilterAPIs.removeFilterField, + { + selectedField: event.currentTarget.getAttribute('field-to-remove') + }, + this.getFilter + ); + }, + /** + * + */ + handlerGetFilterById: function(event) { + // + this.startFetchCmpt( + CoreFilterAPIs.getFilterById, + { + filterId: event.currentTarget.getAttribute("href").substring(1) + }, + this.render ); } }, template: ` - + +
@@ -359,12 +362,12 @@ export const CoreFilterCmpt = { type="button" class="btn-close" v-bind:field-to-remove="fieldToDisplay" - @click=removeSelectedField> + @click=handlerRemoveSelectedField>
-