diff --git a/app/portainer/__module.js b/app/portainer/__module.js index 0dc526a5c..4e9b38c96 100644 --- a/app/portainer/__module.js +++ b/app/portainer/__module.js @@ -166,8 +166,7 @@ angular url: '/endpoints', views: { 'content@': { - templateUrl: './views/endpoints/endpoints.html', - controller: 'EndpointsController', + component: 'endpointsView', }, }, }; diff --git a/app/portainer/components/datatables/endpoints-datatable/endpointsDatatableController.js b/app/portainer/components/datatables/endpoints-datatable/endpointsDatatableController.js index 04ebb9c0d..5447e2b8e 100644 --- a/app/portainer/components/datatables/endpoints-datatable/endpointsDatatableController.js +++ b/app/portainer/components/datatables/endpoints-datatable/endpointsDatatableController.js @@ -1,9 +1,10 @@ +import _ from 'lodash-es'; + angular.module('portainer.app').controller('EndpointsDatatableController', [ '$scope', '$controller', 'DatatableService', 'PaginationService', - 'Notifications', function ($scope, $controller, DatatableService, PaginationService) { angular.extend(this, $controller('GenericDatatableController', { $scope: $scope })); @@ -15,18 +16,18 @@ angular.module('portainer.app').controller('EndpointsDatatableController', [ pageNumber: 1, }); - this.paginationChanged = function () { - this.state.loading = true; - this.state.filteredDataSet = []; - const start = (this.state.pageNumber - 1) * this.state.paginatedItemLimit + 1; - this.retrievePage(start, this.state.paginatedItemLimit, this.state.textFilter) - .then((data) => { - this.state.filteredDataSet = data.endpoints; - this.state.totalFilteredDataSet = data.totalCount; - }) - .finally(() => { - this.state.loading = false; - }); + this.paginationChanged = async function () { + try { + this.state.loading = true; + this.state.filteredDataSet = []; + const start = (this.state.pageNumber - 1) * this.state.paginatedItemLimit + 1; + const { endpoints, totalCount } = await this.retrievePage(start, this.state.paginatedItemLimit, this.state.textFilter); + this.state.filteredDataSet = endpoints; + this.state.totalFilteredDataSet = totalCount; + this.refreshSelectedItems(); + } finally { + this.state.loading = false; + } }; this.onPageChange = function (newPageNumber) { @@ -44,9 +45,17 @@ angular.module('portainer.app').controller('EndpointsDatatableController', [ this.onTextFilterChange = function () { var filterValue = this.state.textFilter; DatatableService.setDataTableTextFilters(this.tableKey, filterValue); + this.resetSelectionState(); this.paginationChanged(); }; + /** + * Overriden + */ + this.uniq = function () { + return _.uniqBy(_.concat(this.state.filteredDataSet, this.state.selectedItems), 'Id'); + }; + /** * Overridden */ @@ -55,6 +64,14 @@ angular.module('portainer.app').controller('EndpointsDatatableController', [ this.paginationChanged(); }; + this.refreshSelectedItems = function () { + _.forEach(this.state.filteredDataSet, (item) => { + if (_.filter(this.state.selectedItems, (i) => i.Id == item.Id).length > 0) { + item.Checked = true; + } + }); + }; + /** * Overridden */ diff --git a/app/portainer/components/datatables/genericDatatableController.js b/app/portainer/components/datatables/genericDatatableController.js index aacbd1d3f..6c3c1a80c 100644 --- a/app/portainer/components/datatables/genericDatatableController.js +++ b/app/portainer/components/datatables/genericDatatableController.js @@ -77,7 +77,7 @@ angular.module('portainer.app').controller('GenericDatatableController', [ item.Checked = !item.Checked; this.state.firstClickedItem = item; } - this.state.selectedItems = _.uniq(_.concat(this.state.selectedItems, this.state.filteredDataSet)).filter((i) => i.Checked); + this.state.selectedItems = this.uniq().filter((i) => i.Checked); if (event && this.state.selectAll && this.state.selectedItems.length !== this.state.filteredDataSet.length) { this.state.selectAll = false; } @@ -96,6 +96,13 @@ angular.module('portainer.app').controller('GenericDatatableController', [ this.onSelectionChanged(); }; + /** + * Override this method to change the uniqness filter when selecting items + */ + this.uniq = function () { + return _.uniq(_.concat(this.state.filteredDataSet, this.state.selectedItems)); + }; + /** * Override this method to allow/deny selection */ diff --git a/app/portainer/views/endpoints/endpoints.html b/app/portainer/views/endpoints/endpoints.html index 386a49ea0..72ac0b64f 100644 --- a/app/portainer/views/endpoints/endpoints.html +++ b/app/portainer/views/endpoints/endpoints.html @@ -1,17 +1,17 @@ - + -
+
diff --git a/app/portainer/views/endpoints/endpoints.js b/app/portainer/views/endpoints/endpoints.js new file mode 100644 index 000000000..7b42d0936 --- /dev/null +++ b/app/portainer/views/endpoints/endpoints.js @@ -0,0 +1,6 @@ +import { EndpointsController } from './endpointsController'; + +angular.module('portainer.app').component('endpointsView', { + templateUrl: './endpoints.html', + controller: EndpointsController, +}); diff --git a/app/portainer/views/endpoints/endpointsController.js b/app/portainer/views/endpoints/endpointsController.js index 24e6ba4e9..703743dc1 100644 --- a/app/portainer/views/endpoints/endpointsController.js +++ b/app/portainer/views/endpoints/endpointsController.js @@ -1,67 +1,72 @@ -import angular from 'angular'; +import { map } from 'lodash'; import EndpointHelper from '@/portainer/helpers/endpointHelper'; import { getEnvironments } from '@/portainer/environments/environment.service'; -angular.module('portainer.app').controller('EndpointsController', EndpointsController); +export class EndpointsController { + /* @ngInject */ + constructor($state, $async, EndpointService, GroupService, ModalService, Notifications, EndpointProvider, StateManager) { + Object.assign(this, { + $state, + $async, + EndpointService, + GroupService, + ModalService, + Notifications, + EndpointProvider, + StateManager, + }); -function EndpointsController($q, $scope, $state, $async, EndpointService, GroupService, ModalService, Notifications, EndpointProvider, StateManager) { - $scope.state = { - loadingMessage: '', - }; + this.state = { + loadingMessage: '', + }; - $scope.setLoadingMessage = setLoadingMessage; - function setLoadingMessage(message) { - $scope.state.loadingMessage = message; + this.setLoadingMessage = this.setLoadingMessage.bind(this); + this.getPaginatedEndpoints = this.getPaginatedEndpoints.bind(this); + this.removeAction = this.removeAction.bind(this); } - $scope.removeAction = removeAction; - function removeAction(endpoints) { - ModalService.confirmDeletion('This action will remove all configurations associated to your environment(s). Continue?', (confirmed) => { + setLoadingMessage(message) { + this.state.loadingMessage = message; + } + + removeAction(endpoints) { + this.ModalService.confirmDeletion('This action will remove all configurations associated to your environment(s). Continue?', (confirmed) => { if (!confirmed) { return; } - return $async(removeActionAsync, endpoints); + return this.$async(async () => { + try { + await Promise.all(endpoints.map(({ Id }) => this.EndpointService.deleteEndpoint(Id))); + this.Notifications.success('Environments successfully removed', map(endpoints, 'Id').join(', ')); + } catch (err) { + this.Notifications.error('Failure', err, 'Unable to remove environment'); + } + + const id = this.EndpointProvider.endpointID(); + // If the current endpoint was deleted, then clean endpoint store + if (endpoints.some((e) => e.Id === id)) { + this.StateManager.cleanEndpoint(); + // trigger sidebar rerender + this.applicationState.endpoint = {}; + } + + this.$state.reload(); + }); }); } - async function removeActionAsync(endpoints) { - for (let endpoint of endpoints) { + getPaginatedEndpoints(start, limit, search) { + return this.$async(async () => { try { - await EndpointService.deleteEndpoint(endpoint.Id); - - Notifications.success('Environment successfully removed', endpoint.Name); - } catch (err) { - Notifications.error('Failure', err, 'Unable to remove environment'); - } - } - - const endpointId = EndpointProvider.endpointID(); - // If the current endpoint was deleted, then clean endpoint store - if (endpoints.some((item) => item.Id === endpointId)) { - StateManager.cleanEndpoint(); - // trigger sidebar rerender - $scope.applicationState.endpoint = {}; - } - - $state.reload(); - } - - $scope.getPaginatedEndpoints = getPaginatedEndpoints; - function getPaginatedEndpoints(start, limit, search) { - const deferred = $q.defer(); - $q.all({ - endpoints: getEnvironments({ start, limit, query: { search, excludeSnapshots: true } }), - groups: GroupService.groups(), - }) - .then(function success(data) { - var endpoints = data.endpoints.value; - var groups = data.groups; + const [{ value: endpoints, totalCount }, groups] = await Promise.all([ + getEnvironments({ start, limit, query: { search, excludeSnapshots: true } }), + this.GroupService.groups(), + ]); EndpointHelper.mapGroupNameToEndpoint(endpoints, groups); - deferred.resolve({ endpoints: endpoints, totalCount: data.endpoints.totalCount }); - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to retrieve environment information'); - }); - return deferred.promise; + return { endpoints, totalCount }; + } catch (err) { + this.Notifications.error('Failure', err, 'Unable to retrieve environment information'); + } + }); } }