diff options
author | talasila <talasila@research.att.com> | 2017-02-07 15:03:57 -0500 |
---|---|---|
committer | talasila <talasila@research.att.com> | 2017-02-07 15:05:15 -0500 |
commit | 4ad39a5c96dd99acf819ce189b13fec946d7506b (patch) | |
tree | a1449286441947cc3d07a45227fa0d6f978e1a7d /ecomp-portal-FE/client/app/views | |
parent | 5500448cbd1f374d0ac743ee2fd636fe2d3c0027 (diff) |
Initial OpenECOMP Portal commit
Change-Id: I804b80e0830c092e307da1599bd9fbb5c3e2da77
Signed-off-by: talasila <talasila@research.att.com>
Diffstat (limited to 'ecomp-portal-FE/client/app/views')
114 files changed, 14144 insertions, 0 deletions
diff --git a/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.js b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.js new file mode 100644 index 00000000..4d77a97c --- /dev/null +++ b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.js @@ -0,0 +1,214 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; +(function () { + class NewAdminModalCtrl { + constructor($log, adminsService, $scope, confirmBoxService, utilsService, $location) { + + let init = () => { + this.isSaving = false; + this.originalApps = []; + /* istanbul ignore if */ + if ($scope.ngDialogData && $scope.ngDialogData.selectedUser && $scope.ngDialogData.dialogState) { + this.selectedUser = $scope.ngDialogData.selectedUser; + this.dialogState = $scope.ngDialogData.dialogState; + this.isShowBack = false; + if (this.dialogState === 2) { + this.getAdminAppsRoles(); + } + } else { + this.isShowBack = true; + this.selectedUser = null; + this.dialogState = 1; + } + + $log.info('NewAdminModalCtrl::initiated'); + this.appsOrder = []; + }; + + let orderList = (apps) => { + this.appsOrder = []; + for (var i = 0; i < apps.length; i++) { + if (apps[i].isAdmin) { + this.appsOrder.push(apps[i].id); + } + } + }; + + this.orderFilter = app => { + if (!app || !app.id || !this.appsOrder.length) { + return; + } + return this.appsOrder.indexOf(app.id); + }; + + this.getAdminAppsRoles = () => { + if (!this.selectedUser || !this.selectedUser.orgUserId) { + $log.error('No user is selected / searchUsers is InProgress'); + this.dialogState = 1; + return; + } + adminsService.getAdminAppsRoles(this.selectedUser.orgUserId).then(roles => { + $log.debug('apps roles res: ', JSON.stringify(roles)); + if (!roles.appsRoles) { + return; + } + + this.adminAppsRoles = []; + for (var i = 0; i < roles.appsRoles.length; i++) { + if (!roles.appsRoles[i].restrictedApp) { + $log.debug('pushing: {id: ', roles.appsRoles[i].id, + 'name: ', roles.appsRoles[i].appName, + 'restrictedApp: ', roles.appsRoles[i].restrictedApp, + 'isAdmin: ', roles.appsRoles[i].isAdmin, '}'); + this.adminAppsRoles.push({ + id: roles.appsRoles[i].id, + appName: roles.appsRoles[i].appName, + isAdmin: roles.appsRoles[i].isAdmin, + restrictedApp: roles.appsRoles[i].restrictedApp + }); + } + } + this.dialogState = 2; + this.adminAppsRoles = this.adminAppsRoles.sort(getSortOrder("appName")); + + orderList(roles.appsRoles); + if (this.appsOrder != null) { + for (var j = 0; j < this.appsOrder.length; j++) { + this.originalApps.push(this.appsOrder[j]); + } + } + }).catch(err => { + $log.error(err); + }); + }; + + let getSortOrder = (prop) => { + return function (a, b) { + if (a[prop].toLowerCase() > b[prop].toLowerCase()) { + return 1; + } else if (a[prop].toLowerCase() < b[prop].toLowerCase()) { + return -1; + } + return 0; + } + } + + this.setSelectedUser = (user) => { + $log.debug('selected user: ', user); + this.selectedUser = user; + }; + + this.unadminApp = (app) => { + confirmBoxService.deleteItem(app.appName).then(confirmed => { + if (confirmed === true) { + app.isAdmin = false; + this.appsOrder.splice(this.appsOrder.indexOf(app.id), 1); + } + }).catch(err => { + $log(err); + }); + }; + + this.updateAdminAppsRoles = () => { + if (!this.selectedUser || !this.selectedUser.orgUserId || !this.adminAppsRoles) { + return; + } + this.isSaving = true; + $log.debug('going to update user: ' + this.selectedUser.orgUserId + ' with app roles: ' + JSON.stringify(this.adminAppsRoles)); + confirmBoxService.makeAdminChanges('Are you sure you want to make these admin changes?') + .then(confirmed => { + adminsService.updateAdminAppsRoles({ + orgUserId: this.selectedUser.orgUserId, + appsRoles: this.adminAppsRoles + }) + .then(res => { + $log.debug('Admin apps roles updated successfully!', res); + this.remindToAddUserIfNecessary(); + $scope.closeThisDialog(true); + }).catch(err => { + $log.error('NewAdminModalCtrl.updateAdminAppsRoles:: Failed - ' + err); + }).finally(()=> { + this.isSaving = false; + }) + }); + }; + + this.navigateBack = () => { + if (this.dialogState === 1) { + } + if (this.dialogState === 2) { + this.dialogState = 1; + } + }; + + init(); + + $scope.$watch('newAdmin.selectedNewApp', (newVal) => { + if (!newVal || newVal.isAdmin === undefined) { + return; + } + let app = _.find(this.adminAppsRoles, {id: newVal.id}); + if (app) { + app.isAdmin = true; + this.appsOrder.push(app.id); + } + this.selectedNewApp = null; + }); + + $scope.$on('$stateChangeStart', e => { + e.preventDefault(); + }); + + this.remindToAddUserIfNecessary = () => { + var adminAddedToNewApp = false; + if ((this.originalApps != null) && (this.originalApps.length > 0)) { + for (var i = 0; i < this.appsOrder.length; i++) { + var foundApp = false; + for (var j = 0; j < this.originalApps.length; j++) { + if (this.originalApps[j] == this.appsOrder[i]) { + foundApp = true; + } + } + if (foundApp == false) { + adminAddedToNewApp = true; + break; + } + } + } else { + adminAddedToNewApp = true; + } + + if (adminAddedToNewApp == true) { + confirmBoxService.confirm('Add this person as an application user? This allows them to access the application from OpenECOMP Portal. Press OK to go to the Add Users page.') + .then(confirmed => { + if (confirmed == true) { + $location.path('/users'); + } + }); + } + } + + } + } + NewAdminModalCtrl.$inject = ['$log', 'adminsService', '$scope', 'confirmBoxService', 'utilsService', '$location']; + angular.module('ecompApp').controller('NewAdminModalCtrl', NewAdminModalCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js new file mode 100644 index 00000000..c0f73078 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js @@ -0,0 +1,130 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +describe('Controller: NewAdminCtrl ', () => { + beforeEach(module('ecompApp')); + + beforeEach(inject((_CacheFactory_)=> { + _CacheFactory_.destroyAll(); + })); + + + let newCtrl, $controller, $q, $rootScope, $log; + + beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_)=> { + [$controller, $q, $rootScope, $log] = [_$controller_, _$q_, _$rootScope_, _$log_]; + })); + + let deferredAdminAppsRoles, deferredUpdateRolesRes; + let adminsServiceMock; + beforeEach(()=> { + [deferredAdminAppsRoles, deferredUpdateRolesRes] = [$q.defer(), $q.defer()]; + + adminsServiceMock = jasmine.createSpyObj('adminsServiceMock', ['getAdminAppsRoles', 'updateAdminAppsRoles']); + + adminsServiceMock.getAdminAppsRoles.and.returnValue(deferredAdminAppsRoles.promise); + adminsServiceMock.updateAdminAppsRoles.and.returnValue(deferredUpdateRolesRes.promise); + + newCtrl = $controller('NewAdminModalCtrl', { + $log: $log, + adminsService: adminsServiceMock, + $scope: $rootScope + }); + }); + + it('should init default values when loading the controller', ()=> { + expect(newCtrl.dialogState).toBe(1); + expect(newCtrl.selectedUser).toBe(null); + }); + + it('should populate admin apps roles and move to the next screen when adminsService.getAdminAppsRoles succeeded', ()=> { + let userApps = {appsRoles: [{id: 1, isAdmin: false}, {id: 2, isAdmin: true}]}; + deferredAdminAppsRoles.resolve(userApps); + + newCtrl.selectedUser = {orgUserId: 'orgUserId'}; + + newCtrl.getAdminAppsRoles(); + $rootScope.$apply(); + + expect(adminsServiceMock.getAdminAppsRoles).toHaveBeenCalledWith(newCtrl.selectedUser.userId); + expect(newCtrl.adminAppsRoles).toEqual(userApps.appsRoles); + expect(newCtrl.dialogState).toBe(2); + }); + + it('should log the error when adminsService.getAdminAppsRoles fails', ()=> { + spyOn($log, 'error'); + deferredAdminAppsRoles.reject('some error'); + + newCtrl.searchUsersInProgress = false; + newCtrl.selectedUser = {orgUserId: 'orgUserId'}; + + newCtrl.getAdminAppsRoles(); + $rootScope.$apply(); + + expect($log.error).toHaveBeenCalled(); + }); + it('should log the error when trying to getAdminAppsRoles without selecting user ', ()=> { + spyOn($log, 'error'); + + newCtrl.searchUsersInProgress = false; + newCtrl.selectedUser = null; + + newCtrl.getAdminAppsRoles(); + $rootScope.$apply(); + + expect($log.error).toHaveBeenCalled(); + }); + + it('should set isAdmin as true when adding app via the dropdown menu', ()=> { + newCtrl.adminAppsRoles = [{id: 1, isAdmin: false},{id: 2, isAdmin: true}]; + $rootScope.$apply('newAdmin.selectedNewApp = null'); + $rootScope.$apply('newAdmin.selectedNewApp = {id: 1, isAdmin: true}'); + + expect(newCtrl.adminAppsRoles[0].isAdmin).toBe(true); + expect(newCtrl.selectedNewApp).toBe(null); + }); + + it('should close the modal when updating apps roles succeeded', ()=> { + $rootScope.closeThisDialog = () => {}; + spyOn($rootScope,'closeThisDialog'); + + newCtrl.selectedUser = {orgUserId: 'orgUserId'}; + newCtrl.adminAppsRoles = [{id: 1}]; + + deferredUpdateRolesRes.resolve(); + newCtrl.updateAdminAppsRoles(); + $rootScope.$apply(); + + expect(adminsServiceMock.updateAdminAppsRoles).toHaveBeenCalledWith({userId: newCtrl.selectedUser.userId, appsRoles: newCtrl.adminAppsRoles}); + expect($rootScope.closeThisDialog).toHaveBeenCalled(); + }); + it('should log the error when updating apps roles fails', ()=> { + newCtrl.selectedUser = {orgUserId: 'orgUserId'}; + newCtrl.adminAppsRoles = [{id: 1}]; + + spyOn($log,'error'); + deferredUpdateRolesRes.reject(); + newCtrl.updateAdminAppsRoles(); + $rootScope.$apply(); + expect($log.error).toHaveBeenCalled(); + }); +}); diff --git a/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.html b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.html new file mode 100644 index 00000000..0a8fc727 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.html @@ -0,0 +1,66 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="new-admin-modal"> + <div class="search-users" ng-show="newAdmin.dialogState===1"> + <search-users search-title="New Admin" + selected-user="newAdmin.selectedUser"></search-users> + <div class="dialog-control"> + <div id="search-users-button-next" class="next-button" ng-click="newAdmin.selectedUser && newAdmin.getAdminAppsRoles()" + ng-class="{disabled: !newAdmin.selectedUser}">Next + </div> + <div id="search-users-button-cancel" class="cancel-button" ng-click="closeThisDialog()">Cancel</div> + </div> + </div> + <div id="div-admin-app-roles" class="admin-app-roles" ng-show="newAdmin.dialogState===2"> + <div class="title" id="title" + ng-bind="newAdmin.selectedUser.firstName + ' ' + newAdmin.selectedUser.lastName + ' (' + newAdmin.selectedUser.orgUserId + ')'"></div> + <div class="app-roles-main"> + <div id="div-app-roles-main-title" class="app-roles-main-title"> + <span class="left">Administrates:</span> + </div> + <div class="dropdown-container"> + <div class ="right_arrow_down"></div> + <div class="select-input custom-select-wrap"> + <select class="new-administrated-app" id="dropdown-select-app" + ui-select2 ng-model="newAdmin.selectedNewApp" + data-placeholder="Select application" + ng-options="app as app.appName for app in (filteredApps = (newAdmin.adminAppsRoles | filter:{isAdmin:'false'})) track by app.id " + ng-disabled="!filteredApps.length"> + <option id="option-select-app" value="" disabled style="display: none;">Select application</option> + </select> + </div> + </div> + <div class="admin-roles-list"> + <div ng-repeat="app in (newAdmin.adminAppsRoles | orderBy:newAdmin.orderFilter) track by app.id" ng-show="app.isAdmin"> + <div id="select-app-{{app.appName.split(' ').join('-')}}" class="administrated-application" ng-bind="app.appName | elipsis: 57"></div> + <i id="i-delete-application" class="ion-trash-b" ng-click="newAdmin.unadminApp(app)"></i> + </div> + </div> + <div class="dialog-control"> + <span class="ecomp-save-spinner" ng-show="newAdmin.isSaving"></span> + <div id="button-back" ng-show="newAdmin.isShowBack" class="back-button" ng-click="newAdmin.navigateBack()">Back</div> + <div id="div-updateAdminAppsRoles" class="next-button" ng-click="newAdmin.updateAdminAppsRoles()" + ng-class="{disabled: false}">Save + </div> + <div id="div-cancel-button" class="cancel-button" ng-click="closeThisDialog()">Cancel</div> + </div> + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.less b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.less new file mode 100644 index 00000000..b992c928 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.less @@ -0,0 +1,99 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .new-admin-modal { + display:block; + overflow:auto; + min-height: 450px; + + .search-users { + } + + .admin-app-roles { + .title { + .n18r; + border-bottom: @a 3px solid; + + } + + .app-roles-main { + margin-top: 16px; + .app-roles-main-title { + .n14r; + margin-bottom: 8px; + .left { + display: inline-block; + } + .right { + display: inline-block; + color: @a; + float: right; + cursor: pointer; + } + } + + .select-input{ + width: 460px; + } + + .new-administrated-app { + height: 30px; + line-height: 30px; + + border: 1px solid @p; + margin-bottom: 8px; + border-radius: 2px; + padding-left: 6px; + padding-top: 0; + .o14r; + } + + .admin-roles-list { + height: 240px; + overflow-y: auto; + } + + .administrated-application { + width: 460px; + height: 30px; + border: 1px solid @p; + margin-bottom: 8px; + border-radius: 2px; + padding: 6px; + .o14r; + display: inline-block; + + } + + .delete-application { + .ico_trash_default; + display: inline-block; + vertical-align: 4px; + cursor: pointer; + position: relative; + top: 6px; + color: transparent; + margin-left: 8px; + } + + } + + } +} + diff --git a/ecomp-portal-FE/client/app/views/admins/admins.controller.js b/ecomp-portal-FE/client/app/views/admins/admins.controller.js new file mode 100644 index 00000000..402ff9d6 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/admins/admins.controller.js @@ -0,0 +1,151 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class AdminsCtrl { + constructor($log, adminsService, applicationsService, ngDialog) { + + let allPortalsFilterObject = {index: 0, title: 'All applications', value: ''}; + + let updateTableData = () => { + this.isLoadingTable = true; + adminsService.getAccountAdmins().then(res=> { + if (!res || !res.length) { + $log.info('AdminsCtrl::updateTableData: no admins err handling'); + this.adminsTableData = []; + return; + } + this.adminsTableData = res; + }).catch(err=> { + $log.error('AdminsCtrl::updateTableData error: ', err); + }).finally(() => { + this.isLoadingTable = false; + }); + }; + + let init = () => { + $log.info('AdminsCtrl:: ::initializing...'); + this.isLoadingTable = false; + this.availableApps = [allPortalsFilterObject]; + this.filterByApp = this.availableApps[0]; + + /* Table general configuration params*/ + this.searchString= ''; + /*Table data*/ + this.adminsTableHeaders = ['First Name', 'Last Name', 'User ID', 'Applications']; + this.adminsTableData = []; + updateTableData(); + }; + + applicationsService.getAvailableApps().then(res=> { + this.availableApps = [allPortalsFilterObject]; + var res1 = res.sort(getSortOrder("title")); + var realAppIndex = 1; + for(let i=1; i<=res1.length; i++){ + if (!res1[i-1].restrictedApp) { + $log.debug('AdminsCtrl:getAvailableApps:: pushing: {index: ', realAppIndex, 'title: ', res1[i - 1].title, + '| value: ', res1[i -1].value, '}'); + this.availableApps.push({ + index: realAppIndex, + title: res1[i - 1].title, + value: res1[i - 1].value + }); + realAppIndex = realAppIndex + 1; + } else { + $log.debug('AdminsCtrl:getAvailableApps:: Restricted/URL only App will not be used = ' + res1[i - 1].title); + } + } + }).catch(err=> { + $log.error(err); + this.availableApps = [allPortalsFilterObject]; + }); + + let getSortOrder = (prop) => { + return function(a, b) { + if (a[prop].toLowerCase() > b[prop].toLowerCase()) { + return 1; + } else if (a[prop].toLowerCase() < b[prop].toLowerCase()) { + return -1; + } + return 0; + } + } + + init(); + + this.portalsRowFilter = (input) => { + if (this.filterByApp.value === '') { + return true; + } + return _.find(input.apps, {appName: this.filterByApp.value}) !== undefined; + }; + + this.openAddNewAdminModal = (user) => { + let data = null; + if(user){ + data = { + dialogState: 2, + selectedUser:{ + orgUserId: user.orgUserId, + firstName: user.firstName, + lastName: user.lastName + } + } + } + ngDialog.open({ + templateUrl: 'app/views/admins/add-admin-dialogs/new-admin.modal.html', + controller: 'NewAdminModalCtrl', + controllerAs: 'newAdmin', + data: data + }).closePromise.then(needUpdate => { + if(needUpdate.value === true){ + $log.debug('AdminsCtrl:openAddNewAdminModal:: updating table data...'); + updateTableData(); + } + }); + }; + + this.openEditUserModal = (loginId) => { + var data = { + loginId : loginId, + updateRemoteApp : false, + appId : this.selectedApp!=null?this.selectedApp.id:'' + } + var modalInstance = ngDialog.open({ + templateUrl: 'app/views/header/user-edit/edit-user.tpl.html', + controller: 'editUserController', + data: data, + resolve: { + message: function message() { + var message = { + type: 'Contact', + }; + return message; + } + } + }).closePromise.then(needUpdate => { + updateTableData(); + }); + } + } + } + AdminsCtrl.$inject = ['$log', 'adminsService', 'applicationsService', 'ngDialog']; + angular.module('ecompApp').controller('AdminsCtrl', AdminsCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/admins/admins.controller.spec.js b/ecomp-portal-FE/client/app/views/admins/admins.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/admins/admins.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/admins/admins.less b/ecomp-portal-FE/client/app/views/admins/admins.less new file mode 100644 index 00000000..cdf0c0f5 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/admins/admins.less @@ -0,0 +1,47 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .admins-page-main { + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + .admins-table { + width: @table-width; + margin: @table-margin; + + .table-control { + .table-dropdown-filter{ + width: @table-dropdown-filter-width; + display: @table-dropdown-filter-display; + } + } + + .table-body { + cursor: pointer; + } + } +} + diff --git a/ecomp-portal-FE/client/app/views/admins/admins.tpl.html b/ecomp-portal-FE/client/app/views/admins/admins.tpl.html new file mode 100644 index 00000000..543b9ead --- /dev/null +++ b/ecomp-portal-FE/client/app/views/admins/admins.tpl.html @@ -0,0 +1,82 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-main"> + <div class="w-ecomp-main-container"> + <div class="admins-page-main" id="contentId"> + <div id="title" class="w-ecomp-main-view-title">Admins</div> + <div class="admins-table"> + <div class="table-control"> + <div class="table-dropdown-filter"> + <div class="c-ecomp-att-abs-select default"> + <div class="form-field" id="dropdown-apps" + att-select="admins.availableApps" + ng-model="admins.filterByApp"></div> + </div> + </div> + <input id="input-table-search" class="table-search" type="text" + placeholder="Search in entire table" + ng-model="admins.searchString"/> + <div id="Add-Admin-button" class="add-button" ng-click="admins.openAddNewAdminModal()">Add Admin</div> + </div> + <span class="ecomp-spinner" ng-show="admins.isLoadingTable"></span> + <div class="c-ecomp-att-abs-table default" ng-hide="admins.isLoadingTable"> + <table att-table + table-data="admins.adminsTableData" + search-string="admins.searchString" + view-per-page="admins.viewPerPageIgnored" + current-page="admins.currentPageIgnored" + total-page="admins.totalPageIgnored"> + <thead att-table-row type="header"> + <tr> + <th id="th-first-name" att-table-header key="firstName" default-sort="a">{{admins.adminsTableHeaders[0]}}</th> + <th id="th-last-name" att-table-header key="lastName" sortable="true">{{admins.adminsTableHeaders[1]}}</th> + <th id="th-userId" att-table-header key="userId" sortable="true">{{admins.adminsTableHeaders[2]}}</th> + <th id="th-apps" att-table-header key="apps" sortable="false">{{admins.adminsTableHeaders[3]}}</th> + </tr> + </thead> + <tbody att-table-row type="body" + class="table-body" + style="overflow-y:scroll" + row-repeat="rowData in admins.adminsTableData | filter: admins.portalsRowFilter"> + <tr ng-click="admins.openAddNewAdminModal(rowData)" > + <td att-table-body class="td-first"> + <div id="div-{{rowData.userId}}-{{rowData.firstName}}" ng-bind="rowData.firstName"></div> + </td> + <td att-table-body> + <div id="div-{{rowData.userId}}-{{rowData.lastName}}" ng-bind="rowData.lastName"></div> + </td> + <td att-table-body> + <div id="div-{{rowData.userId}}" style="float: left;" ng-bind="rowData.orgUserId"></div> + <div> + <span style="float: left; margin-left:15px" class="ion-person" ng-click="admins.openEditUserModal(rowData.orgUserId);$event.stopPropagation()"></span> + </div> + </td> + <td att-table-body> + <div id="apps-{{rowData.userId}}-{{app.appName}}" ng-repeat="app in rowData.apps" ng-bind="app.appName"></div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + </div> + +</div> diff --git a/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.js b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.js new file mode 100644 index 00000000..8c6ffe48 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.js @@ -0,0 +1,244 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class AppDetailsModalCtrl { + constructor($scope, $log, applicationsService, errorMessageByCode, + ECOMP_URL_REGEX,userProfileService, $cookies, confirmBoxService) { + let emptyImg = null; + this.emptyImgForPreview = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; + + let newAppModel = { + 'id': null, + 'name': null, + 'imageUrl': null, + 'description': null, + 'notes': null, + 'url': null, + 'alternateUrl': null, + 'restUrl': null, + 'isOpen': false, + 'username': null, + 'appPassword': null, + 'thumbnail': emptyImg, + 'isEnabled': true, + 'restrictedApp': false + }; + + let init = () => { + $log.info('AppDetailsModalCtrl::init'); + this.isSaving = false; + if($scope.ngDialogData && $scope.ngDialogData.app){ + $log.debug('AppDetailsModalCtrl:init:: Edit app mode for', $scope.ngDialogData.app); + this.isEditMode = true; + this.app = _.clone($scope.ngDialogData.app); + }else{ + $log.debug('AppDetailsModalCtrl:init:: New app mode'); + this.isEditMode = false; + this.app = _.clone(newAppModel); + } + this.originalImage = null + }; + + this.ECOMP_URL_REGEX = ECOMP_URL_REGEX; + + this.imageApi = {}; + this.removeImage = () => { + $log.debug('AppDetailsModalCtrl:removeImage:: entering removeImage'); + + confirmBoxService.confirm("Are you sure you want to remove the image?").then(isConfirmed => { + if(isConfirmed){ + this.imageApi.clearFile(); + this.app.thumbnail = emptyImg; + this.originalImage = null; + this.app.imageUrl = null; + } + }).catch(err => { + $log.error('AppDetailsModalCtrl:removeImage error:: ',err); + }); + }; + + this.conflictMessages = {}; + this.scrollApi = {}; + let handleConflictErrors = err => { + if(!err.data){ + return; + } + if(!err.data.length){ + err.data = [err.data] + } + _.forEach(err.data, item => { + _.forEach(item.fields, field => { + this.conflictMessages[field.name] = errorMessageByCode[item.errorCode]; + $scope.appForm[field.name].$setValidity('conflict', false); + watchOnce[field.name](); + }); + }); + this.scrollApi.scrollTop(); + }; + + let resetConflict = fieldName => { + delete this.conflictMessages[fieldName]; + if($scope.appForm[fieldName]){ + $scope.appForm[fieldName].$setValidity('conflict', true); + } + }; + + + let emptyCookies = () => { + $log.debug('AppDetailsModalCtrl:emptyCookies:: entering emptyCookies'); + userProfileService.getUserProfile() + .then(profile=> { + $scope.userId = profile.orgUserId; + $log.debug('AppDetailsModalCtrl:emptyCookies for the following userId: ' + profile.orgUserId); + if ($cookies.getObject($scope.userId + '_apps') != undefined && $cookies.getObject($scope.userId + '_apps') != null) { + $cookies.remove($scope.userId + '_apps'); + $log.debug('AppDetailsModalCtrl:emptyCookies removed: ' + $scope.userId + '_apps'); + } + if ($cookies.getObject($scope.userId + '_widget') != undefined && $cookies.getObject($scope.userId + '_widget') != null) { + $cookies.remove($scope.userId + '_widget'); + $log.debug('AppDetailsModalCtrl:emptyCookies removed: ' + $scope.userId + '_widget'); + } + }).catch(err => { + $log.error('AppDetailsModalCtrl:emptyCookies error:: '+ JSON.stringify(err)); + }); + }; + + + let watchOnce = { + name: () => { + let unregisterName = $scope.$watch('appDetails.app.name', (newVal, oldVal) => { + if(newVal.toLowerCase() !== oldVal.toLowerCase()){ + resetConflict('name'); + unregisterName(); + } + }); + }, + url: () => { + let unregisterUrl = $scope.$watch('appDetails.app.url', (newVal, oldVal) => { + if(newVal.toLowerCase() !== oldVal.toLowerCase()) { + resetConflict('url'); + unregisterUrl(); + } + }); + } + }; + + this.saveChanges = () => { + if($scope.appForm.$invalid){ + return; + } + this.isSaving = true; + + if (this.app.restrictedApp) { + this.app.restUrl = null; + this.app.isOpen = true; + this.app.username = null; + this.app.appPassword = null; + this.app.uebTopicName = null; + this.app.uebKey = null; + this.app.uebSecret = null; + } + if(this.isEditMode){ + applicationsService.updateOnboardingApp(this.app) + .then(() => { + $log.debug('AppDetailsModalCtrl:updateOnboardingApp:: App update succeeded!'); + $scope.closeThisDialog(true); + emptyCookies(); + }).catch(err => { + if(err.status === 409){ + handleConflictErrors(err); + } + if(err.status === 500){ + confirmBoxService.showInformation('There was a problem updating the application changes. ' + + 'Please try again later.').then(isConfirmed => {}); + } + if(err.status === 403){ + confirmBoxService.showInformation('There was a problem updating the application changes. ' + + 'Please try again. If the problem persists, then try again later.').then(isConfirmed => {}); + } + $log.error('applicationsService:updateOnboardingApp error status:: '+ err.status); + $log.error('applicationsService:updateOnboardingApp error:: '+ JSON.stringify(err)); + }).finally(()=>{ + this.isSaving = false; + var objOffsetVersion = objAgent.indexOf("MSIE"); + if (objOffsetVersion != -1) { + $log.debug('AppDetailsModalCtrl:updateOnboardingApp:: Browser is IE, forcing Refresh'); + $window.location.reload(); + } + }); + }else{ + applicationsService.addOnboardingApp(this.app) + .then(() => { + $log.debug('App creation succeeded!'); + $scope.closeThisDialog(true); + emptyCookies(); + }).catch(err => { + if(err.status === 409){ + handleConflictErrors(err); + } + if(err.status === 500){ + confirmBoxService.showInformation('There was a problem adding the application information. ' + + 'Please try again later.').then(isConfirmed => {}); + } + $log.error('applicationsService:addOnboardingApp error status:: '+ err.status); + $log.error('applicationsService:addOnboardingApp error:: '+ JSON.stringify(err)); + }).finally(()=>{ + this.isSaving = false; + var objOffsetVersion = objAgent.indexOf("MSIE"); + if (objOffsetVersion != -1) { + $log.debug('applicationsService:addOnboardingApp:: Browser is IE, forcing Refresh'); + $window.location.reload(); + } + }); + } + }; + + + init(); + + $scope.$watch('appDetails.originalImage', (newVal, oldVal) => { + if((!newVal || !newVal.resized) && !this.app.imageUrl){ + if (!newVal) { + $log.debug('applicationsService:$scope.$watch:: originalImage: newVal is null'); + } else { + $log.debug('applicationsService:$scope.$watch:: originalImage: newVal is not resized and no imageUrl'); + } + this.app.imageUrl = null; + this.app.thumbnail = emptyImg; + return; + } + + if(!(_.isEqual(newVal, oldVal))){ + $log.debug('applicationsService:$scope.$watch:: thumbnail updated!'); + this.app.imageUrl = null; + this.app.thumbnail = newVal.resized.dataURL; + } + }); + + $scope.$on('$stateChangeStart', e => { + e.preventDefault(); + }); + } + } + AppDetailsModalCtrl.$inject = ['$scope', '$log', 'applicationsService', 'errorMessageByCode', + 'ECOMP_URL_REGEX','userProfileService','$cookies', 'confirmBoxService']; + angular.module('ecompApp').controller('AppDetailsModalCtrl', AppDetailsModalCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.spec.js b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.html b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.html new file mode 100644 index 00000000..57f4e61d --- /dev/null +++ b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.html @@ -0,0 +1,183 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="application-details-modal"> + <div class="title">Application Details</div> + <div class="app-properties-main" scroll-top="appDetails.scrollApi"> + <form name="appForm" novalidate autocomplete="off"> + <script type="text/javascript"> + document.getElementById("appForm").reset(); + </script> + <div class="left-container"> + <div class="property"> + <input id="checkbox-app-is-restricted" type="checkbox" class="checkbox-field" ng-disabled="appDetails.isEditMode" ng-model="appDetails.app.restrictedApp" ng-checked="appDetails.app.restrictedApp"/> + <div class="property-label checkbox-label">Hyperlink only application</div> + </div> + <div class="property required"> + <div class="property-label">Application Name</div> + <input id="input-app-name" class="input-field" type="text" + ng-model="appDetails.app.name" + maxlength="100" + name="name" + ng-pattern="/^[a-zA-Z0-9_\-\s\&]*$/" + required/> + <div id="error-container-conflict" class="error-container" ng-show="appDetails.conflictMessages.name" id="conflictMessages-name"> + <small id="app-name-error-conflict" class="err-message" ng-bind="appDetails.conflictMessages.name"></small> + </div> + <div id="error-container-edit" class="error-container" ng-show="appForm.name.$dirty || appDetails.isEditMode"> + <div ng-messages="appForm.name.$error" class="error-container"> + <small id="app-name-error-required" class="err-message" ng-message="required">Application name is required</small> + <small id="app-name-error-alpha-num" class="err-message" ng-message="pattern">Application name must be alphanumeric</small> + </div> + </div> + </div> + <div class="property required"> + <div id="url-property-label" class="property-label">URL</div> + <input class="input-field" id="input-app-url" + ng-model="appDetails.app.url" + maxlength="256" + name="url" + type="url" placeholder="https://" + ng-pattern="appDetails.ECOMP_URL_REGEX" + required /> + <div class="error-container" ng-show="appDetails.conflictMessages.url" id="div-app-name-err-url"> + <small class="err-message" ng-bind="appDetails.conflictMessages.url"></small> + </div> + <div class="error-container" ng-show="appForm.url.$dirty || appDetails.isEditMode"> + <div ng-messages="appForm.url.$error" class="error-container"> + <small id="error-app-url-req" class="err-message" ng-message="required">Application URL is required</small> + <small id="error-app-url-invalid" class="err-message" ng-show="appForm.url.$error.url">Application URL must be a valid URL</small> + </div> + </div> + </div> + + <div class="property" ng-show="!appDetails.app.restrictedApp"> + <div class="property-label">Rest API URL</div> + <input class="input-field" id="input-app-rest-url" + ng-model="appDetails.app.restUrl" + name="restUrl" + type="url" placeholder="https://" + ng-pattern="appDetails.ECOMP_URL_REGEX" + maxlength="256"/> + <div class="error-container" ng-show="appForm.restUrl.$dirty || appDetails.isEditMode"> + <div ng-messages="appForm.restUrl.$error" class="error-container"> + <small class="err-message" ng-show="appForm.restUrl.$error.url">Application REST URL must be a valid URL</small> + </div> + </div> + </div> + + <div class="property required" ng-show="!appDetails.app.restrictedApp"> + <div id="username-property-label" class="property-label">Username</div> + <input class="input-field" type="text" + ng-model="appDetails.app.username" + name="username" + maxlength="256" + ng-required="!appDetails.app.restrictedApp"/> + <div class="error-container" ng-show="appForm.username.$dirty || appDetails.isEditMode"> + <div ng-messages="appForm.username.$error" class="error-container"> + <small id="error-appusername-reqd" class="err-message" ng-message="required">Rest Api Username is required</small> + </div> + </div> + </div> + + <div class="property required" ng-show="!appDetails.app.restrictedApp"> + <div id="pwd-property-label" class="property-label">Password</div> + <input class="input-field" type="password" id="input-mylogins-password" + ng-model="appDetails.app.appPassword" autocomplete="new-password" + name="appPassword" + maxlength="256" + ng-required="!appDetails.app.restrictedApp"/> + <div class="error-container" ng-show="appForm.appPassword.$dirty || appDetails.isEditMode"> + <div ng-messages="appForm.appPassword.$error" class="error-container"> + <small id="error-mylogins-password-reqd" class="err-message" ng-message="required">Rest Api Password is required</small> + </div> + </div> + </div> + + + <div class="property" ng-show="!appDetails.app.restrictedApp"> + <div class="property-label">Communication Inbox</div> + <input class="input-field" type="text" id="input-UEB-topicname" + ng-model="appDetails.app.uebTopicName" + name="uebTopicName" readonly="readonly"/> + </div> + <div class="property" ng-show="!appDetails.app.restrictedApp"> + <div class="property-label">Communication Key</div> + <input class="input-field" type="text" id="input-UEB-communication-key" + ng-model="appDetails.app.uebKey" + name="uebKey" readonly="readonly" /> + </div> + <div class="property" ng-show="!appDetails.app.restrictedApp"> + <div class="property-label">Communication Secret</div> + <input class="input-field" type="text" id="input-UEB-communication-secret" + ng-model="appDetails.app.uebSecret" + name="uebSecret" readonly="readonly" /> + </div> + + </div> + <div class="right-container"> + <div class="property"> + <div class="property-label">Upload Image</div> + <input type="file" id="input-app-image-upload" + class="input-file-field" + accept="image/*" + ng-model="appDetails.originalImage" + name="appImage" + image-upload="appDetails.originalImage" + image-upload-resize-max-height="300" + image-upload-resize-max-width="360" + image-upload-resize-quality="0.7" + image-upload-api="appDetails.imageApi"/> + + <div class="error-container" ng-show="appForm.appImage.$dirty"> + <div ng-messages="appForm.appImage.$error" class="error-container"> + <small id="error-app-invalid-image" class="err-message" ng-message="mimeType">Uploaded file must be an image</small> + <small id="error-app-invalid-image-size" class="err-message" ng-message="imageSize">Image file must be smaller than 1MB</small> + </div> + </div> + <div class="property-label preview"> + <span class="left-label">Preview</span> + <span class="remove" ng-click="appDetails.removeImage()">Remove</span> + </div> + <img id="image-app-preview" class="image-preview" ng-src="{{appDetails.app.imageUrl || appDetails.app.thumbnail || appDetails.emptyImgForPreview}}"/> + + <div class="property"> + <input id="checkbox-app-is-open" type="checkbox" class="checkbox-field" ng-model="appDetails.app.isOpen" ng-checked="appDetails.app.isOpen || appDetails.app.restrictedApp" ng-disabled="appDetails.app.restrictedApp" /> + <div class="property-label checkbox-label">Allow guest access</div> + </div> + <div class="property"> + <input id="checkbox-app-is-enabled" type="checkbox" class="checkbox-field" ng-model="appDetails.app.isEnabled"/> + <div class="property-label checkbox-label">Active</div> + </div> + </div> + </div> + </form> + </div> + <div class="dialog-control"> + <span class="ecomp-save-spinner" ng-show="appDetails.isSaving"></span> + <div id="button-save-app" class="next-button" ng-class="{disabled: appForm.$invalid}" ng-click="appDetails.saveChanges()">Save</div> + <div id="button-app-cancel" class="cancel-button" ng-click="closeThisDialog()">Cancel</div> + </div> +</div> + +<script type="application/javascript"> + $(document).ready(function(){ + $(".ngdialog-content").css("top","-150px") + }); +</script> diff --git a/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.less b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.less new file mode 100644 index 00000000..6db4845a --- /dev/null +++ b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.less @@ -0,0 +1,122 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .application-details-modal { + height: 700px; + + .title { + .n18r; + border-bottom: @a 3px solid; + + } + + .app-properties-main{ + padding-left: 16px; + padding-top: 16px; + padding-bottom: 16px; + height: 630px; + overflow-y: auto; + + + .left-container{ + display: inline-block; + width: 48%; + + } + .right-container{ + display: inline-block; + width: 48%; + float: right; + + + } + + .property{ + position: relative; + margin-bottom: 18px; + .property-label{ + .o14r; + } + .checkbox-label{ + display: inline-block; + padding-left: 3px; + } + .checkbox-field{ + padding: 0; + margin: 0; + vertical-align: middle; + position: relative; + top: -1px; + } + .preview{ + width: 220px; + margin-top: 22px; + display: block; + + .left-label{ + display:inline-block; + float: left; + } + .remove{ + cursor: pointer; + display: inline-block; + float: right; + .a14r; + } + } + + .input-field{ + .custom-input-field; + width: 220px; + } + + .input-file-field{ + width: 220px; + } + .select-field { + .custom-select-field; + } + + .image-preview{ + background: #f0f0f0; + background-size: cover; + width: 220px; + height: 184px; + margin-top: 10px; + border: 2px solid #e8e8e8; + border-radius: 4px; + } + + .error-container{ + position: absolute; + width: 220px; + display: block; + height: 12px; + line-height: 12px; + + .err-message{ + color: red; + font-size: 10px; + } + } + } + + } + +} diff --git a/ecomp-portal-FE/client/app/views/applications/applications.controller.js b/ecomp-portal-FE/client/app/views/applications/applications.controller.js new file mode 100644 index 00000000..7214fd2d --- /dev/null +++ b/ecomp-portal-FE/client/app/views/applications/applications.controller.js @@ -0,0 +1,111 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class ApplicationsCtrl { + constructor($log, applicationsService, confirmBoxService, ngDialog,userProfileService,$cookies) { + $log.info('ApplicationsCtrl::init: Starting up'); + + this.emptyImgForPreview = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; + let getOnboardingApps = () => { + this.isLoadingTable = true; + applicationsService.getOnboardingApps() + .then(appsList => { + this.appsList = appsList; + }).catch(err => { + $log.error(err); + }).finally(()=> { + this.isLoadingTable = false; + }); + }; + + let init = () => { + this.isLoadingTable = false; + getOnboardingApps(); + + this.searchString = ''; + this.appsTableHeaders = [ + {name: 'Application Name', value: 'name', isSortable: true}, + {name: 'Active', value: 'isEnabled', isSortable: true}, + {name: 'Integration Type', value: 'restrictedApp', isSortable: true}, + {name: 'Guest Access', value: 'isOpen', isSortable: true}, + {name: 'URL', value: 'url', isSortable: true}, + {name: 'REST URL', value: 'restUrl', isSortable: true}, + {name: 'Communication Topic', value: 'uebTopicName', isSortable: true}, + {name: 'Communication Key', value: 'uebKey', isSortable: true}, + {name: 'Communication Secret', value: 'uebSecret', isSortable: true}, + ]; + this.appsList = []; + }; + + init(); + + this.openAddNewAppModal = (selectedApp) => { + let data = null; + if (selectedApp) { + if (!selectedApp.id) { + $log.error('App id not found'); + return; + } + data = { + app: selectedApp + } + } + ngDialog.open({ + templateUrl: 'app/views/applications/application-details-dialog/application-details.modal.html', + controller: 'AppDetailsModalCtrl', + controllerAs: 'appDetails', + data: data + }).closePromise.then(needUpdate => { + if (needUpdate.value === true) { + $log.debug('ApplicationsCtrl:openAddNewAppModal:: updating table data...'); + getOnboardingApps(); + } + }); + + + }; + + this.deleteApp = application => { + $log.debug('ApplicationsCtrl:deleteApp:: ', application.name); + + confirmBoxService.deleteItem(application.name).then(isConfirmed => { + if(isConfirmed){ + if(!application || !application.id){ + $log.error('ApplicationsCtrl:deleteApp:: No application or ID... cannot delete'); + return; + } + applicationsService.deleteOnboardingApp(application.id).then(() => { + this.appsList.splice(this.appsList.indexOf(application), 1); + }).catch(err => { + $log.error(err); + }); + } + }).catch(err => { + $log.error('ApplicationsCtrl:deleteApp error:: ', err); + }); + + }; + + } + } + ApplicationsCtrl.$inject = ['$log', 'applicationsService', 'confirmBoxService', 'ngDialog','userProfileService','$cookies']; + angular.module('ecompApp').controller('ApplicationsCtrl', ApplicationsCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/applications/applications.controller.spec.js b/ecomp-portal-FE/client/app/views/applications/applications.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/applications/applications.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/applications/applications.less b/ecomp-portal-FE/client/app/views/applications/applications.less new file mode 100644 index 00000000..eed77ac2 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/applications/applications.less @@ -0,0 +1,45 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .applications-page-main{ + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + .apps-table { + width: @table-width; + margin: @table-margin; + + .delete-app{ + .ico_trash_default; + } + .small-thumbnail{ + width: 72px; + height: 60px; + border: 1px solid #d8d8d8; + border-radius: 2px; + } + } +} diff --git a/ecomp-portal-FE/client/app/views/applications/applications.tpl.html b/ecomp-portal-FE/client/app/views/applications/applications.tpl.html new file mode 100644 index 00000000..a27821b1 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/applications/applications.tpl.html @@ -0,0 +1,100 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-main"> + <div class="w-ecomp-main-container"> + <div class="applications-page-main" id="contentId"> + <div id='app-title' class="w-ecomp-main-view-title">Application Onboarding</div> + <div class="apps-table"> + <div class="table-control"> + <input class="table-search" + id="app-input-table-search" + type="text" + placeholder="Search by Application Name" + ng-model="searchAppName"/> + <div id="button-openAddNewApp" class="add-button" ng-click="apps.openAddNewAppModal()">Add App</div> + </div> + <span class="ecomp-spinner" ng-show="apps.isLoadingTable"></span> + <div class="c-ecomp-att-abs-table default" ng-hide="apps.isLoadingTable" id="div-app-table"> + <table att-table id="app-onboarding-table" + table-data="apps.appsList" + search-string="apps.searchString" + view-per-page="apps.viewPerPageIgnored" + current-page="apps.currentPageIgnored" + total-page="apps.totalPageIgnored"> + <thead att-table-row type="header" id="att-table-row"> + <tr> + <th id="app-header-delete" att-table-header sortable="false">Delete</th> + <th id="app-header-Thumbnail" att-table-header sortable="false">Thumbnail</th> + <th id="app-header-AppName" att-table-header sortable="true">Application Name</th> + <th id="app-header-Active" att-table-header sortable="false">Active?</th> + <th id="app-header-IntType" att-table-header sortable="false">Integration Type</th> + <th id="app-header-Guest" att-table-header sortable="false">Guest Access</th> + <th id="app-header-URL" att-table-header sortable="false">URL</th> + <th id="app-header-RESTURL" att-table-header sortable="false">REST URL</th> + <th id="app-header-Topic" att-table-header sortable="false">Communication Topic</th> + <th id="app-header-CommKey" att-table-header sortable="false">Communication Key</th> + <th id="app-header-Secret" att-table-header sortable="false">Communication Secret</th> + </tr> + </thead> + <tbody att-table-row type="body" + class="table-body" + row-repeat="rowData in apps.appsList | filter:{name:searchAppName} | orderBy: 'name'"> + <tr> + <td ng-click="apps.deleteApp(rowData)"> + <div id="{{$index}}-app-delete" class="ion-trash-b"></div> + </td> + <td ng-click="apps.openAddNewAppModal(rowData)"> + <img id="{{$index}}-app-imageUrl" class="small-thumbnail" ng-src="{{rowData.imageUrl || apps.emptyImgForPreview}}"> + </td> + <td ng-click="apps.openAddNewAppModal(rowData)"> + <div id="{{$index}}-name">{{rowData.name}}</div> + </td> + <td ng-click="apps.openAddNewAppModal(rowData)"> + <div id="{{$index}}-isEnabled">{{(rowData.isEnabled) ? 'yes' : 'no'}}</div> + </td> + <td ng-click="apps.openAddNewAppModal(rowData)"> + <div id="{{$index}}-restrictedApp">{{(rowData.restrictedApp) ? 'link' : 'standard'}}</div> + </td> + <td ng-click="apps.openAddNewAppModal(rowData)"> + <div id="{{$index}}-isOpen">{{(rowData.isOpen) ? 'yes' : 'no'}}</div> + </td> + <td ng-click="apps.openAddNewAppModal(rowData)"> + <div id="{{$index}}-url">{{rowData.url | elipsis: 27}}</div> + </td> + <td ng-click="apps.openAddNewAppModal(rowData)"> + <div id="{{$index}}-restUrl">{{rowData.restUrl | elipsis: 27}}</div> + </td> + <td ng-click="apps.openAddNewAppModal(rowData)"> + <div id="{{$index}}-uebTopicName">{{rowData.uebTopicName}}</div> + </td> + <td ng-click="apps.openAddNewAppModal(rowData)"> + <div id="{{$index}}-uebKey">{{rowData.uebKey}}</div> + </td> + <td ng-click="apps.openAddNewAppModal(rowData)"> + <div id="{{$index}}-uebSecret">{{rowData.uebSecret}}</div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/catalog/catalog.controller.js b/ecomp-portal-FE/client/app/views/catalog/catalog.controller.js new file mode 100644 index 00000000..652c0886 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/catalog/catalog.controller.js @@ -0,0 +1,166 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function'); + } +} + +(function() { + var HTTP_PROTOCOL_RGX = /https?:\/\//; + + var CatalogCtrl = function CatalogCtrl(catalogService, confirmBoxService, + $log, $window, userProfileService, $scope, $timeout, $interval, + $modal, ngDialog) { + + var _this = this; + + _classCallCheck(this, CatalogCtrl); + + // activate spinner + this.isLoading = true; + $scope.getAppCatalogIsDone = false; + $scope.radioValue = 'All'; + $scope.$watch('radioValue', function(newValue, oldValue) { + var appCatalog = $scope.appCatalog; + $scope.appCatalog = []; + $scope.appCatalog = appCatalog; + + + }); + + this.catalogService = catalogService; + this.userProfileService = userProfileService; + $scope.demoNum = 1; + + this.getAccess = function(item) { + if(!item.access) + confirmBoxService.showDynamicInformation(item, + 'app/views/catalog/information-box.tpl.html','CatalogConfirmationBoxCtrl' + ).then(isConfirmed => {}); + }; + + // Run this function when user clicks on checkbox. + this.storeSelection = function(item) { + // $log.debug('CatalogCtrl:storeSelection: item.id is ' + item.id + ', select is ' + item.select); + var pendingFlag = false; + + if(item.access) + pendingFlag = false; + else + pendingFlag = item.pending; + + + + + var appData = { + appId : item.id, + select : item.select, + pending : pendingFlag // TODO + }; + catalogService.updateAppCatalog(appData).then( + function(result) { + // $log.debug('CatalogCtrl:storeSelection result is ', result); + })['catch'](function(err) { + $log.error('CatalogCtrl:storeSelection: exception: ', err); + }); + }; + + userProfileService + .getUserProfile() + .then( + function(profile) { + $scope.attuid = profile.attuid; + $scope.appCatalog = []; + + // applicationsService.getUserApps() + catalogService + .getAppCatalog() + .then( + function(res) { + // $log.info(res); + _this.apps = res; + var rowNo = 0; + for (var i = 0; i < _this.apps.length; i++) { + $scope.appCatalog[i] = { + sizeX : 2, + sizeY : 2, + id : _this.apps[i].id, + headerText : _this.apps[i].name, + imageLink : _this.apps[i].thumbnail + || _this.apps[i].imageUrl, + url : _this.apps[i].url, + restricted : _this.apps[i].restricted, + select : (_this.apps[i].select || (_this.apps[i].access && _this.apps[i].pending)), + access : _this.apps[i].access, + pending: _this.apps[i].pending + }; + } + //$log.debug('CatalogCtrl: getAppCatalog count : ' + // + $scope.appCatalog.length); + _this.isLoading = false; + $scope.getAppCatalogIsDone = true; + })['catch'](function(err) { + $log.error('CatalogCtrl: failed to get app list: ', err); + _this.isLoading = false; + $scope.getAppCatalogIsDone = true; + }); + }); + + this.gridsterOpts = { + columns : 12, + colWidth : 95, + rowHeight : 95, + margins : [ 20, 20 ], + outerMargin : true, + pushing : true, + floating : true, + swapping : true, + }; + + if (getParameterByName('noUserError') != null) { + if (getParameterByName('noUserError') == "Show") { + $("#errorInfo").show(); + } + + } + }; + + CatalogCtrl.$inject = [ 'catalogService', 'confirmBoxService', '$log', + '$window', 'userProfileService', '$scope', '$timeout', '$interval', + '$modal', 'ngDialog' ]; + angular.module('ecompApp').controller('CatalogCtrl', CatalogCtrl); +})(); + +function getParameterByName(name, url) { + if (!url) + url = window.location.href; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex + .exec(url); + if (!results) + return ''; + if (!results[2]) + return ''; + return results[2].replace(/\+/g, " "); +}
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/catalog/catalog.less b/ecomp-portal-FE/client/app/views/catalog/catalog.less new file mode 100644 index 00000000..9385c8c5 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/catalog/catalog.less @@ -0,0 +1,439 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +.w-ecomp-app-catalog { + //.bg_u;//white for 1702 + .bg_w; // gray for 1610 + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + .gridster-item-container .gridster-item-body { + overflow-y:auto; + overflow-x:hidden; + } + + .app-catalog-container { + .content_justify; + position: relative; + padding: 0 0 32px 0; + width: 100%; + + .app-catalog-title { + .a24r; + margin: auto; + .content_justify; + } + .app-catalog-list { + min-height: 90vh; + //display: flex; + justify-content: center; + flex-flow: row wrap; + width: 1170px; + + margin: auto; + margin-bottom: 63px; + + .app-gridster-header { + background-color: @u; + font-size:12px; + overflow:hidden + } + + .app-gridster-footer { + background-color: @u; + } + + } + } +} + +.build-number { + .o12i; +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.slide.ng-hide-add, +.slide.ng-hide-remove, +.slide.ng-enter, +.slide.ng-leave { + transition: all 0.5s ease; +} +.slide.ng-hide, +.slide.ng-enter { + transform: translate(-100%, 0); +} +.slide.ng-enter-active { + transform: translate(0, 0); +} +.slide.ng-leave { + transform: translate(0, 0); +} +.slide.ng-leave-active { + transform: translate(+100%, 0); +} +.appCatalogue-boarder{ + // border: 2px solid #ccc!important; + // border-radius: 16px!important; + height: 800px; + overflow: auto; +} +.app-visibility.false { + opacity: 0.5; +} + +.dock ul{ +display: inline-block; + +width: auto; +margin: 0px; +padding: 0px; +list-style: none; + +} +.dock ul li { +width: auto; +height: auto; +display: inline-block; +bottom: 0; +vertical-align: bottom; +margin-top: -43px; +} +.dock ul li a { +display: block; +height: 150px; +width: 150px; +position: relative; +-webkit-transition-property: width, height,margin-top; +-webkit-transition-duration: 0.5s; +-o-transition-property: width, height,margin-top; +-o-transition-duration: 0.5s; +-moz-transition-property: width, height,margin-top; +-moz-transition-duration: 0.5s; +} +.dock ul li a:hover { +width: 200px; +height: 200px; +margin-top: -50px; +} +.dock ul li a img { +width: 100%; +position: absolute; +bottom: 0; +left: 0; +border: none; +padding: 0px 0px 0px 30px; +} +.dock_left{ +width: 31px; +-webkit-transform: rotate(33deg); +-moz-transform: rotate(33deg); +-o-transform: rotate(33deg); +position: relative; +background: #EEE; +overflow: hidden; +float: left; +height: 100px; +z-index: 2; +margin: -18px; +} +.dock_right{ +width: 36px; +-webkit-transform: rotate(-33deg); +-moz-transform: rotate(-33deg); +-o-transform: rotate(-33deg); +position: relative; +background: #EEE; +overflow: hidden; +float: left; +height: 100px; +z-index: 2; +margin: -18px; +} + + + + +/*Time for the CSS*/ +* {margin: 0; padding: 0;} +body {background: #ccc;} + +.slider{ + width: 640px; /*Same as width of the large image*/ + position: relative; + /*Instead of height we will use padding*/ + padding-top: 320px; /*That helps bring the labels down*/ + + margin: 50px auto; + + /*Lets add a shadow*/ + box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.75); +} + + +/*Last thing remaining is to add transitions*/ +.slider>img{ + position: absolute; + left: 0; top: 0; + transition: all 0.5s; +} + +.slider input[name='slide_switch'] { + display: none; +} + +.slider label { + /*Lets add some spacing for the thumbnails*/ + margin: 18px 0 0 18px; + border: 3px solid #999; + + float: left; + cursor: pointer; + transition: all 0.5s; + + /*Default style = low opacity*/ + opacity: 0.6; +} + +.slider label img{ + display: block; +} + +/*Time to add the click effects*/ +.slider input[name='slide_switch']:checked+label { + border-color: #666; + opacity: 1; +} +/*Clicking any thumbnail now should change its opacity(style)*/ +/*Time to work on the main images*/ +.slider input[name='slide_switch'] ~ img { + opacity: 0; + transform: scale(1.1); +} +/*That hides all main images at a 110% size +On click the images will be displayed at normal size to complete the effect +*/ +.slider input[name='slide_switch']:checked+label+img { + opacity: 1; + transform: scale(1); +} +/*Clicking on any thumbnail now should activate the image related to it*/ + +.caption { + background: rgba(0, 0, 0, 0.5); + position: absolute; + bottom: 0; + width: 640px +} + +.caption a { + display: block; + color: #fff; + text-decoration: none; + font: normal 16px 'Lato', Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + padding: 15px; +} + + +/*widgets*/ +.singleBtnBorder { + border-radius: 6px 6px 6px 6px; +} + +.icon-anchor { + color: #888; +} + +.widgetHeaderBtn:hover, .widgetHeaderBtn:focus { + background: rgba(0, 0, 0, 0); + color: #3a7999; + box-shadow: inset 0 0 0 2px #3a7999; +} + +.box-one { + -webkit-transition:all linear 0.4s; + transition:all linear 0.4s; + height:100px; width:200px; background:white; border: 2px solid #ccc!important; + border-radius: 16px!important; +} +.box-one.ng-hide { +display: block!important; + opacity:0; +} + +.gridsterContent{ + height:120px; +} +.gridsterAppContent{ + height:120px; + +} + + .gridsterImage{ + height:84px; + width:168px; + } + .grider-content-ecomp{ + transition: transform 0.5s ease-out; + } + .grider-content-ecomp:hover{ + transform: scale(1.1); +} + +/*application empty div*/ +.app-error-block { + padding-top: 10px; +} + +.text-center { text-align: center; } + +.text-right { text-align: right; } + +.text-justify { text-align: justify; } + +/*widget header*/ +.optionsMenu{ + position: absolute; + list-style: none; + top: 25px; + right: 10px; + border: 1px solid #067ab4; + display: none; + z-index: 2; + border-radius: 6px 0px 6px 6px; + background: #fff; + width: 130px; +} + +.optionsMenuLink { + position: relative; + padding-left: 8px; + padding-right: 2px; + font-size: 12px; + line-height: 30px; + color: #444444; +} +.optionsMenu > li:hover a { + color: #ffffff !important; +} +.optionsMenu > li { + width: 100%; + text-align: left; +} +.optionsMenu > li:hover { + background-color: #0faaf7; + border-color: none !important; + cursor: pointer; +} + +.simulateCatGridHeader{ + position: relative; + height: 70px !important; + border: 1px solid #d3d3d3; + border-bottom: 0; + background-color: #E5E5E5; + white-space: nowrap; + text-overflow: ellipsis; + z-index: 1; +} + +.simulateCatGridHeaderTitle{ + line-height: 20px; + margin-top: 10px; + margin-left: 26px; + font-family: arial; + font-size: 18px; + color: #444444; + float: left; +} + +.simulateCatGridHeaderRadio{ + line-height: 20px; + margin-top: 10px; + margin-left: 10px; + font-family: arial; + font-size: 12px; + color: #444444; + float: left; +} + +.simulateCatGridHeaderDetails{ + line-height: 20px; + margin-left: 10px; + font-family: arial; + font-size: 12px; + color: #444444; + float: left; +} + +.simulateGridHeaderHandle{ + cursor: move; + margin: 12px; + position: absolute; + top: 0; + left: 0; + border: 0; + vertical-align: middle; + -ms-interpolation-mode: bicubic; + display: block; +} + +.att-checkbox--on .att-checkbox__indicator, .att-radio--on .att-radio__indicator { + opacity: 1; +} + +.animate-enter, +.animate-leave +{ + -webkit-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all; + -moz-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all; + -ms-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all; + -o-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all; + transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all; + position: relative; + display: block; +} + +.animate-enter.animate-enter-active, +.animate-leave { + opacity: 1; + top: 0; + height: 30px; +} + +.animate-leave.animate-leave-active, +.animate-enter { + opacity: 0; + top: -50px; + height: 0px; +} diff --git a/ecomp-portal-FE/client/app/views/catalog/catalog.tpl.html b/ecomp-portal-FE/client/app/views/catalog/catalog.tpl.html new file mode 100644 index 00000000..e169afa9 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/catalog/catalog.tpl.html @@ -0,0 +1,91 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-app-catalog" id="contentId"> + <div class="app-catalog-container"> + <div id="app-catalog-title" class="app-catalog-title">Application Catalog</div> + <div class="app-catalog-list"> + <div class="simulateCatGridHeader"> + <span class="simulateCatGridHeaderDetails"> Click the check + boxes below to choose which applications are shown on the <a + href="applicationsHome">home page</a>. + </span> <br> + <br> <label class="simulateCatGridHeaderRadio"> Filter: + <input type="radio" ng-model="radioValue" value="All"> All + </label> <label class="simulateCatGridHeaderRadio"> <input + type="radio" ng-model="radioValue" value="Accessible"> + Accessible + </label> <label class="simulateCatGridHeaderRadio"> <input + type="radio" ng-model="radioValue" value="Selected"> On + Home Page + </label> + + </div> + <div class="appCatalogue-boarder"> + <div class="gridster-container override_background"> + <div att-gridster att-gridster-options="catalog.gridsterOpts"> + <div class="style_prevu_kit" att-gridster-item='item' + ng-repeat="item in appCatalog" ng-animate="'animate'" + ng-if="(item.select && radioValue=='Selected') || (item.access && radioValue=='Accessible') || (radioValue == 'All')" + id="app-{{item.headerText.split(' ').join('-')}}"> + + <div class="grider-content-ecomp app-visibility {{item.access}}" + ng-show="(item.select && radioValue=='Selected') || (item.access && radioValue=='Accessible') || (radioValue == 'All')"> + <div id="grips-{{item.headerText.split(' ').join('-')}}" + ng-hide="users.isLoadingTable && !users.getAppCatalogIsDone" + att-gridster-item-header + grips-img-path="assets/images/grips.png" + header-text="{{item.headerText | elipsis: 15}}" + class="app-gridster-header"> + <div ng-show="item.access || isAdminPortalAdmin"> + <input type="checkbox" ng-model="item.select" + ng-change="catalog.storeSelection(item)"> + </div> + <div ng-show="item.pending && !item.access"> + <i style="font-size: 8px">Pending Access</i> + </div> + </div> + <div id="app-image-{{item.headerText.split(' ').join('-')}}" + att-gridster-item-body + ng-style="{'cursor':'pointer', + 'background-image': 'url('+(item.imageLink)+')', + 'order': item.order, + 'background-color':'white', + 'background-repeat': 'no-repeat', + 'background-size': '170px 130px'}" + ng-click="catalog.getAccess(item)" + ng-hide="users.isLoadingTable && !users.getAppCatalogIsDone" + class="gridsterContent"></div> + </div> + </div> + </div> + </div> + </div> + <br> + + <div class="w-ecomp-main-disclaimer"> + To request access to an application, please visit the <a + ng-href="getAccess">Get Access</a> page. + </div> + + <br> + </div> + + </div> +</div>
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/catalog/catalogconfirmation.controller.js b/ecomp-portal-FE/client/app/views/catalog/catalogconfirmation.controller.js new file mode 100644 index 00000000..83438e9c --- /dev/null +++ b/ecomp-portal-FE/client/app/views/catalog/catalogconfirmation.controller.js @@ -0,0 +1,62 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; +(function () { + class CatalogConfirmationBoxCtrl { + constructor($scope,$state,catalogService) { + + let init = () => { + let item = ($scope.ngDialogData && $scope.ngDialogData.item) || 'this'; + this.message = $scope.ngDialogData.message ? $scope.ngDialogData.message : `Are you sure you want to delete "${item}"?`; + this.title = $scope.ngDialogData.title ? $scope.ngDialogData.title : ''; + }; + + this.closeBox = isConfirmed => { + $scope.closeThisDialog(isConfirmed); + }; + + this.goTo = (state, params) => { + $scope.closeThisDialog(false); + $state.go(state,params); + + }; + + this.storeSelection = function(item) { + // $log.debug('CatalogCtrl:storeSelection: item.id is ' + item.id + ', select is ' + item.select); + var appData = { + appId : item.id, + select : item.select, + pending : item.pending + }; + catalogService.updateAppCatalog(appData).then( + function(result) { + // $log.debug('CatalogCtrl:storeSelection result is ', result); + })['catch'](function(err) { + $log.error('CatalogCtrl:storeSelection: exception: ', err); + }); + }; + + init(); + } + } + CatalogConfirmationBoxCtrl.$inject = ['$scope','$state','catalogService']; + angular.module('ecompApp').controller('CatalogConfirmationBoxCtrl', CatalogConfirmationBoxCtrl); +})();
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/catalog/information-box.tpl.html b/ecomp-portal-FE/client/app/views/catalog/information-box.tpl.html new file mode 100644 index 00000000..ccc0a9f5 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/catalog/information-box.tpl.html @@ -0,0 +1,42 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="confirmation-box-container"> + <div class="confirmation-message-wrap"> + <div class="confirmation-message" > + <div> + You do not have access to this application. + See the + <a href="" ng-click="confirmBox.goTo('root.getAccess', {appName: confirmBox.message.headerText})"> + Get Access page</a> and request access. + <br><br> + You may check this box if access is pending: + <input type="checkbox" + + ng-model="confirmBox.message.pending" + ng-change="confirmBox.storeSelection(confirmBox.message)" + > + </div> + + </div> + </div> + <div class="dialog-control"> + <div class="cancel-button" ng-click="confirmBox.closeBox(false)">Close</div> + </div> +</div>
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/admin-confirmation-box.tpl.html b/ecomp-portal-FE/client/app/views/confirmation-box/admin-confirmation-box.tpl.html new file mode 100644 index 00000000..61a812bc --- /dev/null +++ b/ecomp-portal-FE/client/app/views/confirmation-box/admin-confirmation-box.tpl.html @@ -0,0 +1,31 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="confirmation-box-container"> + <div class="confirmation-title-wrap"> + <div class="confirmation-title" ng-bind="confirmBox.title"></div> + </div> + <div class="confirmation-message-wrap"> + <div class="confirmation-message" ng-bind="confirmBox.message"></div> + </div> + <div class="dialog-control"> + <div class="next-button" ng-click="confirmBox.closeBox(true)">Confirm</div> + <div class="cancel-button" ng-click="confirmBox.closeBox(false)">Cancel</div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.controller.js b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.controller.js new file mode 100644 index 00000000..4f5e14b3 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.controller.js @@ -0,0 +1,41 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; +(function () { + class ConfirmationBoxCtrl { + constructor($scope) { + + let init = () => { + let item = ($scope.ngDialogData && $scope.ngDialogData.item) || 'this'; + this.message = $scope.ngDialogData.message ? $scope.ngDialogData.message : `Are you sure you want to delete "${item}"?`; + this.title = $scope.ngDialogData.title ? $scope.ngDialogData.title : ''; + }; + + this.closeBox = isConfirmed => { + $scope.closeThisDialog(isConfirmed); + }; + + init(); + } + } + ConfirmationBoxCtrl.$inject = ['$scope']; + angular.module('ecompApp').controller('ConfirmationBoxCtrl', ConfirmationBoxCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.less b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.less new file mode 100644 index 00000000..410d3967 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.less @@ -0,0 +1,53 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .confirmation-box-container { + .confirmation-message-wrap { + display: table; + height: 60px; + width: 100%; + + .confirmation-message { + .n14r; + display: table-cell; + vertical-align: middle; + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; + } + } + + .confirmation-title-wrap { + display: table; + height: 20px; + width: 100%; + + .confirmation-title { + .n14r; + display: table-cell; + vertical-align: middle; + text-align: center; + font-weight: bold; + max-width: 100px; + overflow: hidden; + padding-bottom: 2px; + text-overflow: ellipsis; + } + } +}
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.tpl.html b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.tpl.html new file mode 100644 index 00000000..759bf137 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.tpl.html @@ -0,0 +1,28 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="confirmation-box-container"> + <div class="confirmation-message-wrap"> + <div id="confirmation-message" class="confirmation-message" ng-bind="confirmBox.message"></div> + </div> + <div class="dialog-control"> + <div id="confirmation-button-next" class="next-button" ng-click="confirmBox.closeBox(true)">OK</div> + <div id="confirmation-button-cancel" class="cancel-button" ng-click="confirmBox.closeBox(false)">Cancel</div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html b/ecomp-portal-FE/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html new file mode 100644 index 00000000..61a812bc --- /dev/null +++ b/ecomp-portal-FE/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html @@ -0,0 +1,31 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="confirmation-box-container"> + <div class="confirmation-title-wrap"> + <div class="confirmation-title" ng-bind="confirmBox.title"></div> + </div> + <div class="confirmation-message-wrap"> + <div class="confirmation-message" ng-bind="confirmBox.message"></div> + </div> + <div class="dialog-control"> + <div class="next-button" ng-click="confirmBox.closeBox(true)">Confirm</div> + <div class="cancel-button" ng-click="confirmBox.closeBox(false)">Cancel</div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/information-box.tpl.html b/ecomp-portal-FE/client/app/views/confirmation-box/information-box.tpl.html new file mode 100644 index 00000000..1e17e50a --- /dev/null +++ b/ecomp-portal-FE/client/app/views/confirmation-box/information-box.tpl.html @@ -0,0 +1,27 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="confirmation-box-container"> + <div class="confirmation-message-wrap"> + <div class="confirmation-message" ng-bind="confirmBox.message"></div> + </div> + <div class="dialog-control"> + <div class="cancel-button" ng-click="confirmBox.closeBox(false)">Close</div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget-manage.html b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget-manage.html new file mode 100644 index 00000000..84ac2a44 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget-manage.html @@ -0,0 +1,152 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div style="margin-top: 10px; margin-bottom: 10psx;"> + <h1 class="heading1"></h1> +</div> +<br /> + +<div style="margin-top: 25px;"> + <div id="title" class="manage-contactUs-home-manageWidgets-title">Manage {{widgetTypeDisplay}} Widgets</div> + <div class="widgets-popup-margin"> + <div class="get-access-table"> + <div class="table-control" style="margin:30px;"> + <div class="c-ecomp-att-abs-table" style="height:300px""> + <table att-table id="table-main" + table-data="ignoredTableData" + current-page="ignoredCurrentPage"> + <thead att-table-row type="header"> + <tr> + <th id="th-users-1" att-table-header key="title" sortable="true" >Title</th> + <th id="th-users-2" att-table-header key="href" sortable="true" >URL</th> + <th ng-show="{{widgetType=='EVENTS'}}" id="th-users-3" att-table-header key="event_date" sortable="true" >Date</th> + <th ng-show="{{widgetType=='EVENTS'}}" id="th-users-4" att-table-header key="content" sortable="true" >Content</th> + <th id="th-users-5" att-table-header key="sortOrder" sortable="true" >Order</th> + <th id="th-users-6" att-table-header key="edit" sortable="false" >Edit</th> + <th id="th-users-7" att-table-header key="delete" sortable="false" >Delete</th> + </tr> + </thead> + <!-- Use track-by="UNIQUE KEY HERE" or leave out if no unique keys in data --> + <tbody att-table-row type="body" class="table-body" track-by="$index" row-repeat="rowData in widgetData"> + <tr id="tr-rowData" ng-click=""> + <td att-table-body> + <div id="widget-td-title" ng-hide="rowData.showEdit" ng-bind="rowData.title"></div> + <input type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].title"/> + </td> + <td att-table-body> + <div id="widget-td-href" ng-hide="rowData.showEdit" ng-bind="rowData.href"></div> + <input type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].href"/> + </td> + <td ng-show="{{widgetType=='EVENTS'}}" att-table-body> + <div id="widget-td-date" ng-hide="rowData.showEdit" ng-bind="rowData.eventDate"></div> + <input type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].eventDate"/> + </td> + <td ng-show="{{widgetType=='EVENTS'}}" att-table-body> + <div id="widget-td-content" ng-hide="rowData.showEdit">{{rowData.content | cut:true:20:' ...'}}</div> + <input type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].content"/> + </td> + <td att-table-body> + <div id="widget-td-order" ng-hide="rowData.showEdit" ng-bind="rowData.sortOrder"></div> + <input type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].sortOrder"/> + </td> + <td att-table-body> + <div class="delete-contact-us" ng-hide="rowData.showEdit" ng-click="setEdit($index);"> + <span class="ion-edit"></span> + </div> + <span ng-show="rowData.showEdit"> + <a att-button btn-type="primary" ng-click="modify($index);" class="button button--primary" size="small" >Save</a> + </span> + </td> + <td att-table-body> + <div class="delete-contact-us" ng-click="remove($index);"> + <span class="ion-trash-b"></span> + </div> + </td> + </tr> + </tbody> + </table> + </div> + + <div att-divider-lines></div> + <div style="margin-top: 15px; margin-left: -80px;" > + <form name = "widgetForm" novalidate> + <div id="addWidgetHeader" class="contact-us-margin"> + <h1 style="font-size:18px;">Add</h1> + <div class="errMsg">{{errMsg}}</div> + <div class="sucessMsg" ng-bind="successMsg"></div> + </div> + <div id="addWidget" class="contact-us-margin"> + <div> + <div> + <div class="add-widget-field"> + <div id="mots-property-label" class="property-label">Title</div> + <input class="input-text-area" type="text" ng-model="newWidgetObject.title" name="title" ng-required="true"/> + </div> + <div class="add-widget-field"> + <div class="property-label">URL</div> + <input class="input-text-area" type="text" ng-model="newWidgetObject.href" type="url" size="3" name = "url" ng-required = "true"/> + </div> + <div class="add-widget-field" > + <div class="property-label">Sort Order</div> + <input class="input-text-area" type="text" ng-model="newWidgetObject.sortOrder"/> + </div> + </div> + <div class="add-widget-field" ng-controller="DatePickerController" ng-show="{{widgetType=='EVENTS'}}"> + <h4>Event Date</h4> + <div> + <div> + <p class="input-group"> + <input type="text" name = "eventDate" datetime="yyyy-MM-dd" class="input-text-area" uib-datepicker-popup="{{format}}" + ng-model="newWidgetObject.eventDate" is-open="popup1.opened" datepicker-options="dateOptions" close-text="Close" alt-input-formats="altInputFormats" + ng-focus="open1()" + ng-required = "widgetType =='EVENTS'" /> + </p> + </div> + </div> + </div> + + <div class="add-contact-us-field-des" ng-show="{{widgetType=='EVENTS'}}"> + <div class="property-label">Content</div> + <textarea style="margin-top: 0px; margin-bottom: 0px; height: 150px" ng-model="newWidgetObject.content" name="content" ng-required = "widgetType =='EVENTS'"> + </textarea> + </div> + </div> + <div style="height:50px;"> + <a style="float:right; margin-top:20px;" att-button btn-type="primary" class="button button--primary" size="small" ng-click="saveNew()">Add New</a> + + </div> + + <div att-divider-lines></div> + + </div> + </form> + </div> + <div style="height:50px;"> + <a style="float:right;" att-button btn-type="primary" class="button button--primary" size="small" ng-click="closeDialog()">Close</a> + </div> + </div> + </div> + </div> +</div> + +<script type="application/javascript"> + $(document).ready(function(){ + $(".ngdialog-content").css("width","85%") + }); +</script> diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.js b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.js new file mode 100644 index 00000000..d489ac42 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.js @@ -0,0 +1,422 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +(function () { + class CommonWidgetController { + constructor(dashboardService, $scope, message, $q, $http, conf, $filter,confirmBoxService,$log) { + $scope.widgetType = message.type; + $scope.widgetTypeDisplay = message.type; + $scope.modflag = false; + + switch (message.type) { + case 'EVENTS': + $scope.widgetTypeDisplay = 'Events' + break; + case 'NEWS': + $scope.widgetTypeDisplay = 'News' + break; + case 'IMPORTANTRESOURCES': + $scope.widgetTypeDisplay = 'Resources' + break; + } + + $scope.widgetData = []; + dashboardService.getCommonWidgetData(message.type) + .then(function (res) { + // console.log('CommonWidgetController: result is ' + res); + $scope.widgetData = res.response.items; + }); + + + + $scope.setEdit = function(index) { + + //for(var i=0; i<$scope.widgetData ) + + if($scope.modflag === false){ + $scope.widgetData[index].showEdit = true; + $scope.modflag = true; + } + } + + $scope.modify = function(index) { + + $scope.widgetObject = {}; + $scope.widgetObject.id = $scope.widgetData[parseInt(index)].id; + $scope.widgetObject.category = $scope.widgetData[parseInt(index)].category; + $scope.widgetObject.title = $scope.widgetData[parseInt(index)].title; + $scope.widgetObject.href = $scope.widgetData[parseInt(index)].href; + $scope.widgetObject.eventDate = $scope.widgetData[parseInt(index)].eventDate; + $scope.widgetObject.content = $scope.widgetData[parseInt(index)].content; + $scope.widgetObject.sortOrder = $scope.widgetData[parseInt(index)].sortOrder; + + var validateMsg = $scope.validateWidgetObject($scope.widgetObject); + if (validateMsg) { + confirmBoxService.showInformation(validateMsg).then(isConfirmed => {return;}); + return; + } + + dashboardService.saveCommonWidgetData($scope.widgetObject) + .then(function(res){ + if (res.status == 'OK') { + dashboardService.getCommonWidgetData(message.type) + .then(function(res){ + $scope.widgetData = res.response.items; + $scope.modflag = false; + $scope.cancelEdit(index); + }); + } + else { + // Save failed + confirmBoxService.showInformation("Save failed").then(isConfirmed => {return;}); + return; + } + }); + + }; + + $scope.newWidgetObject = {}; + + // Answers string error if validation fails; + // answers null if all is well. + $scope.validateWidgetObject = function(wo) { + if (wo.title == null || wo.title == '') + return "Please enter a title."; + if (wo.href == null || wo.href == '' || !validateUrl(wo.href)) + return "Please enter a valid URL that starts with http."; + if (wo.sortOrder == null || wo.sortOrder == '' || isNaN(parseInt(wo.sortOrder))) + return "Please enter a number for the sort order."; + if (wo.category=='EVENTS') { + if (wo.eventDate == null || wo.eventDate == '') + return "Please enter a date for the event."; + // Parses and normalizes the date with rollover. + var filteredDate = $filter('date')(wo.eventDate, "yyyy-MM-dd"); + // $log.debug('dashboard-widget-controller: date filter yields ' + filteredDate); + // The date picker shows mm/dd/yy. + if (filteredDate == null || filteredDate.length != 10) + return "Please enter a valid date as YYYY-MM-DD"; + if (wo.content==null || wo.content=='') + return "Please enter content for the event."; + } + return null; + }; + + $scope.saveNew = function() { + $scope.newWidgetObject.category = message.type; + // $log.info($scope.newWidgetObject); + var validateMsg = $scope.validateWidgetObject($scope.newWidgetObject); + if (validateMsg) { + confirmBoxService.showInformation(validateMsg).then(isConfirmed => {return;}); + return; + } + // Transform date into expected storage format + $scope.newWidgetObject.eventDate = $filter('date')($scope.newWidgetObject.eventDate, "yyyy-MM-dd"); + + dashboardService.saveCommonWidgetData($scope.newWidgetObject) + .then(function(res){ + if (res.status == 'OK') { + $scope.widgetForm.$setPristine(); + confirmBoxService.showInformation('You have added a new item').then(isConfirmed => { + }); + dashboardService.getCommonWidgetData(message.type) + .then(function(res){ + $scope.widgetData = res.response.items; + $scope.newWidgetObject = {}; + }); + } + else { + confirmBoxService.showInformation("Save failed").then(isConfirmed => {return;}); + return; + } + }); + }; + + $scope.remove = function(index) { + var confirmMsg = 'Are you sure you want to delete this item from the list?' + ' Press OK to delete.'; + confirmBoxService.confirm(confirmMsg).then(function (confirmed) { + if (confirmed == true) { + $scope.widgetObject = {}; + $scope.widgetObject.id = $scope.widgetData[parseInt(index)].id; + $scope.widgetObject.category = $scope.widgetData[parseInt(index)].category; + $scope.widgetObject.title = $scope.widgetData[parseInt(index)].title; + $scope.widgetObject.href = $scope.widgetData[parseInt(index)].href; + $scope.widgetObject.sortOrder = $scope.widgetData[parseInt(index)].sortOrder; + + + dashboardService.removeCommonWidgetData($scope.widgetObject) + .then(function(res){ + dashboardService.getCommonWidgetData(message.type) + .then(function(res){ + $scope.widgetData = res.response.items; + $scope.cancelEdit(index); + }); + }); + } + }); + + }; + $scope.closeDialog = function(){ + $scope.closeThisDialog( $scope.widgetData); + } + $scope.deleteSpeedDial = function(widget){ + + } + + $scope.manage = function(index) { + $scope.modflag = true; + + var thisdata = $scope.widgetData[index]; + $scope.editMode=true; + var category = "#category"+index; + var categoryEdit = "#categoryEdit"+index; + + var title = "#title"+index; + var titleEdit = "#titleEdit"+index; + + var url = "#href"+index; + var urlEdit = "#hrefEdit"+index; + + var order = "#order"+index; + var orderEdit = "#orderEdit"+index; + + var eventDate = "#eventDate"+index; + var eventDateEdit = "#eventDateEdit"+index; + + + var dsavebutton ="#savebutton"+index; + var dremovebutton="#removebutton"+index; + var dmanagebutton="#managebutton"+index; + var deditRbutton="#editRbutton"+index; + + $(title).css('display', 'none'); + $(titleEdit).css('display', 'inherit'); + $(url).css('display', 'none'); + $(urlEdit).css('display', 'inherit'); + $(order).css('display', 'none'); + $(orderEdit).css('display', 'inherit'); + $(eventDate).css('display', 'none'); + $(eventDateEdit).css('display', 'inherit'); + + $(dsavebutton).css('display', 'inherit'); + $(dremovebutton).css('display', 'inherit'); + $(dmanagebutton).css('display', 'none'); + $(deditRbutton).css('display', 'inherit'); + + }; + + $scope.cancelEdit = function(index) { + + $scope.editMode=false; + var category = "#category"+index; + var categoryEdit = "#categoryEdit"+index; + + var title = "#title"+index; + var titleEdit = "#titleEdit"+index; + + var url = "#href"+index; + var urlEdit = "#hrefEdit"+index; + + var order = "#order"+index; + var orderEdit = "#orderEdit"+index; + + var eventDate = "#eventDate"+index; + var eventDateEdit = "#eventDateEdit"+index; + + var dsavebutton ="#savebutton"+index; + var dremovebutton ="#removebutton"+index; + var dmanagebutton="#managebutton"+index; + var deditRbutton="#editRbutton"+index; + + $(title).css('display', 'inherit'); + $(titleEdit).css('display', 'none'); + + $(url).css('display', 'inherit'); + $(urlEdit).css('display', 'none'); + + $(order).css('display', 'inherit'); + $(orderEdit).css('display', 'none'); + + $(eventDate).css('display', 'inherit'); + $(eventDateEdit).css('display', 'none'); + + $(dsavebutton).css('display', 'none'); + $(dremovebutton).css('display', 'none'); + $(dmanagebutton).css('display', 'inherit'); + $(deditRbutton).css('display', 'none'); + + }; + + $scope.popupConfirmWin = function(title, msgBody, callback){ + modalService.popupConfirmWin.apply(null, arguments); + }; + } + } + CommonWidgetController.$inject = ['dashboardService', '$scope', 'message', '$q', '$http', 'conf', '$filter','confirmBoxService','$log']; + angular.module('ecompApp').controller('CommonWidgetController', CommonWidgetController); +})(); + +angular.module('ecompApp').filter('cut', function () { + return function (value, wordwise, max, tail) { + if (!value) return ''; + + max = parseInt(max, 10); + if (!max) return value; + if (value.length <= max) return value; + + value = value.substr(0, max); + if (wordwise) { + var lastspace = value.lastIndexOf(' '); + if (lastspace != -1) { + //Also remove . and , so its gives a cleaner result. + if (value.charAt(lastspace-1) == '.' || value.charAt(lastspace-1) == ',') { + lastspace = lastspace - 1; + } + value = value.substr(0, lastspace); + } + } + + return value + (tail || ' …'); + }; +}); + +angular.module('ecompApp').controller('DatePickerController', function ($scope, uibDateParser) { + $scope.today = function() { + $scope.dt = new Date(); + }; + $scope.today(); + + $scope.clear = function() { + $scope.dt = null; + }; + + $scope.inlineOptions = { + customClass: getDayClass, + minDate: new Date(), + showWeeks: true + }; + + $scope.dateOptions = { + dateDisabled: disabled, + formatYear: 'yy', + minDate: new Date(), + startingDay: 1 + }; + + // Disable weekend selection + function disabled(data) { + var date = data.date, + mode = data.mode; + return mode === 'day' && (date.getDay() === 0 || date.getDay() === 6); + } + + $scope.toggleMin = function() { + $scope.inlineOptions.minDate = $scope.inlineOptions.minDate ? null : new Date(); + $scope.dateOptions.minDate = $scope.inlineOptions.minDate; + }; + + $scope.toggleMin(); + + $scope.open1 = function() { + // console.log('In open1'); + $scope.popup1.opened = true; + }; + + $scope.open2 = function() { + // console.log('In open2'); + $scope.popup2.opened = true; + }; + + + $scope.setDate = function(year, month, day) { + $scope.dt = new Date(year, month, day); + }; + + $scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate']; + $scope.format = $scope.formats[3]; + $scope.altInputFormats = ['M!/d!/yyyy']; + + $scope.popup1 = { + opened: false + }; + + $scope.popup2 = { + opened: false + }; + + var tomorrow = new Date(); + tomorrow.setDate(tomorrow.getDate() + 1); + var afterTomorrow = new Date(); + afterTomorrow.setDate(tomorrow.getDate() + 1); + $scope.events = [ + { + date: tomorrow, + status: 'full' + }, + { + date: afterTomorrow, + status: 'partially' + } + ]; + + function getDayClass(data) { + var date = data.date, + mode = data.mode; + if (mode === 'day') { + var dayToCheck = new Date(date).setHours(0,0,0,0); + + for (var i = 0; i < $scope.events.length; i++) { + var currentDay = new Date($scope.events[i].date).setHours(0,0,0,0); + + if (dayToCheck === currentDay) { + return $scope.events[i].status; + } + } + } + + return ''; + } +}); + + + +function toggleCollapsible(id, clickedIconId, subtitutingIconId){ + $("#"+id).toggle(); + $("#"+clickedIconId).toggle(); + $("#"+subtitutingIconId).toggle(); + setTimeout(function(){focusFirstEle(id);}, 1000); +} + +function focusFirstEle(id){ + var focusItems = $("#"+id).find(":focusable"); + for(var i=0; i<focusItems.length; i++){ + if(!isHidden(focusItems[i])){ + var targetClassName = focusItems[i].className; + if(targetClassName!='collapsibleArrow'){ + focusItems[i].focus(); + return; + } + } + } +} + +function validateUrl(value){ + return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value); + } diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.less b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.less new file mode 100644 index 00000000..dda8e300 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.less @@ -0,0 +1,101 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .searchLiHeader { +font-weight: bold; +color: #0574ac; +font-size: 16px; +padding-bottom: 10px; +line-height: 1.5; +font-family: arial; + +} + +.searchLiItems{ +cursor: pointer; +font-weight: normal; +font-size: 12px; +color: #444444; +font-family: arial; +} + +.searchUl { +list-style: none; +border-bottom: 1px solid #bbb; +padding-bottom: 20px; +} + +#editWidgetsContent { + height: 300px; + width: 1770px; +} + +#editWidgetsContent .scroll-viewport { + height: 300px; + width: 1770px; +} + +.full button span { + background-color: limegreen; + border-radius: 32px; + color: black; +} + +.partially button span { + background-color: orange; + border-radius: 32px; + color: black; + } + + .add-widget-field{ + width:250px; + display: inline-table; + margin:10px; + + } + .uib-datepicker-popup { + th { + vertical-align: middle !important; + } + } + + .input-text-area { + font-style: italic; + padding: 7px 10px; + width: 250px !important; + display: inline-block; + position: relative; + margin-bottom: 10px; + border-radius: 6px; + border: 1px solid #d8d8d8; + height: 32px; + border-color: slategrey !important; + } + .input-inline-edit-text { + font-style: italic; + padding: 7px 10px; + display: inline-block; + position: relative; + margin-bottom: 10px; + border-radius: 6px; + border: 1px solid #d8d8d8; + height: 32px; + border-color: slategrey !important; + width:100%; + }
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.js b/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.js new file mode 100644 index 00000000..3228f636 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.js @@ -0,0 +1,312 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function'); + } +} + +(function() { + var HTTP_PROTOCOL_RGX = /https?:\/\//; + + var DashboardCtrl = function DashboardCtrl(applicationsService, $log, + $window, userProfileService, $scope, $cookies, $timeout, $interval, + $modal, $state, dashboardService,ngDialog) { + + var _this = this; + + _classCallCheck(this, DashboardCtrl); + + // activate spinner + this.isLoading = true; + $scope.getUserAppsIsDone = false; + this.userProfileService = userProfileService; + $scope.demoNum = 1; + $scope.event_content_show = false; + $scope.widgetData = []; + + $scope.editWidgetModalPopup = function(availableData, resourceType) { + $scope.editData = JSON.stringify(availableData); + $scope.availableDataTemp = $scope.availableData; + ngDialog.open({ + templateUrl : 'app/views/dashboard/dashboard-widget-manage.html', + controller : 'CommonWidgetController', + resolve : { + message : function message() { + var message = { + type : resourceType, + availableData : $scope.editData + }; + return message; + } + } + }).closePromise.then(needUpdate => { + if(resourceType=='NEWS'){ + $scope.updateNews(); + }else if(resourceType=='EVENTS'){ + $scope.updateEvents(); + }else if(resourceType=='IMPORTANTRESOURCES'){ + $scope.updateImportRes(); + } + }); + }; + + userProfileService.getUserProfile() + .then( + function(profile) { + // $log.info(profile); + $scope.attuid = profile.attuid; + // $log.info('user has the following attuid: ' + + // profile.attuid); + $scope.appsView = []; + + applicationsService.getPersUserApps() + .then( + function(res) { + // $log.info(res); + _this.apps = res; + + for (var i = 0; i < _this.apps.length; i++) { + $scope.appsView[i] = { + sizeX : 2, + sizeY : 2, + headerText : _this.apps[i].name, + subHeaderText : _this.apps[i].notes, + imageLink : _this.apps[i].thumbnail || _this.apps[i].imageUrl, + order : _this.apps[i].order, + restrictedApp : _this.apps[i].restrictedApp, + url : _this.apps[i].url + }; + } + + // Append the show add/remove applications tile + $scope.appsView[_this.apps.length] = { + addRemoveApps : true, + sizeX : 2, + sizeY : 2, + headerText : 'Add/Remove Applications', + subHeaderText : '', + imageLink : 'assets/images/cloud.png', + order : '', + restrictedApp : false, + url : '' + }; + + // $log.info('getUserApps apps count : ' + + // $scope.appsView.length); + // Show 2 rows in the gridster if needed + if ($scope.appsView.length > 6) { + $(".dashboard-boarder").css({ + "height" : "400px" + }); + }else{ + $(".dashboard-boarder").css({ + "height" : "210px" + }); + } + _this.isLoading = false; + $scope.getUserAppsIsDone = true; + })['catch'] + (function(err) { + $log.error('DashboardCtlr: failed to get applications list', err); + _this.isLoading = false; + $scope.getUserAppsIsDone = true; + }); + }); + + + /* Widget Gridster Section */ + $scope.newsGridsterItem = { + sizeX : 4, + sizeY : 4, + headerText : 'News', + subHeaderText : '', + imageLink : '', + order : '', + url : '' + }; + + $scope.eventsGridsterItem = { + sizeX : 4, + sizeY : 4, + headerText : 'Calendar Events', + subHeaderText : '', + imageLink : '', + order : '', + url : '' + }; + + $scope.impoResGridsterItem = { + sizeX : 4, + sizeY : 4, + headerText : 'Resources', + subHeaderText : '', + imageLink : '', + order : '', + url : '' + }; + this.gridsterOpts = { + columns : 12, + colWidth : 95, + rowHeight : 95, + margins : [ 20, 20 ], + outerMargin : true, + pushing : true, + floating : true, + swapping : true, + draggable : { + handle: 'img' + } + }; + + this.emptyGridsterOpts = { + columns : 24, + colWidth : 190, + rowHeight : 190, + margins : [ 20, 20 ], + outerMargin : true, + pushing : true, + floating : true, + swapping : true, + draggable : { + handle: 'img' + } + }; + + this.goToCatalog = function(item) { + $state.go('root.appCatalog'); + } + + // navigate to application url in new tab + this.goToPortal = function(item) { + if (!item.url) { + $log.error('No URL found for this application, doing nothing!'); + return; + } + if (item.restrictedApp) { + // Link-based apps open in their own browser tab + $window.open(item.url, '_blank'); + } else { + // cache control so browsers load app page every time + var ccParam = '?cc=' + new Date().getTime(); + var appUrl = null; + var urlParts = item.url.split('#'); + if (urlParts.length < 2) + appUrl = item.url + ccParam; + else + appUrl = urlParts[0] + ccParam + "#" + urlParts[1]; + // $log.debug('DashboardCtrlr::goToPortal: opening tab with URL + // ' + appUrl); + var tabContent = { + id : new Date(), + title : item.headerText, + url : appUrl + }; + $cookies.putObject('addTab', tabContent); + } + }; + + /* News data */ + $scope.newsData = []; + $scope.updateNews = function() { + $scope.newsData.length=0; + dashboardService.getCommonWidgetData('NEWS').then(function(res) { + // $log.info(res.message); + var newsDataJsonArray = res.response.items; + for (var i = 0; i < newsDataJsonArray.length; i++) { + $scope.newsData.push(newsDataJsonArray[i]); + } + })['catch'](function(err) { + $log.error('dashboard controller: failed to get news list', err); + _this.isLoading = false; + }); + } + $scope.updateNews(); + + /* Events data */ + $scope.eventData = []; + $scope.updateEvents = function() { + $scope.eventData.length=0; + dashboardService.getCommonWidgetData('EVENTS').then(function(res) { + var eventDataJsonArray = res.response.items; + for (var i = 0; i < eventDataJsonArray.length; i++) { + if(eventDataJsonArray[i].eventDate !=null) { + // yyyy-mm-dd + eventDataJsonArray[i].year = eventDataJsonArray[i].eventDate.substring(2,4); + eventDataJsonArray[i].mon = eventDataJsonArray[i].eventDate.substring(5,7); + eventDataJsonArray[i].day = eventDataJsonArray[i].eventDate.substring(8,10); + } + $scope.eventData.push(eventDataJsonArray[i]); + } + })['catch'](function(err) { + $log.error('dashboard controller: failed to get Events list', err); + _this.isLoading = false; + }); + } + $scope.updateEvents(); + + /* Important Resources data */ + $scope.importResData = []; + $scope.updateImportRes = function() { + $scope.importResData.length=0; + dashboardService.getCommonWidgetData('IMPORTANTRESOURCES').then( + function(res) { + // $log.error('couldnt get important list...', + // res.response.dataBlob); + // $log.info(res); + var importResDataJSONArray = res.response.items; + for (var i = 0; i < importResDataJSONArray.length; i++) { + $scope.importResData.push(importResDataJSONArray[i]); + } + })['catch'](function(err) { + $log.error('dashboard controller: failed to get resources list...', err); + _this.isLoading = false; + }); + } + $scope.updateImportRes(); + + if (getParameterByName('noUserError') != null) { + if (getParameterByName('noUserError') == "Show") { + $("#errorInfo").show(); + } + } + + }; + + DashboardCtrl.$inject = [ 'applicationsService', '$log', '$window', + 'userProfileService', '$scope', '$cookies', '$timeout', + '$interval', '$modal', '$state', 'dashboardService','ngDialog']; + angular.module('ecompApp').controller('DashboardCtrl', DashboardCtrl); +})(); + +function getParameterByName(name, url) { + if (!url) + url = window.location.href; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex + .exec(url); + if (!results) + return ''; + if (!results[2]) + return ''; + return results[2].replace(/\+/g, " "); +} diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.spec.js b/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.spec.js new file mode 100644 index 00000000..0a2ddd3a --- /dev/null +++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.spec.js @@ -0,0 +1,78 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +describe('Controller: DashboardCtrl ', function() { + beforeEach(module('ecompApp')); + + let DashboardCtrl, $controller, $q, rootScope, $log, $window, $cookies, scope; + let deferredApps, deferredUserProfile, applicationsServiceMock, userProfileServiceMock; + + + beforeEach(inject( (_$controller_, _$q_, _$rootScope_, _$log_, _$window_, _$cookies_, _CacheFactory_)=>{ + rootScope = _$rootScope_; + scope = rootScope.$new(); + $q = _$q_; + $controller = _$controller_; + $log = _$log_; + $window = _$window_; + $cookies = _$cookies_; + + _CacheFactory_.destroyAll(); + + deferredApps = $q.defer(); + deferredUserProfile = $q.defer(); + applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock', ['getUserApps']); + applicationsServiceMock.getUserApps.and.returnValue(deferredApps.promise); + + userProfileServiceMock = jasmine.createSpyObj('userProfileServiceMock',['getUserProfile']); + userProfileServiceMock.getUserProfile.and.returnValue(deferredUserProfile.promise); + + DashboardCtrl = $controller('DashboardCtrl', { + applicationsService: applicationsServiceMock, + $log: $log, + $window: $window, + userProfileService: userProfileServiceMock, + $scope: scope, + $cookies: $cookies + }); + scope.$digest(); + })); + + it('should populate this.apps with data from portals service getUserApps', inject(function ( _$q_) { + $q = _$q_; + + let profile = {roles: 'superAdmin', userId: 'userid'}; + + deferredUserProfile.resolve(profile) + deferredApps.resolve([{name: 'portal1'},{name: 'portal2'},{name: 'portal3'}]); + scope.$digest(); + expect(scope.appsViewData.length).toBe(3); + })); + + it('should call $log error when getAllPortals fail', inject(function ( _$q_) { + $q = _$q_; + spyOn($log, 'error'); + deferredUserProfile.reject('something happened!'); + scope.$digest(); + expect($log.error).toHaveBeenCalled(); + })); + +}); diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard.less b/ecomp-portal-FE/client/app/views/dashboard/dashboard.less new file mode 100644 index 00000000..4dc1a6c8 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard.less @@ -0,0 +1,766 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-applications-home { + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + .gridster-item-container .gridster-item-body { + overflow-y:auto; + overflow-x:hidden; + } + + .go-button { + .btn-green; + width: 96px; + position: absolute; + border-radius: 0px; + } + + .applications-home-container { + .content_justify; + position: relative; + padding: 15px 0 32px 0; + width: 100%; + + .applications-home-title { + .a24r; + margin: auto; + .content_justify; + } + .portals-list { + min-height: 70vh; + //display: flex; + justify-content: center; + flex-flow: row wrap; + width: 1170px; + + margin: auto; + margin-bottom: 63px; + + .app-gridster-header { + background-color: @u; + font-size:12px; + overflow:hidden + } + + .app-gridster-footer { + background-color: @u; + } + + .portals-list-item { + background-color: @u; + border-radius: 2px; + box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1); + display: inline-block; + width: 360px; + height: 300px; + background-size: cover; + cursor: pointer; + margin: 15px; + overflow: hidden; + + .portals-item-info { + background-color: @u; + height: 120px; + top: 180px; + position: relative; + box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1); + padding: 16px; + + .info-title { + .a24r; + margin-bottom: 4px; + + text-overflow: ellipsis; + overflow: hidden; + } + .info-description { + .c16r; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .info-button { + .btn-green; + width: 96px; + position: absolute; + bottom: 16px; + left: 16px; + } + + &:hover { + opacity: .93; + z-index: 3; + } + } + } + } + } +} +.w-ecomp-main-error{ + .o14r; + position: absolute; + width: 100%; + line-height: 1.5em; +} +.w-ecomp-main-disclaimer { + text-align: center; + .o14r; + //position: absolute; + bottom: -75px; + line-height: 1.5em; + margin: 0 auto; + width:1170px; + position: relative; + +} +.build-number { + .o12i; +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.slide.ng-hide-add, +.slide.ng-hide-remove, +.slide.ng-enter, +.slide.ng-leave { + transition: all 0.5s ease; +} +.slide.ng-hide, +.slide.ng-enter { + transform: translate(-100%, 0); +} +.slide.ng-enter-active { + transform: translate(0, 0); +} +.slide.ng-leave { + transform: translate(0, 0); +} +.slide.ng-leave-active { + transform: translate(+100%, 0); +} +.dashboard-boarder{ + height: 210px; + overflow: auto; +} +.dashboard-information-boarder{ + border: 2px solid #ccc!important; + border-radius: 16px!important; +} + +#information-section { + margin-top:50px; +} + +.information-section-title{ + color: #0574ac; + font-family: arial; + font-size: 25px; + margin: 20px; + width: 1170px; +} +#left { + float: left; + width: 33%; + height:300px; + background-color:white; +} + +#center { + float: right; + width: 33%; + height:300px; + background: white; +} +#right { + margin-left:34%; + margin-right:34%; + height:300px; + background:white; +} + + +.dock ul{ +display: inline-block; + +width: auto; +margin: 0px; +padding: 0px; +list-style: none; + +} +.dock ul li { +width: auto; +height: auto; +display: inline-block; +bottom: 0; +vertical-align: bottom; +margin-top: -43px; +} +.dock ul li a { +display: block; +height: 150px; +width: 150px; +position: relative; +-webkit-transition-property: width, height,margin-top; +-webkit-transition-duration: 0.5s; +-o-transition-property: width, height,margin-top; +-o-transition-duration: 0.5s; +-moz-transition-property: width, height,margin-top; +-moz-transition-duration: 0.5s; +} +.dock ul li a:hover { +width: 200px; +height: 200px; +margin-top: -50px; +} +.dock ul li a img { +width: 100%; +position: absolute; +bottom: 0; +left: 0; +border: none; +padding: 0px 0px 0px 30px; +} +.dock_left{ +width: 31px; +-webkit-transform: rotate(33deg); +-moz-transform: rotate(33deg); +-o-transform: rotate(33deg); +position: relative; +background: #EEE; +overflow: hidden; +float: left; +height: 100px; +z-index: 2; +margin: -18px; +} +.dock_right{ +width: 36px; +-webkit-transform: rotate(-33deg); +-moz-transform: rotate(-33deg); +-o-transform: rotate(-33deg); +position: relative; +background: #EEE; +overflow: hidden; +float: left; +height: 100px; +z-index: 2; +margin: -18px; +} + + + + +* {margin: 0; padding: 0;} +body {background: #ccc;} + +.slider{ + width: 640px; + position: relative; + padding-top: 320px; + + margin: 50px auto; + + box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.75); +} + +.slider>img{ + position: absolute; + left: 0; top: 0; + transition: all 0.5s; +} + +.slider input[name='slide_switch'] { + display: none; +} + +.slider label { + margin: 18px 0 0 18px; + border: 3px solid #999; + + float: left; + cursor: pointer; + transition: all 0.5s; + + opacity: 0.6; +} + +.slider label img{ + display: block; +} + +.slider input[name='slide_switch']:checked+label { + border-color: #666; + opacity: 1; +} + +.slider input[name='slide_switch'] ~ img { + opacity: 0; + transform: scale(1.1); +} +/*That hides all main images at a 110% size +On click the images will be displayed at normal size to complete the effect +*/ +.slider input[name='slide_switch']:checked+label+img { + opacity: 1; + transform: scale(1); +} + +.accordion { + width: 895px; height: 320px; + overflow: hidden; + box-shadow: 0 10px 6px -6px #111; + margin: 20px auto +} +.accordion ul { width: 200% } + +.accordion li { + position: relative; + display: block; + width: 160px; + float: left; + box-shadow: 0 0 30px 8px #222; + transition: all 0.4s ease .300ms; +} + +.accordion ul:hover li {width: 40px } +.accordion ul li:hover {width: 640px } + +.caption { + background: rgba(0, 0, 0, 0.5); + position: absolute; + bottom: 0; + width: 640px +} + +.caption a { + display: block; + color: #fff; + text-decoration: none; + font: normal 16px 'Lato', Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + padding: 15px; +} + + + +/*events*/ +.events-date{ + margin-left: 10px; + float:left; + white-space: normal; + display: inline-block; + vertical-align: middle; + width: 55px; +} +.event-title-div{ + float: left; + width: 235px; + line-height: 20px; + padding: 5px; + font-size: 14px; + margin-left: 4px; +} +.events-date{ + height: 20px; + margin-left: 4px; + font-size: 14px; +} +.events-content{ + font-size: 14px; + color: #444; + margin-right: 10px; +} +.events-content-body{ + margin-top:5px; + color:#444; + margin-left:12px; + line-height:1.5; +} +.events { + border-radius: 4px; + padding: 3px; + -webkit-user-select: none; +} +.events ul { + float: left; + width:100%; + -webkit-user-select: none +} +.events ul li { + line-height: 30px; + list-style: none; + border-bottom: 2px solid grey; + height: 100%; + min-height: 42px; +} +.events ul li:hover { + background: #ddd; + cursor: pointer; +} +.events ul li a { + color: black; + text-decoration: none; + font: 14px Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -webkit-user-select: none; + font-family: arial; +} +.events-link{ + color: #067ab4 !important; +} + + + +/*widgets*/ +.singleBtnBorder { + border-radius: 6px 6px 6px 6px; +} + +.widgetHeaderBtn{ + + height: 30px; + background-color: #FFFFFF; + position: relative; + display: inline-block; + -moz-background-clip: padding-box; + -webkit-background-clip: padding-box; + background-clip: padding-box; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 8px 20px; + font-size: 14px; + line-height: 14px; + min-width: 60px; + border: none; + border-radius: 6px; + background-color: #ffffff; + background-image: none; + color: #ffffff; + vertical-align: middle; + text-align: center; + text-decoration: none; + text-transform: capitalize; + text-shadow: none; + white-space: nowrap; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; + -webkit-transition: background-color 0.3s ease-out; + -moz-transition: background-color 0.3s ease-out; + transition: background-color 0.3s ease-out; + } + +.widgetHeaderBtnPosition { + width: 30px; + min-width: 0px; + border: 1px solid #AAAAAA; + padding-left: 5px; + padding-right: 5px; + } + +.icon-anchor { + color: #888; +} + +.widgetHeaderBtn:hover, .widgetHeaderBtn:focus { + background: rgba(0, 0, 0, 0); + color: #3a7999; + box-shadow: inset 0 0 0 2px #3a7999; +} + +@keyframes ticker { + 0% {margin-top: 0} + 25% {margin-top: -55px} + 50% {margin-top: -110px} + 75% {margin-top: -165px} + 100% {margin-top: 0} +} + +body { background: #333; width: 100%; height: 100% } + +.news { + + width: 350px; + height: 250px; + margin: 0px auto; + border-radius: 4px; + padding: 3px; + -webkit-user-select: none; + +} + + + +.news ul { + float: left; + width:100%; + -webkit-user-select: none +} + +.news ul li {line-height: 30px; list-style: none; + border-bottom: 2px solid grey; + min-height: 42px; + } + +.news ul li:hover { + background: #ddd; + cursor: pointer; +} + +.news ul li a { + color: black; + text-decoration: none; + font-size: 14px; + line-height: 1.5; + display: inline-block; + width:100%; + min-height:40px; + padding-top: 5px; + padding-bottom: 5px; + -webkit-font-smoothing: antialiased; + -webkit-user-select: none; + + /* font-family: arial;*/ +} + +.news ul:hover { animation-play-state: paused } +.news span:hover+ul { animation-play-state: paused } +.resources { + + width: 350px; + height: 200px; + margin-left: 5px; + border-radius: 4px; + padding: 3px; + -webkit-user-select: none; + +} + + + +.resources ul { + float: left; + width:100%; + + -webkit-user-select: none +} + +.resources ul li {line-height: 30px; list-style: none; + border-bottom: 2px solid grey; + min-height: 42px; + } + +.resources ul li:hover { + background: #ddd; + cursor: pointer; +} + +.resources ul li a { + color: black; + text-decoration: none; + font-size: 14px; + line-height: 1.5; + width:100%; + min-height:40px; + display: inline-block; + padding-top: 5px; + padding-bottom: 5px; + -webkit-font-smoothing: antialiased; + -webkit-user-select: none; +} + + +.blue { background: #347fd0 } +.blue span { background: #2c66be } +.red { background: #d23435 } +.red span { background: #c22b2c } +.green { background: #699B67 } +.green span { background: #547d52 } +.magenta { background: #b63ace } +.magenta span { background: #842696 } + + +.box-one { + -webkit-transition:all linear 0.4s; + transition:all linear 0.4s; + height:100px; width:200px; background:white; border: 2px solid #ccc!important; + border-radius: 16px!important; +} +.box-one.ng-hide { +display: block!important; + opacity:0; +} + +.gridsterContent{ + height:120px; +} +.gridsterAppContent{ + height:120px; + +} + + .gridsterImage{ + height:84px; + width:168px; + } + .grider-content-ecomp{ + transition: transform 0.5s ease-out; + } + .grider-content-ecomp:hover{ + transform: scale(1.1); +} + + +.information-section{ + /*margin-top:25px;*/ +} +.information-section-gridsterContent{ + height:300px; +} +.information-sections-gridster-header{ + color: #0574ac; + font-family: arial; + font-size: 25px; + +} +/*application empty div*/ +.app-error-block { + padding-top: 10px; + +} +.activity-error-block { + padding-top: 60px; + +} + +.activity-error-msg1{ + text-align: center; + margin-top: 20px; + font-family: arial; + color: #444; + font-size: 20px; + +} + +.newstape { + background: white; + color: black; + height: 400px; + overflow: hidden; +} + +.newstape-content { + position: relative; + padding: 15px; +} + +.newstape-drag { cursor: ns-resize; } + +.text-center { text-align: center; } + +.text-right { text-align: right; } + +.text-justify { text-align: justify; } + +.optionsMenu{ + position: absolute; + list-style: none; + top: 25px; + right: 10px; + border: 1px solid #067ab4; + display: none; + z-index: 2; + border-radius: 6px 0px 6px 6px; + background: #fff; + width: 130px; +} + +.optionsMenuLink { + position: relative; + padding-left: 8px; + padding-right: 2px; + font-size: 12px; + line-height: 30px; + color: #444444; +} +.optionsMenu > li:hover a { + color: #ffffff !important; +} +.optionsMenu > li { + width: 100%; + text-align: left; +} +.optionsMenu > li:hover { + background-color: #0faaf7; + border-color: none !important; + cursor: pointer; +} + +.simulateGridHeader{ + position: relative; + height: 50px !important; + border: 1px solid #d3d3d3; + border-bottom: 0; + background-color: #E5E5E5; + white-space: nowrap; + text-overflow: ellipsis; + z-index: 1; +} + +.simulateGridHeaderTitle{ + line-height: 44px; + margin-left: 26px; + font-family: arial; + font-size: 18px; + color: #444444; + float: left; +} + +.simulateGridHeaderHandle{ + cursor: move; + margin: 12px; + position: absolute; + top: 0; + left: 0; + border: 0; + vertical-align: middle; + -ms-interpolation-mode: bicubic; + display: block; +}
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard.tpl.html b/ecomp-portal-FE/client/app/views/dashboard/dashboard.tpl.html new file mode 100644 index 00000000..0d96a585 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard.tpl.html @@ -0,0 +1,346 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-applications-home" id="contentId"> + <div class="applications-home-container"> + <div align="center" id="errorInfo" + style="display: none; font-size: 12px; margin-left: 5px"> + <span style="color: red">You don't have a user account in that + application. Please ask the system administrator. </span> + </div> + <div class="portals-list" style="margin-bottom: 0px;"> + <div> + <div class="simulateGridHeader"> + <span class="simulateGridHeaderTitle">Applications</span> + </div> + <div class="dashboard-boarder"> + <div class="gridster-container override_background"> + + <div att-gridster att-gridster-options="dashboard.gridsterOpts"> + + <div ng-if="appsView.length==1"> + <i class="ion-information-circled full-linear-ion-information-circled" + style="margin-left: 50%; font-size: 90px; color: black"></i> <br> + <div class="activity-error-msg1"> + <span>Click on the tile at the left to personalize your applications.</span> + </div> + </div> + + <div class="style_prevu_kit" att-gridster-item='item' + ng-repeat="item in appsView" + id="app-{{item.headerText.split(' ').join('-')}}"> + <!-- A regular application tile --> + <div class="grider-content-ecomp" ng-if="item.addRemoveApps == null"> + <div id="grips-{{item.headerText.split(' ').join('-')}}" + ng-hide="users.isLoadingTable && !users.getUserAppsIsDone" + att-gridster-item-header + grips-img-path="assets/images/grips.png" + header-text="{{item.headerText | elipsis: 33}}" + class="app-gridster-header"></div> + <div id="app-image-{{item.headerText.split(' ').join('-')}}" + att-gridster-item-body + ng-style="{'cursor':'pointer', + 'background-image': 'url('+(item.imageLink)+')', + 'order': item.order, + 'background-color':'white', + 'background-repeat': 'no-repeat', + 'background-size': '170px 130px'}" + ng-click="dashboard.goToPortal(item)" + ng-hide="users.isLoadingTable && !users.getUserAppsIsDone" + class="gridsterContent"></div> + </div> + <!-- Format the special tile differently --> + <div class="grider-content-ecomp" style="width: 170px" ng-if="item.addRemoveApps"> + <div id="grips-add-remove-applications-tile" att-gridster-item-header + grips-img-path="assets/images/grips.png" + header-text='Select applications..' + ng-click="dashboard.goToCatalog()" + ng-style="{'cursor':'pointer'}" + class="app-gridster-header"> + </div> + <div id="app-image-empty" att-gridster-item-body + ng-style="{'cursor':'pointer', + 'text-align':'center', + 'order': item.order, + 'background-color':'white', + 'background-repeat': 'no-repeat', + 'background-size': '170px 130px', + 'width': '170px', + 'height': '120px', + 'opacity': '1'}" + ng-click="dashboard.goToCatalog()" class="gridsterContent"> + <label style="font-size:12px">Click here to personalize <br> this applications page</label> + <i class="ion-plus-circled" style="font-size:70px;color: #067ab4"></i> + </div> + </div> + </div> + + </div> + + </div> + + </div> + </div> + + <br> + <br> + + <div class="information-section"> + <div id="news-gridster-container" + class="gridster-container override_background"> + <div att-gridster att-gridster-options="dashboard.gridsterOpts"> + + <div class="style_prevu_kit"> + <!-- News --> + <div att-gridster-item='newsGridsterItem'> + <div att-gridster-item-header + grips-img-path="assets/images/grips.png" + header-text="{{newsGridsterItem.headerText | elipsis: 33}}" + header-icon="full-linear-icon-webwindow" + sub-header-text="{{newsGridsterItem.subHeaderText | elipsis: 56}}" + class="information-sections-gridster-header" + style="font-size: 18px"> + + <div ng-if="isAdminPortalAdmin == true" + style="font-size: 20px;"> + + <a id="widgetHeaderBtns" + class="widgetHeaderBtn widgetHeaderBtnPosition singleBtnBorder" + ng-mousedown="showNewsOption=!showNewsOption" + ng-mouseleave="showNewsOption=false" + ng-class="singleBtnBorder" role="button" alt="Settings" + title="Settings" att-accessibility-click="13,32" tabindex="0"> + <span class="ion-more" style="color:#888"></span> + </a> + <ul class="optionsMenu" + style="max-width: 130px; height: 32px; display: block" + ng-show="showNewsOption" ng-mouseenter="showNewsOption=true" + ng-mouseleave="showNewsOption=false"> + <!-- ngIf: widget.duplicateAllowed --> + <li ng-click="editWidgetModalPopup(newsData, 'NEWS');" + ddh-accessibility-click="13,32"><a + class="optionsMenuLink" href="javascript:void(0)">Edit</a></li> + <!-- end ngIf: widget.duplicateAllowed --> + + </ul> + </div> + + </div> + <div id="newsContainer" att-gridster-item-body + class="information-section-gridsterContent"> + <div class="news"> + <ul id="newsContent" ng-show="newsData.length!=0"> + <li ng-repeat="news in newsData"><a href="{{news.href}}" + target="_blank" ng-bind="news.title"></a></li> + </ul> + <div ng-hide="newsData.length!=0"> + <div class="activity-error-container" + style="background: rgb(255, 255, 255); overflow: auto !important; width: 100%;"> + <div class="activity-error-block"> + <i class="ion-information-circled full-linear-ion-information-circled" + style="margin-left: 125px; font-size: 90px"></i> <br> + <div class="activity-error-msg1">There's currently no + news available.</div> + </div> + </div> + </div> + </div> + </div> + </div> + <!-- Events --> + <div att-gridster-item='eventsGridsterItem'> + <div att-gridster-item-header + grips-img-path="assets/images/grips.png" + header-text="{{eventsGridsterItem.headerText | elipsis: 33}}" + header-icon="full-linear-icon-calendar" + sub-header-text="{{eventsGridsterItem.subHeaderText | elipsis: 56}}" + class="information-sections-gridster-header" + style="font-size: 18px"> + <div style="font-size: 20px;" + ng-if="isAdminPortalAdmin == true" class="ng-scope" + role="button" tabindex="0"> + <a id="widgetHeaderBtns" + class="widgetHeaderBtn widgetHeaderBtnPosition singleBtnBorder" + ng-mousedown="showEventsOption=!showEventsOption" + ng-mouseleave="showEventsOption=false" + ng-class="singleBtnBorder" role="button" alt="Settings" + title="Settings" att-accessibility-click="13,32" tabindex="0"> + <span class="ion-more" style="color:#888"></span> + </a> + <ul class="optionsMenu" + style="max-width: 130px; height: 32px; display: block" + ng-show="showEventsOption" + ng-mouseenter="showEventsOption=true" + ng-mouseleave="showEventsOption=false"> + <!-- ngIf: widget.duplicateAllowed --> + <li ng-click="editWidgetModalPopup(eventData, 'EVENTS');"> + <a class="optionsMenuLink" href="javascript:void(0)" + ddh-accessibility-click="13,32">Edit</a> + </li> + <!-- end ngIf: widget.duplicateAllowed --> + </ul> + </div> + </div> + <div att-gridster-item-body + class="information-section-gridsterContent"> + <div class="events"> + <ul ng-show="eventData.length!=0"> + <li ng-repeat="event in eventData"> + <div ng-click="event_content_show=!event_content_show"> + <div class="events-date">{{event.mon}}/{{event.day}}/{{event.year}} + </div> + <div> + <div class="event-title-div"> + <p ng-bind="event.title"></p> + </div> + <div> + <span class="ion-ios-arrow-up" ng-if="event_content_show" + style="color: #888; font-size: 22px;"></span> <span + class="ion-ios-arrow-down" ng-if="!event_content_show" + style="color: #888; font-size: 22px;"></span> + + </div> + <div style="clear: both;"></div> + </div> + </div> + <div class="events-content" ng-show="event_content_show"> + <div class="events-content-body"> + <a class="events-link" ng-href="{{event.href}}" + target="_blank"> <span ng-bind="event.content"></span> + </a> + </div> + <div></div> + </div> + </li> + + + </ul> + <div ng-hide="eventData.length!=0"> + <div class="activity-error-container" + style="background: rgb(255, 255, 255); overflow: hidden !important; width: 100%;"> + <div class="activity-error-block"> + <i class="ion-information-circled full-linear-ion-information-circled" + style="margin-left: 125px; font-size: 90px"></i> <br> + <div class="activity-error-msg1">There's currently no + event available.</div> + </div> + </div> + </div> + </div> + </div> + </div> + + <!-- Important Resources --> + <div att-gridster-item='impoResGridsterItem'> + <div att-gridster-item-header + grips-img-path="assets/images/grips.png" + header-text="{{impoResGridsterItem.headerText | elipsis: 33}}" + header-icon="full-linear-icon-cloud" + sub-header-text="{{impoResGridsterItem.subHeaderText | elipsis: 56}}" + class="information-sections-gridster-header" + style="font-size: 18px"> + <!-- <span class="full-linear-icon-cloud" style="font-size: 33px; font-weight: bold; margin-right: 10px;"></span> + --> + <div style="font-size: 20px;" + ng-if="isAdminPortalAdmin == true" class="ng-scope" + role="button" tabindex="0"> + <a id="widgetHeaderBtns" + class="widgetHeaderBtn widgetHeaderBtnPosition singleBtnBorder" + ng-class="singleBtnBorder" role="button" alt="Settings" + ng-mousedown="showImpResOption=!showImpResOption" + ng-mouseleave="showImpResOption=false" title="Settings" + att-accessibility-click="13,32" tabindex="0"> <span + class="ion-more" style="color:#888"></span> + </a> + + <ul class="optionsMenu" + style="max-width: 130px; height: 32px; display: block" + ng-show="showImpResOption" + ng-mouseenter="showImpResOption=true" + ng-mouseleave="showImpResOption=false"> + <!-- ngIf: widget.duplicateAllowed --> + <li + ng-click="editWidgetModalPopup(importResData, 'IMPORTANTRESOURCES');"> + <a class="optionsMenuLink" href="javascript:void(0)" + ddh-accessibility-click="13,32">Edit</a> + </li> + <!-- end ngIf: widget.duplicateAllowed --> + + </ul> + </div> + </div> + <div att-gridster-item-body + class="information-section-gridsterContent"> + <div class="resources"> + <ul ng-show="importResData.length!=0"> + + <li ng-repeat="item in importResData"><a + href="{{item.href}}" target="_blanl">{{item.title}}</a></li> + </ul> + <div ng-hide="importResData.length!=0"> + <div class="activity-error-container" + style="background: rgb(255, 255, 255); overflow: hidden !important; width: 100%;"> + <div class="activity-error-block"> + <i class="ion-information-circled full-linear-ion-information-circled" + style="margin-left: 125px; font-size: 90px"></i> <br> + <div class="activity-error-msg1">There's currently no + links available.</div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + + <div class="style_prevu_kit" att-gridster-item='item' + ng-repeat="item in widgetData"> + <div> + <div att-gridster-item-header + grips-img-path="assets/images/grips.png" + header-text="{{item.headerText | elipsis: 33}}" + sub-header-text="{{item.subHeaderText | elipsis: 56}}" + class="app-gridster-header"></div> + <div att-gridster-item-body></div> + </div> + + </div> + + <div class="style_prevu_kit" att-gridster-item='item' + ng-repeat="item in widgetData"> + <div> + <div att-gridster-item-header + grips-img-path="assets/images/grips.png" + header-text="{{item.headerText | elipsis: 33}}" + sub-header-text="{{item.subHeaderText | elipsis: 56}}" + class="app-gridster-header"></div> + <div att-gridster-item-body></div> + </div> + + </div> + </div> + </div> + </div> + + <div></div> + + </div> + + </div> +</div>
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/dashboard/newsticker.controller.js b/ecomp-portal-FE/client/app/views/dashboard/newsticker.controller.js new file mode 100644 index 00000000..88e6942e --- /dev/null +++ b/ecomp-portal-FE/client/app/views/dashboard/newsticker.controller.js @@ -0,0 +1,47 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class NewsTickerController { + constructor($scope) { + $scope.newstape = function(){ + jQuery('.newstape').newstape({ + + period: 30, + + offset: 1, + + mousewheel: true, + + mousewheelRate: 30, + + dragable: true, + + heightSpy: true + + }); + }; + $scope.newstape(); + + } + } + NewsTickerController.$inject = ['$scope']; + angular.module('ecompApp').controller('NewsTickerController', NewsTickerController); +})(); diff --git a/ecomp-portal-FE/client/app/views/errors/error.404.tpl.html b/ecomp-portal-FE/client/app/views/errors/error.404.tpl.html new file mode 100644 index 00000000..255bfe36 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/errors/error.404.tpl.html @@ -0,0 +1,31 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-main"> + <div class="w-ecomp-main-container"> + <div class="admins-page-main"> + <div id="404-title" class="w-ecomp-main-view-title">Page Not Found</div> + <div class="admins-table"> + <span id="404-error-text" class="errors-text ">The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable. Please select from the + menu on the left to navigate to a valid page. + </span> + </div> + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/errors/error.controller.js b/ecomp-portal-FE/client/app/views/errors/error.controller.js new file mode 100644 index 00000000..7bf86c24 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/errors/error.controller.js @@ -0,0 +1,36 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class ErrorCtrl { + constructor($scope, $window) { + + $scope.errorMsg = ''; + + if(window.location.href.indexOf('noUserError') !== -1){ + $scope.errorMsg = 'You do not have a user account for this application. Please contact your system administrator'; + } else if(window.location.href.indexOf('unKnownError') !== -1){ + $scope.errorMsg = 'An unknown error has occurred. Please contact your system administrator'; + } + } + } + ErrorCtrl.$inject = ['$scope','$window']; + angular.module('ecompApp').controller('ErrorCtrl', ErrorCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/errors/error.tpl.html b/ecomp-portal-FE/client/app/views/errors/error.tpl.html new file mode 100644 index 00000000..9de8084a --- /dev/null +++ b/ecomp-portal-FE/client/app/views/errors/error.tpl.html @@ -0,0 +1,26 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-main"> + <div class="w-ecomp-main-container"> + <div > + <div class="admins-table" align="center" id="errorInfo" style="font-size:12px;margin-left:5px"><span class="errors-text">{{errorMsg}}</span></div> + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/footer/footer.controller.js b/ecomp-portal-FE/client/app/views/footer/footer.controller.js new file mode 100644 index 00000000..adcd470a --- /dev/null +++ b/ecomp-portal-FE/client/app/views/footer/footer.controller.js @@ -0,0 +1,49 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class FooterCtrl { + constructor($scope, $rootScope, manifestService, $log) { + this.manifestService = manifestService; + this.$log = $log; + this.$scope = $scope; + this.$rootScope = $rootScope; + + $scope.buildinfo = null; + + $rootScope.showFooter = true; + $scope.date = new Date(); + + $scope.portalName = "OpenECOMP Portal Version: "; + + manifestService.getManifest().then( jsonObj => { + // $log.debug('FooterCtrl.getManifest: ', JSON.stringify(jsonObj)); + $scope.buildInfo = jsonObj; + + }).catch(err=> { + $log.error('FooterCtrl::updateTableData error :',err); + }); + }; + + } + + FooterCtrl.$inject = ['$scope', '$rootScope', 'manifestService', '$log']; + angular.module('ecompApp').controller('FooterCtrl', FooterCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/footer/footer.controller.spec.js b/ecomp-portal-FE/client/app/views/footer/footer.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/footer/footer.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/footer/footer.less b/ecomp-portal-FE/client/app/views/footer/footer.less new file mode 100644 index 00000000..f8eee07d --- /dev/null +++ b/ecomp-portal-FE/client/app/views/footer/footer.less @@ -0,0 +1,53 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-footer { + z-index: 101; + background-color: rgb(6, 122, 180); + position: absolute; + bottom: 0; + width: 100%; + height: @footer-height; + .o12r; + color: white; + + a { + color: white; + } + + .footer-image { + margin: auto; + + } + + .build-number { + float: right; + padding: 5px; + color: white; + } + .footer-content { + align-items: center; + text-align: center; + vertical-align: middle; + display: flex; + justify-content: center; + //width: @table-width; + margin: auto; + } +} diff --git a/ecomp-portal-FE/client/app/views/footer/footer.tpl.html b/ecomp-portal-FE/client/app/views/footer/footer.tpl.html new file mode 100644 index 00000000..a1788a3a --- /dev/null +++ b/ecomp-portal-FE/client/app/views/footer/footer.tpl.html @@ -0,0 +1,36 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-footer" id="w-ecomp-footer" ng-style="{height: tabBottom}"> + <div id="footer" class="footer-content"> + <div > + <img id="logo-image" class="footer-image"> + </div> + <div > + <p> + + </p> + <p> + + </p> + <p id="build-number" class="build-number" >OpenECOMP Portal Version: 1.0.0</p> + <!-- <p id="build-number" class="build-number" >ECOMP Portal Version: {{buildInfo.manifest['Implementation-Version']}}.{{buildInfo.manifest['Build-Number']}}</p> --> + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js new file mode 100644 index 00000000..0d27d868 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js @@ -0,0 +1,384 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class MenuDetailsModalCtrl { + constructor($scope, $log, functionalMenuService, errorMessageByCode, ECOMP_URL_REGEX,$rootScope,confirmBoxService) { + let newMenuModel = { + name: null, + menuId: null, + parentMenuId: null, + url: null + }; + + let getAvailableRoles = (appid) => { + this.isSaving = true; + if (appid != null) { + $log.debug("MenuDetailsModalCtrl::getAvailableRoles: About to call getManagedRolesMenu"); + functionalMenuService.getManagedRolesMenu(appid).then(rolesObj => { + $log.debug("MenuDetailsModalCtrl::getAvailableRoles: Roles returned = " + JSON.stringify(rolesObj)) + this.availableRoles = rolesObj; + this.preSelectedRoles = {roles:[]}; + + if(($scope.ngDialogData.source=='edit') && this.isMidLevelMenuItem()){ + this.preSelectedRoles = {roles:[]}; + }else if(!angular.isUndefined(this.menuItem.menuDetails) && + $scope.ngDialogData.source=='edit' && this.isLeafMenuItem() && + this.menuItem.menuDetails.appid!=appid) { + this.preSelectedRoles = {roles:[]}; + }else{ + if((!angular.isUndefined(this.menuItem.menuDetails)) && + (!angular.isUndefined(this.menuItem.menuDetails.roles))){ + $log.debug('menuDetails.roles: ' + this.menuItem.menuDetails.roles); + for(var i=0; i<this.menuItem.menuDetails.roles.length; i++){ + var role = {"roleId":this.menuItem.menuDetails.roles[i]}; + $log.debug('MenuDetailsModalCtrl::getAvailableRoles: adding role to preselected: ' + i + ': ' + JSON.stringify(role)); + this.preSelectedRoles.roles.push(role); + } + } + } + $rootScope.$broadcast('availableRolesReady'); + this.isSaving = false; + }).catch(err => { + $log.error("MenuDetailsModalCtrl::getAvailableRoles: error: " + err); + }); + } else { + $log.debug("MenuDetailsModalCtrl::getAvailableRoles: appid was null"); + } + }; + + let getAvailableApps = () => { + functionalMenuService.getAvailableApplications().then(apps => { + $log.debug("MenuDetailsModalCtrl::getAvailableApps: Apps returned = " + JSON.stringify(apps)) + this.availableApps = apps; + if (angular.isDefined(this.selectedApp) && angular.isDefined(this.selectedApp.index)) { + for (var i = 0; i < apps.length; i++) { + if (apps[i].index === this.selectedApp.index) { + $log.debug("MenuDetailsModalCtrl::getAvailableApps: found app with index: " + this.selectedApp.index); + $log.debug("MenuDetailsModalCtrl::getAvailableApps: setting isDisabled to: " + !apps[i].enabled); + this.selectedApp.isDisabled = !apps[i].enabled; + break; + } + } + $log.debug("didn't find index: " + this.selectedApp.index); + } + })['catch'](function (err) { + $log.error("MenuDetailsModalCtrl::getAvailableApps: getAvailableApps error: " + err); + }).finally(()=>{ + this.isSaving = false; + }); + }; + + let init = () => { + $log.info('MenuDetailsModalCtrl::init'); + this.saveOrContinueBtnText = "Save"; + this.isSaving = false; + this.displayRoles = $scope.ngDialogData.source=='view' ? true : false; + this.formEditable = $scope.ngDialogData.source=='view' ? false : true; + this.selectedRole = []; + this.availableRoles = []; + this.menuItem = _.clone($scope.ngDialogData.menuItem); + $log.info('MenuDetailsModalCtrl::getAvailableApps: Within init, about to check menuDetails for defined'); + if(!angular.isUndefined(this.menuItem.menuDetails) && + ($scope.ngDialogData.source=='view' || + ($scope.ngDialogData.source=='edit') && this.isLeafMenuItem() )){ + + $log.debug("MenuDetailsModalCtrl::init: menuItem: "); + $log.debug('MenuDetailsModalCtrl::init: ',this.menuItem); + this.menuItem.menu.url = this.menuItem.menuDetails.url; + this.selectedApp={}; + this.selectedApp.index = this.menuItem.menuDetails.appid; + getAvailableRoles(this.selectedApp.index); + + } + + if($scope.ngDialogData.source=='view' || $scope.ngDialogData.source=='edit'){ + this.menutitle = this.menuItem.menu.name; + this.menuLocation = this.isParentMenuItem() ? this.menuItem.menu.name : this.menuItem.menu.parent.name; + }else{ + this.menutitle = ''; + this.menuLocation = this.menuItem.menu.name; + } + getAvailableApps(); + $log.debug("MenuDetailsModalCtrl::init: Menu details: " + JSON.stringify(this.menuItem.menuDetails)); + }; + + + this.ECOMP_URL_REGEX = ECOMP_URL_REGEX; + + this.conflictMessages = {}; + this.scrollApi = {}; + let handleConflictErrors = err => { + if(!err.data){ + return; + } + if(!err.data.length){ + err.data = [err.data] + } + $log.debug('MenuDetailsModalCtrl::handleConflictErrors: err.data = ' + JSON.stringify(err.data)); + _.forEach(err.data, item => { + _.forEach(item.fields, field => { + this.conflictMessages[field.name] = errorMessageByCode[item.errorCode]; + $log.debug('MenuDetailsModalCtrl::handleConflictErrors: fieldName = ' + field.name); + $scope.functionalMenuForm[field.name].$setValidity('conflict', false); + watchOnce[field.name](); + }); + }); + this.scrollApi.scrollTop(); + }; + + let resetConflict = fieldName => { + delete this.conflictMessages[fieldName]; + $log.debug('MenuDetailsModalCtrl::resetConflict: $setValidity(true) = ' + fieldName); + if($scope.functionalMenuForm[fieldName]){ + $scope.functionalMenuForm[fieldName].$setValidity('conflict', true); + } + }; + + let watchOnce = { + text: () => { + let unregisterName = $scope.$watch('functionalMenuDetails.menutitle', (newVal, oldVal) => { + if(newVal.toLowerCase() !== oldVal.toLowerCase()){ + resetConflict('text'); + unregisterName(); + } + }); + }, + url: () => { + let unregisterUrl = $scope.$watch('functionalMenuDetails.menuItem.menu.url', (newVal, oldVal) => { + if(newVal.toLowerCase() !== oldVal.toLowerCase()){ + resetConflict('url'); + unregisterUrl(); + } + }); + } + }; + + //*************************** + + this.isLeafMenuItem = () => { + return this.menuItem.menu.children.length>0 ? false : true; + }; + + this.isMidLevelMenuItem = () => { + return this.menuItem.menu.parentMenuId!=null && this.menuItem.menu.children.length>0 ? true : false; + }; + + this.isParentMenuItem = () => { + return this.menuItem.menu.parentMenuId!=null ? false : true; + }; + + this.updateSelectedApp = (appItem) => { + if (!appItem) { + return; + } + $log.debug('MenuDetailsModalCtrl::updateSelectedApp: drop down app item = ' + JSON.stringify(appItem.index)); + $log.debug("MenuDetailsModalCtrl::updateSelectedApp: appItem in updateSelectedApp: "); + $log.debug('MenuDetailsModalCtrl::updateSelectedApp: ',appItem); + this.selectedApp.isDisabled = ! appItem.enabled; + $log.debug("MenuDetailsModalCtrl::updateSelectedApp: isDisabled: "+this.selectedApp.isDisabled); + getAvailableRoles(appItem.index); + }; + + this.continue = () => { + this.displayRoles = true; + this.formEditable = false; + }; + + this.saveChanges = () => { + + if(!!this.menuItem.menu.url && (angular.isUndefined(this.selectedApp) || !this.selectedApp.index>0)) { + confirmBoxService.showInformation('Please select the appropriate app, or remove the url').then(isConfirmed => {}); + return; + }else if(!this.menuItem.menu.url && !angular.isUndefined(this.selectedApp) && this.selectedApp.index>0){ + confirmBoxService.showInformation('Please enter url, or select "No Application"').then(isConfirmed => {}); + return; + }else if(!this.menutitle){ + confirmBoxService.showInformation('Please enter the Menu title').then(isConfirmed => {}); + return; + } + + this.isSaving = true; + var activeMenuItem = {}; + + if ($scope.ngDialogData.source == 'edit') { + $log.debug('MenuDetailsModalCtrl::saveChanges: Will be saving an edit menu item'); + activeMenuItem = { + menuId:this.menuItem.menu.menuId, + column:this.menuItem.menu.column, + text:this.menutitle, + parentMenuId:this.menuItem.menu.parentMenuId, + url:this.menuItem.menu.url, + appid: angular.isUndefined(this.selectedApp) ? null:this.selectedApp.index, + roles:this.selectedRole + }; + if (!activeMenuItem.appid && !activeMenuItem.url) { + activeMenuItem.roles = null; + } + functionalMenuService.saveEditedMenuItem(activeMenuItem) + .then(() => { + $log.debug('MenuDetailsModalCtrl::saveChanges: Menu Item saved'); + $scope.closeThisDialog(true); + }).catch(err => { + if(err.status === 409){ + handleConflictErrors(err); + } + $log.error('MenuDetailsModalCtrl::saveChanges: error - ',err); + }).finally(()=>{ + this.isSaving = false; + }); + + $log.debug("MenuDetailsModalCtrl::saveChanges: Edit Menu output will be: " + JSON.stringify(activeMenuItem)); + } else { + $log.debug('MenuDetailsModalCtrl::saveChanges: Will be saving a New menu item'); + var newMenuItem = { + menuId:null, + column:this.menuItem.menu.column, + text:this.menutitle, + parentMenuId:this.menuItem.menu.menuId, + url:this.menuItem.menu.url, + appid: angular.isUndefined(this.selectedApp) ? null:this.selectedApp.index, + roles:this.selectedRole + }; + + $log.debug("MenuDetailsModalCtrl::saveChanges: New Menu output will be: " + JSON.stringify(newMenuItem)); + functionalMenuService.saveMenuItem(newMenuItem) + .then(() => { + $log.debug('MenuDetailsModalCtrl::saveChanges: Menu Item saved'); + $scope.closeThisDialog(true); + }).catch(err => { + if(err.status === 409){//Conflict + handleConflictErrors(err); + } + $log.error('MenuDetailsModalCtrl::saveChanges error: ', err); + }).finally(()=>{ + this.isSaving = false; + }); + + } + }; + + init(); + + $scope.$on('$stateChangeStart', e => { + e.preventDefault(); + }); + } + } + MenuDetailsModalCtrl.$inject = ['$scope', '$log', 'functionalMenuService', 'errorMessageByCode', 'ECOMP_URL_REGEX','$rootScope','confirmBoxService']; + angular.module('ecompApp').controller('MenuDetailsModalCtrl', MenuDetailsModalCtrl); + + angular.module('ecompApp').directive('dropdownMultiselect', ['functionalMenuService',function(){ + return { + restrict: 'E', + scope: { + model: '=', + options: '=', + populated_roles: '=preSelected', + dropdownTitle: '@', + source: '=' + }, + template: "<div class='btn-group' data-ng-class='{open: open}'>" + + "<button class='btn btn-medium'>{{dropdownTitle}}</button>" + + "<button class='btn dropdown-toggle' data-ng-click='open=!open;openDropDown()'><span class='caret'></span></button>" + + "<ul class='dropdown-menu dropdown-menu-medium' aria-labelledby='dropdownMenu'>" + + "<li data-ng-repeat='option in options'> <input ng-disabled='setDisable(source)' type='checkbox' data-ng-change='setSelectedItem(option.roleId)' ng-model='selectedItems[option.roleId]'>{{option.rolename}}</li>" + + "</ul>" + + "</div>", + controller: function ($scope) { + $scope.selectedItems = {}; + $scope.checkAll = false; + $scope.$on('availableRolesReady', function() { + init(); + }); + + function init() { + console.log('dropdownMultiselect init'); + $scope.dropdownTitle = $scope.source =='view' ? 'View Roles' : 'Select Roles'; + console.log('$scope.populated_roles = ' + $scope.populated_roles); + } + + $scope.$watch('populated_roles', function(){ + if ($scope.populated_roles && $scope.populated_roles.length>0) { + for (var i = 0; i < $scope.populated_roles.length; i++) { + $scope.model.push($scope.populated_roles[i].roleId); + $scope.selectedItems[$scope.populated_roles[i].roleId] = true; + } + if ($scope.populated_roles.length == $scope.options.length) { + $scope.checkAll = true; + } + }else{ + deselectAll(); + } + }); + + $scope.openDropDown = function () { + + }; + + $scope.checkAllClicked = function () { + if ($scope.checkAll) { + selectAll(); + } else { + deselectAll(); + } + }; + + function selectAll() { + $scope.model = []; + $scope.selectedItems = {}; + angular.forEach($scope.options, function (option) { + $scope.model.push(option.roleId); + }); + angular.forEach($scope.model, function (id) { + $scope.selectedItems[id] = true; + }); + console.log($scope.model); + }; + + function deselectAll() { + $scope.model = []; + $scope.selectedItems = {}; + console.log($scope.model); + }; + + $scope.setSelectedItem = function (id) { + var filteredArray = []; + if ($scope.selectedItems[id] == true) { + $scope.model.push(id); + } else { + filteredArray = $scope.model.filter(function (value) { + return value != id; + }); + $scope.model = filteredArray; + $scope.checkAll = false; + } + console.log(filteredArray); + return false; + }; + + $scope.setDisable = function(source){ + return source =='view' ? true : false; + } + } + } + }]); + +})(); diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html new file mode 100644 index 00000000..7157886b --- /dev/null +++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html @@ -0,0 +1,35 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="functionalMenu-details-modal"> + <div class="title">Delete Menu Item</div> + <div class="functionalMenu-properties-main" scroll-top="functionalMenuDetails.scrollApi"> + <form name="functionalMenuForm" novalidate> + <div class="confirmation-message-wrap"> + <div class="confirmation-message" ng-bind="confirmBox.message"></div> + </div> + </form> + </div> + <div class="dialog-control"> + <span class="ecomp-save-spinner" ng-show="functionalMenuDetails.isSaving"></span> + <div id='menu-details-next-button' class="next-button" + ng-class="{disabled: functionalMenuForm.$invalid}" ng-click="functionalMenuDetails.saveChanges()">Delete</div> + <div class="cancel-button" ng-click="closeThisDialog()">Cancel</div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html new file mode 100644 index 00000000..58588ce5 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html @@ -0,0 +1,106 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="functionalMenu-details-modal"> + <div class="title">{{ngDialogData.title}}</div> + <div class="functionalMenu-properties-main" scroll-top="functionalMenuDetails.scrollApi"> + <form name="functionalMenuForm"> + <div class="item"> + <div class="item-label">Parent</div> + <input id="input-parent" readonly="readonly" class="input-field" + value="{{functionalMenuDetails.menuLocation}}" type="text" name="menuItem" required/> + </div> + <div class="item"> + <div class="item-label">Title</div> + <input id="input-title" ng-readonly="!functionalMenuDetails.formEditable ? 'checked':'' " + placeholder="Enter text" class="input-field" ng-model="functionalMenuDetails.menutitle" type="text" name="text"/> + <div id="div-error-conflict" class="error-container" ng-show="functionalMenuDetails.conflictMessages.text"> + <small id="msg-error-conflict" id="functionalMenuDetails-input-title-conflict" + class="err-message" ng-bind="functionalMenuDetails.conflictMessages.text"></small> + </div> + </div> + <div class="item"> + <div class="left-item"> + <div class="item-label">URL</div> + <input id="input-url" ng-readonly="!functionalMenuDetails.formEditable ? 'checked':'' " + placeholder="http://" class="input-field" ng-model="functionalMenuDetails.menuItem.menu.url" type="url" name="url"/> + <span id="error-url-conflict" class="err-message" ng-show="functionalMenuForm.url.$error.url"> Not valid URL!</span> + </div> + <div class="right-item"> + <div class="item-label">App</div> + <div class="custom-select-wrap" ng-readonly="checked"> + <select id="select-app" class="select-field" + ng-model="functionalMenuDetails.selectedApp" + ng-change="functionalMenuDetails.updateSelectedApp(functionalMenuDetails.selectedApp);" + ng-options="app.title for app in functionalMenuDetails.availableApps track by app.index" + ng-disabled="!functionalMenuDetails.availableApps || !functionalMenuDetails.availableApps.length + || !functionalMenuDetails.formEditable" + name="app" + required> + <option value="" >No Application</option> + </select> + </div> + </div> + </div> + <div class="item" ng-show="functionalMenuDetails.displayRoles==true"> + <div class="item-label">Roles</div> + <dropdown-multiselect id="dropdown-roles" dropdown-title="Select Roles" + pre-selected="functionalMenuDetails.preSelectedRoles.roles" + model="functionalMenuDetails.selectedRole" + options="functionalMenuDetails.availableRoles" source="ngDialogData.source"> + </dropdown-multiselect> + </div> + <div ng-show="functionalMenuDetails.selectedApp && functionalMenuDetails.selectedApp.isDisabled" class="err-message">Application is disabled</div> + </form> + </div> + <div class="dialog-control"> + <div ng-if="ngDialogData.source!='view'"> + <span class="ecomp-save-spinner" ng-show="functionalMenuDetails.isSaving"></span> + <span ng-if="ngDialogData.source=='edit' && functionalMenuDetails.isLeafMenuItem()"> + <button class="save-button" id="add-menu-button-save" + ng-show="!functionalMenuDetails.menuItem.menu.url || + !(functionalMenuDetails.selectedApp.index>0) || + functionalMenuDetails.displayRoles" + ng-disabled="!functionalMenuForm.text.$valid" + ng-click="functionalMenuDetails.saveChanges()">Save</button> + <button class="save-button" id="add-menu-button-continue" + ng-show="!!functionalMenuDetails.menuItem.menu.url && + functionalMenuDetails.selectedApp.index>0 && + functionalMenuDetails.menutitle && + !functionalMenuDetails.displayRoles" + ng-disabled="!functionalMenuForm.url.$valid" ng-click="functionalMenuDetails.continue()">Continue</button> + </span> + <span ng-if="ngDialogData.source=='add' || (ngDialogData.source=='edit' && !functionalMenuDetails.isLeafMenuItem())"> + <button id="button-save-add" + class="save-button" ng-show="!functionalMenuDetails.menuItem.menu.url || !(functionalMenuDetails.selectedApp.index>0) + || functionalMenuDetails.selectedRole.length>0 || !functionalMenuDetails.menutitle" ng-disabled="!functionalMenuForm.text.$valid || !functionalMenuDetails.menutitle" + ng-click="functionalMenuDetails.saveChanges()">Save</button> + <button id="button-save-continue" class="save-button" + ng-show="!!functionalMenuDetails.menuItem.menu.url && functionalMenuDetails.selectedApp.index>0 + && functionalMenuDetails.menutitle + && !functionalMenuDetails.selectedRole.length>0" ng-disabled="!functionalMenuForm.url.$valid" + ng-click="functionalMenuDetails.continue()">Continue</button> + </span> + <button id="button-cancel-close-dialog" class="cancel-button" ng-click="closeThisDialog()">Cancel</button> + </div> + <div ng-if="ngDialogData.source=='view'"> + <div id="div-close-dialog" class="cancel-button" ng-click="closeThisDialog()">Close</div> + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less new file mode 100644 index 00000000..c322f453 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less @@ -0,0 +1,103 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .functionalMenu-details-modal { + height: 430px; + + .title { + .n18r; + border-bottom: @a 3px solid; + + } + + .btn-medium{ + width:216px; + } + .dropdown-menu-medium{ + width:236px; + max-height: 200px; + overflow-y: auto; + } + + .functionalMenu-properties-main { + padding: 16px; + height: 306px; + overflow-y: visible; + + .item{ + position: relative; + margin-bottom: 18px; + + .input-field{ + .custom-input-field; + width: 100%; + &.url{ + width: 78%; + display: inline-block; + } + } + + .select-field { + .custom-select-field; + } + + .item-label{ + .o14r; + } + + .right-item{ + position: relative; + display: inline-block; + width: 48%; + float: right; + } + .left-item{ + display: inline-block; + width: 48%; + } + + .url-validation-button{ + .btn-blue; + width: 20%; + display: inline-block; + float: right; + } + + .error-container{ + position: absolute; + width: 220px; + display: block; + height: 12px; + line-height: 12px; + + .err-message{ + color: red; + font-size: 9px; + } + .valid-message{ + color: green; + font-size: 9px; + } + } + + } + + } + +} diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.controller.js b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.controller.js new file mode 100644 index 00000000..d17c4ab8 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.controller.js @@ -0,0 +1,331 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class FunctionalMenuCtrl { + constructor($log, functionalMenuService, $scope,ngDialog, confirmBoxService) { + $log.info('FunctionalMenuCtrl init'); + + $scope.invokeDialog = () => { + alert("click dialog"); + }; + + this.regenerateFunctionalMenuAncestors = () => { + functionalMenuService.regenerateFunctionalMenuAncestors().then(res => { + $log.debug("FunctionalMenuCtrl:regenerateFunctionalMenuAncestors::returned from regenerateFunctionalMenuAncestors API call"); + confirmBoxService.showInformation('You have successfully regenerated the menu.').then(isConfirmed => { + }); + })['catch'](function (err) { + $log.error("FunctionalMenuCtrl:regenerateFunctionalMenuAncestors:: error: " + err); + confirmBoxService.showInformation('There was an error while regenerating the menu.').then(isConfirmed => { + }); + }); + }; + + let getFunctionalMenu = () => { + this.isLoadingTable = true; + functionalMenuService.getManagedFunctionalMenu().then(res => { + + let actualData=[]; + + for(let i = 0; i < res.length; i++){ + res[i].children=[]; + res[i].label=res[i].text; + res[i].id=res[i].text; + + } + + for(let i = 0; i < res.length; i++){ + + let parentId=res[i].menuId; + for(let j = 0; j < res.length; j++){ + let childId=res[j].parentMenuId; + if(parentId==childId){ + res[i].children.push(res[j]); + + } + } + } + + res.sort(function(a, b) { + return a.column-b.column; + }) + + for(let i = 0; i < res.length; i++){ + res[i].children.sort(function(a, b){ + return a.column-b.column; + }) + } + + for(let i = 0; i < res.length; i++){ + let parentId=res[i].parentMenuId; + if(parentId==null){ + actualData.push(res[i]); + } + } + + $scope.treedata = actualData; + + }).catch(err => { + $log.error('FunctionalMenuCtrl:getFunctionalMenu:: error ',err); + }).finally(()=> { + this.isLoadingTable = false; + }); + + }; + + + let init = () => { + this.isLoadingTable = false; + this.functionalMenu = []; + getFunctionalMenu(); + this.searchString = ''; + }; + + this.filterByDropdownValue = item => { + if(this.filterByApp.value === ''){ + return true; + } + return item.appName === this.filterByApp.value; + }; + + let getDialogTitle = (source) => { + switch (source) { + case 'edit': + return "Functional Menu - Edit"; + case 'view': + return "Functional Menu - View"; + case 'add': + return "Functional Menu - Add"; + default: + return "Functional Menu"; + } + } + + $scope.reloadTreeStructure = (selectedItem,source) => { + getFunctionalMenu(); + }; + $scope.openMenuDetailsModal = (selectedItem,source) => { + let data = null; + let selectedMenuDetails = null; + console.log('selectedItem: ', selectedItem); + + functionalMenuService.getMenuDetails(selectedItem.menuId) + .then(function( resp ){ + selectedMenuDetails = resp; + $log.info('FunctionalMenuCtrl::openMenuDetailsModal: getMenuDetails: ', resp ); + + if(selectedItem){ + data = { + menuItem: {menu: _.clone(selectedItem),menuDetails:_.clone(selectedMenuDetails)}, + source: source, + title: getDialogTitle(source) + } + } + ngDialog.open({ + templateUrl: 'app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html', + controller: 'MenuDetailsModalCtrl', + controllerAs: 'functionalMenuDetails', + data: data + }).closePromise.then(needUpdate => { + if(needUpdate.value === true){ + $log.debug('FunctionalMenuCtrl::openMenuDetailsModal: updating table data...'); + if(source=="edit") + init(); + } + }); + }); + }; + + + $scope.createNewMenuItem = (selectedItem,source) => { + + if(selectedItem != null && selectedItem.getLevel() >= 4){ + confirmBoxService.showInformation('You are not allowed to have a menu item at a level greater than 4.').then(isConfirmed => { + + }); + return ; + } + + let data = null; + let selectedMenuDetails = null; + functionalMenuService.getMenuDetails(selectedItem.menuId) + .then(function( resp ){ + selectedMenuDetails = resp; + + if((selectedItem.children==null || !selectedItem.children.length>0) && + (!!selectedMenuDetails.url || !!selectedMenuDetails.appid || !!selectedMenuDetails.roles)){ + confirmBoxService.showInformation('Warning: the child menu item "' + selectedItem.name + '" is already configured with an application. You can create a new mid-level menu item, and move this item under it.').then(isConfirmed => { + return; + }); + }else{ + if(selectedItem){ + data = { + menuItem: {menu: _.clone(selectedItem)}, + source:source, + title: getDialogTitle(source) + } + } + + ngDialog.open({ + templateUrl: 'app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html', + controller: 'MenuDetailsModalCtrl', + controllerAs: 'functionalMenuDetails', + data: data + }).closePromise.then(needUpdate => { + if(needUpdate.value === true){ + $log.debug('FunctionalMenuCtrl::getMenuDetails: updating table data...'); + init(); + } + }); + } + }); + }; + + $scope.deleteMenuItem = (selectedItem,source) => { + $log.info('FunctionalMenuCtrl:deleteMenuItem:: delete selectedItem: ', selectedItem); + + if(selectedItem.children!=null && selectedItem.children.length>0){ + confirmBoxService.showInformation('You are not allowed to delete a menu item that has children. You can only delete leaf menu items.').then(isConfirmed => { + + }); + }else{ + confirmBoxService.deleteItem(selectedItem.name).then(isConfirmed => { + if(isConfirmed){ + $log.info('FunctionalMenuCtrl:deleteMenuItem:: Deleting Menu Item :: name: '+selectedItem.name+'; menuId: '+selectedItem.menuId); + $log.info('FunctionalMenuCtrl:deleteMenuItem:: selectedItem: ', selectedItem); + + functionalMenuService.deleteMenuItem(selectedItem.menuId).then(() => { + $log.info('FunctionalMenuCtrl:deleteMenuItem:: Removed Menu Item :: '+selectedItem.name); + init(); + }).catch(err => { + $log.error(err); + }); + } + }).catch(err => { + $log.error(err); + }); + } + }; + + init(); + } + } + FunctionalMenuCtrl.$inject = ['$log', 'functionalMenuService','$scope', 'ngDialog', 'confirmBoxService']; + angular.module('ecompApp').controller('FunctionalMenuCtrl', FunctionalMenuCtrl); + + angular.module('ecompApp').directive('jqTree', ['functionalMenuService','$log','confirmBoxService',function(functionalMenuService,$log,confirmBoxService){ + return { + templateUrl: 'jqtree-tmpl.html', + link: function(scope, el, attrs){ + + var $jqTree = el.find('#jqTree').tree({ + data: scope.treedata, + autoOpen: false, + dragAndDrop: true, + onCreateLi: function(node, $li) { + $li.attr('id', node.id.replace(/\s+/g,"_")); + } + }); + + el.find('#jqTree').bind('tree.move', function(event){ + event.preventDefault(); + console.log('moved_node', event.move_info.moved_node); + console.log('target_node', event.move_info.target_node); + console.log('position', event.move_info.position); + console.log('previous_parent', event.move_info.previous_parent); + + + + if(event.move_info.target_node != null && + ((event.move_info.position == 'after' && event.move_info.target_node.getLevel() > 4) || + (event.move_info.position == 'inside' && event.move_info.target_node.getLevel() > 3))){ + confirmBoxService.showInformation('You are not allowed to have a menu item at a level greater than 4.').then(isConfirmed => { + + }); + return ; + } + + var confMsg = 'Are you sure you want to move "'+event.move_info.moved_node.name+'" ?'; + if ((event.move_info.position == "inside") && (event.move_info.target_node.url != "")) { + confMsg = 'Warning: You are moving "'+event.move_info.moved_node.name+'" under "'+event.move_info.target_node.name+'", which has application information associated with it. This will cause all the application information from "'+event.move_info.target_node.name+'" to be deleted.'; + } + confirmBoxService.moveMenuItem(confMsg).then(isConfirmed => { + if(isConfirmed){ + var new_column = event.move_info.target_node.column; + var old_column = event.move_info.moved_node.column; + if ((event.move_info.moved_node.parentMenuId != + event.move_info.target_node.parentMenuId) || + (new_column < old_column) + ) { + new_column += 1; + } + var activeMenuItem = { + menuId:event.move_info.moved_node.menuId, + column:new_column, + text:"", + parentMenuId:event.move_info.target_node.parentMenuId, + url:"", + appid: null, + roles:null + }; + + if (event.move_info.position == "inside") { + console.log("special case: target_node is parent"); + activeMenuItem.column = 1; + activeMenuItem.parentMenuId = event.move_info.target_node.menuId; + } + + functionalMenuService.saveEditedMenuItem(activeMenuItem) + .then(() => { + $log.debug(' Menu Item moved'); + scope.reloadTreeStructure(); + }).catch(err => { + $log.error(err); + }).finally(()=>{ + }); + } + }).catch(err => { + $log.error(err); + }); + }); + + + $jqTree.jqTreeContextMenu(el.find('ul.dropdown-menu'), { + "view": function (node) {scope.openMenuDetailsModal(node,'view'); }, + "edit": function (node) {scope.openMenuDetailsModal(node,'edit'); }, + "delete": function (node) { scope.deleteMenuItem(node,'delete') }, + "add": function (node) { scope.createNewMenuItem(node,'add') } + }); + + scope.$watch('treedata', function(oldValue, newValue){ + if(oldValue !== newValue){ + console.log('FunctionalMenuCtrl:: Tree value has changed in some way'); + $jqTree.tree('loadData', scope.treedata); + $jqTree.tree('reload', function() { + console.log('FunctionalMenuCtrl:: Tree is reloaded'); + }); + } + }) + } + } + }]); + +})(); diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.less b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.less new file mode 100644 index 00000000..24a91287 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.less @@ -0,0 +1,70 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .functional-admin-page-main{ + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + .u16r; + + .functional-admin-container { + .content_justify; + position: relative; + padding: 0 0 32px; + width: 1170px; + + .functional-admin-home-title { + .a24r; + padding-bottom: 20px; + .content_justify; + } + .tree{ + } + .functional-admin-button-container { + padding-top: 10px; + .btn-blue{ + height: 35px; + width: 140px; + } + } + } + .error-text { + width: 1170px; + margin: auto; + padding: 20px; + left: 20px; + font-weight: bold; + font-size: 16px; + text-align: left; + color: @err; + background-color: @u; + + .error-help { + color: @o; + font-weight: normal; + } + } + +}
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.tpl.html b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.tpl.html new file mode 100644 index 00000000..a04cfe4c --- /dev/null +++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.tpl.html @@ -0,0 +1,48 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-main"> + <div class="w-ecomp-main-container"> + <div class="functional-admin-page-main" id="contentId"> + <div class="functional-admin-container"> + <div id="functional-admin-home-title" class="functional-admin-home-title">Edit Functional Menu</div> + <div id="tree1"> + <div id="Service_Creation"></div> + </div> + <div id="jq-tree-div" jq-tree class="tree"></div> + <script type="text/ng-template" id="jqtree-tmpl.html"> + <div id="jqTree"></div> + <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"> + <li id="functional-menu-view-li"><a id="functional-menu-view-href" ng-href="#view"><i class="ion-eye"></i> View</a></li> + <li class="divider"></li> + <li id="functional-menu-add-li"><a id="functional-menu-add-href" ng-href="#add"><i class="ion-plus-round"></i> Add</a></li> + <li class="divider"></li> + <li id="functional-menu-edit-li"><a id="functional-menu-edit-href" ng-href="#edit"><i class="ion-edit"></i> Edit</a></li> + <li class="divider"></li> + <li id="functional-menu-delete-li"><a id="functional-menu-delete-href" ng-href="#delete"><i class="ion-trash-b"></i> Delete</a></li> + </ul> + </script> + <div class="functional-admin-button-container"> + <button class="btn-blue" ng-click="functionalMenu.regenerateFunctionalMenuAncestors()">Regenerate Menu</button> + <span class="n16r">Click when you are done with your changes</span> + </div> + </div> + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/jqTreeContextMenu.js b/ecomp-portal-FE/client/app/views/functionalMenu/jqTreeContextMenu.js new file mode 100644 index 00000000..21e8454d --- /dev/null +++ b/ecomp-portal-FE/client/app/views/functionalMenu/jqTreeContextMenu.js @@ -0,0 +1,192 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +(function ($) { + if (!$.fn.tree) { + throw "Error jqTree is not loaded."; + } + + $.fn.jqTreeContextMenu = function (menuElement, callbacks) { + + var self = this; + var $el = this; + + var $menuEl = menuElement; + + var nodeToDisabledMenuItems = {}; + + $menuEl.hide(); + + $el.bind("contextmenu", function (e) { + e.preventDefault(); + return false; + }); + + $el.bind('tree.contextmenu', function (event) { + var x = event.click_event.pageX; + var y = event.click_event.pageY; + var yPadding = 5; + var xPadding = 5; + var menuHeight = $menuEl.height(); + var menuWidth = $menuEl.width(); + var windowHeight = $(window).height(); + var windowWidth = $(window).width(); + + if (menuHeight + y + yPadding > windowHeight) { + y = y - menuHeight; + } + if (menuWidth + x + xPadding > windowWidth) { + x = x - menuWidth; + } + + if (Object.keys(nodeToDisabledMenuItems).length > 0) { + if (event.node.name in nodeToDisabledMenuItems) { + var nodeName = event.node.name; + var items = nodeToDisabledMenuItems[nodeName]; + if (items.length === 0) { + $menuEl.find('li').addClass('disabled'); + $menuEl.find('li > a').unbind('click'); + } else { + $menuEl.find('li > a').each(function () { + $(this).closest('li').removeClass('disabled'); + var hrefValue = $(this).attr('href'); + var value = hrefValue.slice(hrefValue.indexOf("#") + 1, hrefValue.length) + if ($.inArray(value, items) > -1) { + $(this).closest('li').addClass('disabled'); + $(this).unbind('click'); + } + }); + } + } else { + $menuEl.find('li.disabled').removeClass('disabled'); + } + } + + $menuEl.show(); + + $menuEl.offset({ left: x, top: y }); + + var dismissContextMenu = function () { + $(document).unbind('click.jqtreecontextmenu'); + $el.unbind('tree.click.jqtreecontextmenu'); + $menuEl.hide(); + } + + $(document).bind('click.jqtreecontextmenu', function () { + dismissContextMenu(); + }); + + $el.bind('tree.click.jqtreecontextmenu', function (e) { + dismissContextMenu(); + }); + + var selectedNode = $el.tree('getSelectedNode'); + if (selectedNode !== event.node) { + $el.tree('selectNode', event.node); + } + + var menuItems = $menuEl.find('li:not(.disabled) a'); + if (menuItems.length !== 0) { + menuItems.unbind('click'); + menuItems.click(function (e) { + e.stopImmediatePropagation(); + dismissContextMenu(); + var hrefAnchor = e.currentTarget.attributes.href.nodeValue; + var funcKey = hrefAnchor.slice(hrefAnchor.indexOf("#") + 1, hrefAnchor.length) + var callbackFn = callbacks[funcKey]; + if (callbackFn) { + callbackFn(event.node); + } + return false; + }); + } + }); + + this.disable = function () { + if (arguments.length === 0) { + $menuEl.find('li:not(.disabled)').addClass('disabled'); + $menuEl.find('li a').unbind('click'); + nodeToDisabledMenuItems = {}; + } else if (arguments.length === 1) { + var items = arguments[0]; + if (typeof items !== 'object') { + return; + } + $menuEl.find('li > a').each(function () { + var hrefValue = $(this).attr('href'); + var value = hrefValue.slice(hrefValue.indexOf("#") + 1, hrefValue.length) + if ($.inArray(value, items) > -1) { + $(this).closest('li').addClass('disabled'); + $(this).unbind('click'); + } + }); + nodeToDisabledMenuItems = {}; + } else if (arguments.length === 2) { + var nodeName = arguments[0]; + var items = arguments[1]; + nodeToDisabledMenuItems[nodeName] = items; + } + }; + + this.enable = function () { + if (arguments.length === 0) { + $menuEl.find('li.disabled').removeClass('disabled'); + nodeToDisabledMenuItems = {}; + } else if (arguments.length === 1) { + var items = arguments[0]; + if (typeof items !== 'object') { + return; + } + + $menuEl.find('li > a').each(function () { + var hrefValue = $(this).attr('href'); + var value = hrefValue.slice(hrefValue.indexOf("#") + 1, hrefValue.length) + if ($.inArray(value, items) > -1) { + $(this).closest('li').removeClass('disabled'); + } + }); + + nodeToDisabledMenuItems = {}; + } else if (arguments.length === 2) { + var nodeName = arguments[0]; + var items = arguments[1]; + if (items.length === 0) { + delete nodeToDisabledMenuItems[nodeName]; + } else { + var disabledItems = nodeToDisabledMenuItems[nodeName]; + for (var i = 0; i < items.length; i++) { + var idx = disabledItems.indexOf(items[i]); + if (idx > -1) { + disabledItems.splice(idx, 1); + } + } + if (disabledItems.length === 0) { + delete nodeToDisabledMenuItems[nodeName]; + } else { + nodeToDisabledMenuItems[nodeName] = disabledItems; + } + } + if (Object.keys(nodeToDisabledMenuItems).length === 0) { + $menuEl.find('li.disabled').removeClass('disabled'); + } + } + }; + return this; + }; +} (jQuery)); diff --git a/ecomp-portal-FE/client/app/views/header/header.controller.js b/ecomp-portal-FE/client/app/views/header/header.controller.js new file mode 100644 index 00000000..8d3cab29 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/header/header.controller.js @@ -0,0 +1,419 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class HeaderCtrl { + constructor($log, $window, userProfileService, menusService, $scope, ECOMP_URL_REGEX, $cookies, $state) { + this.firstName = ''; + this.lastName = ''; + this.$log = $log; + this.menusService = menusService; + this.$scope = $scope; + this.favoritesMenuItems = ''; + $scope.favoriteItemsCount = 0; + $scope.favoritesMenuItems = ''; + $scope.showFavorites = false; + $scope.emptyFavorites = false; + $scope.favoritesWindow = false; + + $scope.showNotification = true; + + $scope.hideMenus = false; + + $scope.menuItems = []; + $scope.activeClickSubMenu = { + x: '' + }; + $scope.activeClickMenu = { + x: '' + }; + $scope.megaMenuDataObject =[]; + + this.isLoading = true; + this.ECOMP_URL_REGEX = ECOMP_URL_REGEX; + + var menuStructureConvert = function(menuItems) { + $scope.megaMenuDataObjectTemp = [ + { + text: "Manage/Functions", + children: [], + }, + { + text: "Help", + children: [{ + restrictedApp: true, + text:"Contact Us", + url:"" + }, + { + text:"Get Access", + url:"root.access" + }] + } + + ]; + return $scope.megaMenuDataObjectTemp; + }; + + var unflatten = function( array, parent, tree ){ + + tree = typeof tree !== 'undefined' ? tree : []; + parent = typeof parent !== 'undefined' ? parent : { menuId: null }; + var children = _.filter( array, function(child){ return child.parentMenuId == parent.menuId; }); + + if( !_.isEmpty( children ) ){ + if( parent.menuId === null ){ + tree = children; + }else{ + parent['children'] = children + } + _.each( children, function( child ){ unflatten( array, child ) } ); + } + + return tree; + } + + var self = this; + $scope.$on('handleUpdateUserInfo', function () { + userProfileService.resetFunctionalMenuStaticInfo() + .then(reset_res=> { + userProfileService.getFunctionalMenuStaticInfo() + .then(res=> { + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting Functional Menu Static Info init'); + if(res==null || res.firstName==null || res.firstName=='' || res.lastName==null || res.lastName=='' ){ + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: failed getting userinfo from shared context.. '); + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting userinfo from session '); + userProfileService.getUserProfile() + .then(profile=> { + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting userinfo from session success'); + self.firstName = profile.firstName; + self.lastName = profile.lastName; + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: user has the following roles: ' + profile.roles); + }).catch(err=> { + $log.error('Header Controller:: getUserProfile() failed: ' + err); + }); + }else{ + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting Functional Menu Static Info successfully',res); + self.firstName = res.firstName; + self.lastName = res.lastName; + $scope.contactUsURL = res.contact_us_link; + } + + menusService.GetFunctionalMenuForUser() + .then(jsonHeaderMenu=> { + $scope.menuItems = unflatten( jsonHeaderMenu ); + $scope.megaMenuDataObject = $scope.menuItems; + }).catch(err=> { + $scope.megaMenuDataObject = menuStructureConvert(''); + $log.error('HeaderCtrl::GetFunctionalMenuForUser: HeaderCtrl json returned: ' + err); + }); + + userProfileService.refreshUserBusinessCard(); + + }).catch(err=> { + $log.error('HeaderCtrl::getFunctionalMenuStaticInfo failed: ' + err); + }); + }).catch(err=> { + $log.error('HeaderCtrl::resetFunctionalMenuStaticInfo failed: ' + err); + }) + + + }); + + userProfileService.getFunctionalMenuStaticInfo() + .then(res=> { + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting Functional Menu Static Info init'); + if(res==null || res.firstName==null || res.firstName=='' || res.lastName==null || res.lastName=='' ){ + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: failed getting userinfo from shared context.. '); + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting userinfo from session '); + userProfileService.getUserProfile() + .then(profile=> { + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting userinfo from session success'); + this.firstName = profile.firstName; + this.lastName = profile.lastName; + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: user has the following roles: ' + profile.roles); + }).catch(err=> { + $log.error('Header Controller:: getUserProfile() failed: ' + err); + }); + }else{ + $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting Functional Menu Static Info successfully',res); + this.firstName = res.firstName; + this.lastName = res.lastName; + $scope.contactUsURL = res.contact_us_link; + } + + menusService.GetFunctionalMenuForUser() + .then(jsonHeaderMenu=> { + $scope.menuItems = unflatten( jsonHeaderMenu ); + $scope.megaMenuDataObject = $scope.menuItems; + }).catch(err=> { + $scope.megaMenuDataObject = menuStructureConvert(''); + $log.error('HeaderCtrl::GetFunctionalMenuForUser: HeaderCtrl json returned: ' + err); + }); + + }).catch(err=> { + $log.error('HeaderCtrl::getFunctionalMenuStaticInfo failed: ' + err); + }); + + + $scope.loadFavorites = function () { + $scope.hideMenus = false; + $log.debug('HeaderCtrl::loadFavorites: loadFavorites has happened.'); + if ($scope.favoritesMenuItems == '') { + generateFavoriteItems(); + $log.debug('HeaderCtrl::loadFavorites: loadFavorites is calling generateFavoriteItems()'); + } else { + $log.debug('HeaderCtrl::loadFavorites: loadFavorites is NOT calling generateFavoriteItems()'); + } + } + + $scope.goToUrl = (item) => { + $log.info('HeaderCtrl::goToUrl has started'); + let url = item.url; + let restrictedApp = item.restrictedApp; + if (!url) { + $log.info('HeaderCtrl::goToUrl: No url found for this application, doing nothing..'); + return; + } + if (restrictedApp) { + $window.open(url, '_blank'); + } else { + if(item.url=="getAccess" || item.url=="contactUs"){ + // if (url = window.location.href) + $state.go("root."+url); + var tabContent = { id: new Date(), title: 'Home', url: item.url }; + $cookies.putObject('addTab', tabContent ); + } else { + var tabContent = { id: new Date(), title: item.text, url: item.url }; + $cookies.putObject('addTab', tabContent ); + } + $log.debug('HeaderCtrl::goToUrl: url = ', url); + } + $scope.hideMenus = true; + } + + + + $scope.submenuLevelAction = function(index, column) { + if ($scope.favoritesMenuItems == '') { + generateFavoriteItems(); + $log.debug('HeaderCtrl::submenuLevelAction: submenuLevelAction is calling generateFavoriteItems()'); + } + if (index=='Favorites' && $scope.favoriteItemsCount != 0) { + $log.debug('HeaderCtrl::submenuLevelAction: Showing Favorites window'); + $scope.favoritesWindow = true; + $scope.showFavorites = true; + $scope.emptyFavorites = false; + } + if (index=='Favorites' && $scope.favoriteItemsCount == 0) { + $log.debug('HeaderCtrl::submenuLevelAction: Hiding Favorites window in favor of No Favorites Window'); + $scope.favoritesWindow = true; + $scope.showFavorites = false; + $scope.emptyFavorites = true; + } + if (index!='Favorites' ) { + $scope.favoritesWindow = false; + $scope.showFavorites = false; + $scope.emptyFavorites = false; + } + + }; + + $scope.hideFavoritesWindow = function() { + $scope.showFavorites = false; + $scope.emptyFavorites = false; + } + + $scope.isUrlFavorite = function (menuId) { + var jsonMenu = JSON.stringify($scope.favoritesMenuItems); + var isMenuFavorite = jsonMenu.indexOf('menuId\":' + menuId); + if (isMenuFavorite==-1) { + return false; + } else { + return true; + } + + } + + let generateFavoriteItems = () => { + menusService.getFavoriteItems() + .then(favorites=> { + $scope.favoritesMenuItems = favorites; + $scope.favoriteItemsCount = Object.keys(favorites).length; + $log.info('HeaderCtrl.getFavoriteItems:: number of favorite menus: ' + $scope.favoriteItemsCount); + }).catch(err=> { + $log.error('HeaderCtrl.getFavoriteItems:: Error retrieving Favorites menus: ' + err); + }); + } + + $scope.setAsFavoriteItem = function(event, menuId){ + var jsonMenuID = angular.toJson({'menuId': + menuId }); + $log.debug('HeaderCtrl::setFavoriteItems: ' + jsonMenuID + " - " + event.target.id); + + menusService.setFavoriteItem(jsonMenuID) + .then(() => { + angular.element('#' + event.target.id).css('color', '#fbb313'); + generateFavoriteItems(); + }).catch(err=> { + $log.error('HeaderCtrl::setFavoriteItems:: API setFavoriteItem error: ' + err); + }); + }; + + $scope.removeAsFavoriteItem = function(event, menuId){ + $log.debug('-----------------------------removeAsFavoriteItem: ' + menuId + " - " + event.target.id); + menusService.removeFavoriteItem(menuId) + .then(() => { + angular.element('#' + event.target.id).css('color', '#666666'); + generateFavoriteItems(); + }).catch(err=> { + $log.error('HeaderCtrl::removeAsFavoriteItem: API removeFavoriteItem error: ' + err); + }); + }; + + $scope.goToPortal = (headerText, url) => { + if (!url) { + $log.info('HeaderCtrl::goToPortal: No url found for this application, doing nothing..'); + return; + } + if (!ECOMP_URL_REGEX.test(url)) { + url = 'http://' + url; + } + + if(headerText.startsWith("vUSP")) { + window.open(url, '_blank'); + } + else { + var tabContent = { id: new Date(), title: headerText, url: url }; + $cookies.putObject('addTab', tabContent ); + } + }; + + } + } + class LoginSnippetCtrl { + constructor($log, $scope, $cookies, $timeout, userProfileService, sessionService,ngDialog) { + $scope.firstName=""; + $scope.lastName=""; + + $scope.allAppsLogout = function(){ + + var cookieTabs = $cookies.getObject('visInVisCookieTabs'); + if(cookieTabs!=null){ + for(var t in cookieTabs){ + + var url = cookieTabs[t].content; + if(url != "") { + sessionService.logout(url); + } + } + } + // wait for individular applications to logoutout before the portal logout + $timeout(function() { + window.location = "logout.htm"; + }, 2000); + } + + $scope.openEditUserModal = function(){ + + var data = { + loginId : $scope.loginSnippetUserId, + updateRemoteApp : false, + appId : $scope.selectedApp!=null?$scope.selectedApp.id:'' + } + var modalInstance = ngDialog.open({ + templateUrl: 'app/views/header/user-edit/edit-user.tpl.html', + controller: 'editUserController', + data: data, + resolve: { + message: function message() { + var message = { + type: 'Contact', + }; + return message; + } + } + }).closePromise.then(needUpdate => { + //updateContactUsTable(); + }); + + } + + try { + userProfileService.getFunctionalMenuStaticInfo() + .then(res=> { + $log.info('HeaderCtrl::LoginSnippetCtrl: Login information: ' + JSON.stringify(res)); + $scope.firstName = res.firstName; + $scope.lastName = res.lastName; + $scope.loginSnippetEmail = res.email; + $scope.loginSnippetUserId = res.userId; + $scope.lastLogin = res.last_login; + }).catch(err=> { + $log.error('HeaderCtrl::LoginSnippetCtrl: User Profile error: ' + err); + }); + } catch (err) { + $log.error('HeaderCtrl::LoginSnippetCtrl:getFunctionalMenuStaticInfo failed: ' + err); + } + + $scope.$on('refreshUserBusinessCard', function () { + try { + userProfileService.getFunctionalMenuStaticInfo() + .then(res=> { + $log.info('HeaderCtrl::LoginSnippetCtrl: Login information: ' + JSON.stringify(res)); + $scope.firstName = res.firstName; + $scope.lastName = res.lastName; + $scope.loginSnippetEmail = res.email; + $scope.loginSnippetUserId = res.userId; + $scope.lastLogin = res.last_login; + }).catch(err=> { + $log.error('HeaderCtrl::LoginSnippetCtrl: User Profile error: ' + err); + }); + } catch (err) { + $log.error('HeaderCtrl::LoginSnippetCtrl:getFunctionalMenuStaticInfo failed: ' + err); + } + }); + + + } + } + class NotificationCtrl{ + constructor($log, $scope, $cookies, $timeout, sessionService) { + $scope.notifications=[]; + for(var i=0;i<6;i++){ + var data ={ + content:'Notification '+i, + time:'10:0'+i+'AM' + }; + $scope.notifications.push(data); + } + + $scope.deleteNotification = function(index){ + + $scope.notifications.splice(index,1); + } + } + } + NotificationCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService']; + LoginSnippetCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService','ngDialog']; + HeaderCtrl.$inject = ['$log', '$window', 'userProfileService', 'menusService', '$scope', 'ECOMP_URL_REGEX','$cookies','$state']; + angular.module('ecompApp').controller('HeaderCtrl', HeaderCtrl); + angular.module('ecompApp').controller('loginSnippetCtrl', LoginSnippetCtrl); + angular.module('ecompApp').controller('notificationCtrl', NotificationCtrl); + +})(); diff --git a/ecomp-portal-FE/client/app/views/header/header.controller.spec.js b/ecomp-portal-FE/client/app/views/header/header.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/header/header.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/header/header.less b/ecomp-portal-FE/client/app/views/header/header.less new file mode 100644 index 00000000..19ea467a --- /dev/null +++ b/ecomp-portal-FE/client/app/views/header/header.less @@ -0,0 +1,380 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .header-section { + position: relative; + z-index: 999; +} + +.logo-image { + .ecomp-logo; + display: inline-block; + vertical-align: middle; + margin-top: -3px; + } + +.portal-title { + font-weight: 400; + font-family: arial !important; + font-size: 18px; +} + + +.megamenu-tabs .megamenu__item { + padding: 0 0; +} +.megamenu-tabs .megamenu__item span { + font-size: 18px; +} + +.submenu-tabs { + line-height:50px; +} +.submenu-tabs .sub__menu{ + top:54px; + padding-top: 10px; + + padding-left: 20px; + + .third-level-title{ + padding-left: 10px; + margin-bottom: 10px; + } +} + +.submenu-tabs .sub__menu .tabsbid__item{ + width:100% +} + +.top-megamenu .megamenu-tabs ul{ + width:98%; + list-style: none; +} + +.megaMenuContainer { + margin-top: 0; + min-height: 50px; + overflow: visible; +} + +.megaMenuDataObject { + float: left; + width: 100%; +} + +.parentmenu-tabs { + height: 55px; +} + +.menu-section { + float: left; +} + +.controlCls{ + font-size: .975rem; + color: #666; + display: inline-block; + font-family: arial; + cursor: pointer; + height: 37px; + line-height: 37px; + padding-bottom: 10px; + vertical-align: middle; + width: 100%; +} + + +.controlCls:hover{ + color:#199DDF !important; +} + +.login-section { + float: right; + min-width:150px; +} + +.login-snippet-text { + display: inline-block; + font-size: 12px; + font-weight: bold; + margin-left: 5px; + overflow: hidden; + max-height: 60px; + max-width: 120px; + padding-top: 20px; + margin-top: 0; + white-space: nowrap; +} + +.megamenu__item { + width: 100px; +} + +.megamenu-item-top { + line-height:55px; +} + + +.newrow { + clear: left; +} + +.header-columns{ + -webkit-column-count:4; + -moz-column-rule: 1px outset #D3D3D3; + -moz-column-count:4; + column-count: 4; + line-height: 12px; + max-height: 500px; + overflow-x: hidden; + overflow-y:hidden; + column-gap: 13px; + column-rule: 1px outset #D3D3D3; + + +} + +.header-columns li{ + -webkit-column-break-inside: avoid; + break-inside: avoid; + //-webkit-page-break-inside: avoid; + page-break-inside: avoid; + + margin-top: 0px !important; + +} + +.header-columns-div{ + width:100%; + margin-left: 12px; + margin-top: 12px; + &:hover{ + .level3-favorites-icon-inactive { + opacity: 1; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + -moz-opacity: 1; + -khtml-opacity: 1; + } + } +} + + +.header-level4-div{ + width:100%; + margin-left: 12px; + margin-top: 12px; + &:hover{ + .level4-favorites-icon-inactive { + opacity: 1; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + -moz-opacity: 1; + -khtml-opacity: 1; + } + } +} + +.favorites-icon-active { + position: relative; + margin-top: 5px; + margin-left: 5px; + top: 3px; + color: @i; +} + +.level3-favorites-icon-inactive { + .favorites-icon-inactive; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -moz-opacity: 0; + -khtml-opacity: 0; +} + +.level4-favorites-icon-inactive { + .favorites-icon-inactive; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -moz-opacity: 0; + -khtml-opacity: 0; +} + +.favorites-icon-inactive { + position: relative; + margin-top: 5px; + margin-left: 5px; + top: 3px; + color: @o; +} + +.favorites-window-empty { + width: 100%; + height: auto; + display:flex; + justify-content:center; + align-items:center; + margin: auto; + text-align: center; + + .largeText { + font-weight: 400; + font-family: arial !important; + font-size: 18px; + text-align: center; + color: @o; + } + + .normal { + color: @o; + font-size: 14px; + text-align: center; + } + + +} +.favorites-window { + width: 100%; + height: auto; + //margin: auto; + font-size: 14px !important; + display: flex; + font-family: arial; + margin-top: 25px; + margin-left: 25px; + z-index: 1000; + + .fav-links { + margin-right: 25px; + } + + .largeText { + font-weight: 400; + font-family: arial !important; + font-size: 18px; + text-align: center; + color: @o; + } + + a:link, a:active, a:hover { + margin-left: 3px; + margin-right: 20px; + text-decoration: none; + } + + a:hover { + color: @z; + } +} + + + + .notifications-count + { + .border-radius(50%); + background:#db3434; + color: @colorWhite; + font: normal .85em 'Lato'; + height: 16px; + + position: absolute; + right: -7px; + text-align: center; + top: -8px; + width: 16px; + } + + .notification-header{ + border-bottom: 1px solid #b4b4b4; + padding: 10px 40px 0px 40px; + } + .notification-heading{ + font-family: arial; + font-size: 24px; + padding-top: 15px; + margin-bottom: 1rem; + } + .notificationBox{ + border-bottom: 1px solid #b4b4b4; + + } + + .notification-info-icon{ + padding-top: 41px; + font-size: 47px; + } + .notification-text { + line-height: 15px; + margin: 0; + padding: 0 0 24px 0; + text-align: center; + font-family: arial; + font-size: 16px; + } + +#icon-user-small{ + display: inline-block; + vertical-align: middle; + width: 20px; + padding-top: 20px; +} + +.notificationBox .icon-circle-action-close { + cursor: pointer; + font-size: 16px; + } + + .notification-close { + padding: 2px 2px 0px 0px; + float: right; + } + + #fav-icon{ + color: #ffb81c; + font-size: 80px; + } + + .edit-user-button{ + margin-top:5px; + display: inline; + margin-left:2px; + width: 20px; + } + + .log-out-button{ + margin-top:5px; + display: inline; + margin-left:10px; + width: 20px; + } + + .new-button { + margin-left:5px; + float: left; + display: block; + width: 80px; + height: 25px; + background: #0574ac!important; + padding: 6px; + text-align: center; + border-radius: 5px; + color: white; + font-size:13px; +}
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/header/header.tpl.html b/ecomp-portal-FE/client/app/views/header/header.tpl.html new file mode 100644 index 00000000..1928cb64 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/header/header.tpl.html @@ -0,0 +1,277 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<style type="text/css"> + + .att-popover .pop-over-caret.pop-over-caret--below{ + left: 5%; + color:#fff; + } + .att-popover .pop-over-caret.pop-over-caret-border--below{ + left: 5%; + } + + .popover-demo.att-popover{ + float: left; + top: 44px; + } + .reg-profileDetails{ + outline:none !important; + } + #reg-header-snippet .reg-profileDetails { + padding: 0px; + min-height: 150px; + } + #reg-header-snippet .reg-profileDetails .reg-userEmail-label .reg-userEmail-label-spn { + color: #0574ac; font-size: 13px; font-weight: bold; + } + #reg-header-snippet .reg-profileDetails .reg-userRole-label .reg-userRole-label-spn { + color: #0574ac;font-size: 13px; font-weight: bold; + } + +</style> +<div class="header-section"> + <div class="headerContainer" id="headerContainer"> + <div id="megaMenuContainer" class="megaMenuContainer"> + <div> +<!--for mega Menu--> +<!-- Mega Menu parent-tab directive with three models menu-items, active-sub-menu, active-menu --> + <div id="topMenu" class="top-megamenu" ng-mouseleave="activeClickSubMenu.x.active=false; activeClickMenu.x.active=false"> + <div class="megaMenuDataObject" parent-tab menu-items="megaMenuDataObject" + active-sub-menu='activeClickSubMenu.x' active-menu='activeClickMenu.x'> + <div parentmenu-tabs mega-menu="true" menu-items="megaMenuDataObject" class="parentmenu-tabs"> + <div class="menu-section"> +<!-- TITLE --> + <li class="megamenu__item" style="line-height:55px;" onclick="window.location = 'applicationsHome'"> + <span id="logo-image" class="logo-image"></span> + <span id="portal-title" class="portal-title" >OpenECOMP Portal</span> + </li> +<!-- MENUS --> <!-- MENUS --> + <div id="menu-tabs-mega-menu" + menu-tabs mega-menu="true" + tab-name="item.text" + menu-item="item" + ng-mousedown="loadFavorites()" + active-menu="activeClickMenu.x" + ng-repeat="item in megaMenuDataObject" style="font-size: 18px;"> + <div id="parentmenu-tabs" parentmenu-tabs sub-menu="true" + ng-show="activeClickMenu.x.active && item.active" + menu-items="activeClickMenu.x.children"> + +<!-- Second level menu --> <!-- Second level menu --> + <div id="second-level-menus" menu-tabs sub-menu="true" + menu-item="subItem" tab-name="subItem.text" tab-url="subItem.url" + ng-repeat="subItem in activeClickMenu.x.children | orderBy : 'column'" + active-menu="activeClickSubMenu.x" + sub-item-active="{{subItem.active}}" style="float:left;" + aria-label="{{subItem.text}} | orderBy : 'column'" + ng-mousemove="submenuLevelAction(subItem.text,subItem.column)" + ng-if="item.text!='Help' && item.text!='Support'" + > + <i ng-if="subItem.text=='Favorites'" id="favorite-star" data-size="large" + class="ion-star favorites-icon-active"> + </i> + </div> + <div id="second-level-menus-help" menu-tabs sub-menu="true" + menu-item="subItem" + ng-repeat="subItem in activeClickMenu.x.children | orderBy : 'column'" + ng-if="item.text=='Help' || item.text=='Support'" + > + <span ng-click="goToUrl(subItem)">{{subItem.text}}</span> + </div> + + + <div class="sub__menu" > + <ul ng-show="activeClickSubMenu.x.active" role="menubar" class="header-columns"> +<!-- Third level menu --> <!-- Third level menu --> + <div id="third-level-menus" class="header-columns-div" + ng-hide='favoritesWindow || hideMenus' + menu-tabs menu-item="subItem" + ng-repeat="subItem in activeClickSubMenu.x.children | orderBy : 'column'" + ng-show="activeClickSubMenu.x.active" > + <i id="level3-star-inactive-{{subItem.menuId}}" ng-cloak + class="ion-star level3-favorites-icon-inactive" data-size="large" + data-ng-click="setAsFavoriteItem($event, subItem.menuId)" + ng-if="subItem.url.length > 1 && isUrlFavorite(subItem.menuId)==false"> + </i> + <i id="level3-star-active-{{subItem.menuId}}" ng-cloak + ng-if="subItem.url.length > 1 && isUrlFavorite(subItem.menuId)" + class="ion-star favorites-icon-active ng-cloak" data-size="large" + data-ng-click="removeAsFavoriteItem($event, subItem.menuId)" + > + </i> + <span class="title" + aria-label="{{subItem.text | elipsis: 50}}" + ng-click="goToUrl(subItem)">{{subItem.text}}</span> +<!-- Fourth level menus - <!-- Fourth level menus --> + <div class ="header-level4-div" att-links-list="" ng-repeat="tabValue in subItem.children" > + <i id="level4-star-inactive-{{tabValue.menuId}}" ng-cloak + class="ion-star level4-favorites-icon-inactive" + data-ng-click="setAsFavoriteItem($event, tabValue.menuId)" + ng-if="tabValue.url.length > 1 && isUrlFavorite(tabValue.menuId)==false"> + </i> + <i id="level4-star-active-{{tabValue.menuId}}" ng-cloak + class="ion-star favorites-icon-active" + data-ng-click="removeAsFavoriteItem($event, tabValue.menuId)" + ng-if="tabValue.url.length > 1 && isUrlFavorite(tabValue.menuId)"> + + </i> + + <span ng-click="goToUrl(tabValue)" + role="menuitem" att-links-list-item="" + style="display: inline; padding-left: 8px" + att-accessibility-click="13,32" + title="{{tabValue.text}}" + ng-class="{'disabled': tabValue.disabled}">{{tabValue.text | elipsis: 50}}</span> + </div> + <div att-divider-lines ng-show="!$last"></div> + </div> + </ul> +<!-- Favorites level menu --> <!-- Favorites level menu --> + <div class="header-columns-div" ng-show='favoritesWindow' ng-mouseleave="hideFavoritesWindow()"> + <div id="favorites-menu-items" att-links-list="" + ng-show="showFavorites" + ng-hide="hideMenus" + ng-repeat="subItem in favoritesMenuItems" + att-links-list="" > + + <div class="fav-links"> + <i id="favorite-selector-favorites-list" + class="ion-star favorites-icon-active" + style="font-size:18px" + data-ng-click="removeAsFavoriteItem($event, subItem.menuId)" + ng-mousedown="removeAsFavoriteItem($event, subItem.menuId)"> + </i> + <a id="favorites-list" aria-label="{{subItem.text}}" style="color: #000" + ng-click="goToUrl(subItem)">{{subItem.text}} + </a> + </div> + </div> + <!-- Favorites when empty --> + <div id="favorites-empty" + ng-show="emptyFavorites" + class="favorites-window-empty" > + <div> + <span id="fav-icon" class="ion-ios-star" ></span> + <p id="p-no-favs" class="largeText">No Favorites</p> + <p id="p-no-favs-desc" class="normal">Add your favorite items for quick access.</p> + </div> + </div> + </div> + </div> + + </div> + </div > + </div> + + <div class="login-section"> + <li id="bcLoginSnippet" class="megamenu__item" style="width: 140px;" > + <div popover="loginSnippet.html" aria-label="Login Snippet" referby="loginSnippet" att-accessibility-click="13,32" popover-style="" popover-trigger="click" popover-placement="below" style="width: 150px; height: 55px; display: inline-flex"> + <div id="icon-user-small" class="ion-android-person login-snippet-icon"></div> + <div id="login-snippet-text" class="login-snippet-text">{{header.firstName}}</div> + </div> + </li> + </div> + </div> + </div> + <div style="clear: both"></div> + </div> + </div> + </div> + </div> +</div> +<script type="text/ng-template" id="loginSnippet.html"> + <div ng-controller="loginSnippetCtrl" > + <div id="reg-header-snippet"> + <div tabindex="0" class="reg-profileDetails" id="reg-profiledetails-id"> + <ul class="reg-Details-table" style="list-style: none; border-bottom: 1px solid #bbb; padding-bottom: 20px;" > + <li> + <div class="reg-userName-table"> + <div id="reg-userName-table-row"> + <div id="reg-userName-table-cell"> + <h3 class="att-global-fonts" style="font-size:18px; line-height:1.6 !important;" id="reg-userName" > + {{firstName}} {{lastName}} </h3> + <span> </span> + </div> + </div> + </div> + </li> + <li><div class="reg-userEmail-label"><span class="reg-userEmail-label-spn">Email<span class="visuallyhidden">: + </span></span></div></li> + <li><div class="reg-userEmail-value"><span class="reg-userEmail-value-spn"> + {{loginSnippetEmail}}</span></div></li> + <li> </li> + <li><div class="reg-userRole-label"><span class="reg-userRole-label-spn"> + User ID<span class="visuallyhidden">:</span></span></div></li> + <li><div class="reg-userRole-value"><span class="reg-userRole-value-spn"> + {{loginSnippetUserId}}<span class="visuallyhidden"></span></span></div></li> + + </ul> + <div id="reg-logout-div" style="padding-top: 8px;"> + <p>Last login: <span ng-bind="lastLogin"></span></p> + <div style="margin-top: 10px"> + <a href="javascript:void(0)" id="allLogout" ng-click="openEditUserModal()" style="" class="new-button"> + Profile + </a> + <a href="javascript:void(0)" id="allLogout" ng-click="allAppsLogout()" class="new-button"> + Log out + </a> + </div> + </div> + </div> + </div> + </div> + +</script> +<script type="text/ng-template" id="notificationCtrl.html"> + <div style="line-height: normal; right: 167px; min-height: 122px; height: auto; width: auto; " ng-controller="notificationCtrl" > + <div class="ng-scope"> + <div id="notification" class="notificationBox "> + <div class="notification-header"> + <div style="float:left;"> + <p class="notification-heading">Notifications</p> + </div> + <!-- <div class="notification-close ng-isolate-scope" tabindex="0" ddh-accessibility-click="13,32" b2b-close-flyout=""> + <span class="icon-circle-action-close" aria-label="close button"></span> + </div>--> + <div style="clear:both;"></div> + </div> + <div class="notification-main"> + <div style="height:113px;"> + <div align="center" class="icon-information notification-info-icon"></div> + </div> + <div> + <p class="notification-text">No New Notifications.</p> + </div> + </div> + <div class="notification-main ng-hide" ng-show="showList"> + <!-- ngRepeat: notification in notifications track by $index --> + </div> + <div class="notification-footer"> + <div class="notification-links"> + <div style="clear:both;"></div> + </div> + </div> + <!--/div --> + + + </div> + </div> + </div> +</script> diff --git a/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.controller.js b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.controller.js new file mode 100644 index 00000000..507b3a0e --- /dev/null +++ b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.controller.js @@ -0,0 +1,134 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +(function () { + class EditUserController { + constructor($scope,$log, message, $q, $http, conf,contactUsService,confirmBoxService,userProfileService,adminsService) { + + $scope.newUser ={ + firstName:'', + lastName:'', + emailAddress:'', + middleName:'', + loginId:'', + loginPwd:'', + }; + + $scope.userId = $scope.ngDialogData.loginId; + $scope.updateRemoteApp = $scope.ngDialogData.updateRemoteApp; + try { + userProfileService.getCurrentUserProfile($scope.userId) + .then(res=> { + $scope.newUser.firstName = res.firstName; + $scope.newUser.lastName = res.lastName; + $scope.newUser.emailAddress = res.email; + $scope.newUser.middleName = res.middleInitial; + $scope.newUser.loginId = res.loginId; + $scope.newUser.loginPwd = res.loginPwd; + $scope.newUser.loginPwdCheck = res.loginPwd; + + }).catch(err=> { + $log.error('HeaderCtrl::LoginSnippetCtrl:get Current User Profile error: ' + err); + }); + } catch (err) { + $log.error('HeaderCtrl::LoginSnippetCtrl:getFunctionalMenuStaticInfo failed: ' + err); + } + + $scope.changePwdText = function(){ + $scope.newUser.loginPwdCheck = ''; + } + + $scope.closeDialog = function(){ + $scope.closeThisDialog( $scope.widgetData); + } + + $scope.updateUserFun = function(){ + + if ($scope.newUser.firstName==''||$scope.newUser.lastName==''||$scope.newUser.loginPwd=='') { + var warningMsg = "Please enter missing values"; + confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;}); + return; + } else if ($scope.newUser.loginPwd != $scope.newUser.loginPwdCheck) { + var warningMsg = "Passwords do not match, please try again."; + confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;}); + return; + } else { + + // check password length complexity. + var warningMsg = adminsService.isComplexPassword($scope.newUser.loginPwd); + if (warningMsg != null) { + confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;}); + return; + } + + adminsService.addNewUser($scope.newUser,'No').then(res=> { + + $scope.closeThisDialog(); + + confirmBoxService.showInformation('Update User Info successfully'); + userProfileService.broadCastUpdatedUserInfo(); + + + if($scope.updateRemoteApp){ + //need update remote app's database + var remoteAppId = $scope.ngDialogData.appId; + if(remoteAppId!=null){ + + userProfileService.updateRemoteUserProfile($scope.userId,remoteAppId).then(res=> { + + + }).catch(err=> { + $log.error('userProfileService: update user profile in remote app error:: ', err); + confirmBoxService.showInformation('Update User in remote app failed: ' + err); + + }).finally(() => { + + }); + + + } + + } + + }).catch(err=> { + $log.error('adminsService: addNewUser error:: ', err); + // $scope.errMsg=err; + confirmBoxService.showInformation('Add New User failed: ' + err); + + }).finally(() => { + //this.isLoadingTable = false; + + }); + + } + } + + + } + } + EditUserController.$inject = ['$scope','$log', 'message', '$q', '$http', 'conf','contactUsService','confirmBoxService','userProfileService','adminsService']; + angular.module('ecompApp').controller('editUserController', EditUserController); + + +})(); +function validateUrl(value){ + return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value); + }
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.less b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.less new file mode 100644 index 00000000..853e48db --- /dev/null +++ b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.less @@ -0,0 +1,61 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + + .main{ + padding:10px; + margin-left: 40px; + + } + .manage-user-home-title{ + + margin-top:15px; + color: #067ab4; + font-family: arial; + font-size: 24px; + margin-left: 0px; + width: 250px; + + } + + .input-text-edit-user { + font-style: italic; + padding: 7px 10px; + width: 200px !important; + display: inline-block; + position: relative; + margin-bottom: 10px; + border-radius: 6px; + border: 1px solid #d8d8d8; + height: 32px; + border-color: slategrey !important; + } + + .input-edit-user-div{ + float: left; + width: 300px; + } + + .edit-user-section{ + + margin-top:5px; + margin-left:10px; + + } +
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.tpl.html b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.tpl.html new file mode 100644 index 00000000..719f8bde --- /dev/null +++ b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.tpl.html @@ -0,0 +1,69 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="main" style="margin-top: 5px; height: 350px;"> + <div id="title" class="manage-user-home-title">Profile</div> + + <div class="edit-user-section" > + <div class="input-edit-user-div" > + <div class="">*First Name</div> + <input class="input-text-edit-user" type="text" ng-model="newUser.firstName"/> + </div> + <div class="input-edit-user-div" > + <div class="">Middle Name</div> + <input class="input-text-edit-user" type="text" ng-model="newUser.middleName"/> + </div> + <div class="input-edit-user-div" > + <div class="">*Last Name</div> + <input class="input-text-edit-user" type="text" ng-model="newUser.lastName"/> + </div> + <div class="input-edit-user-div" > + <div class="">*Email Address ID</div> + <input class="input-text-edit-user" type="text" disabled ng-model="newUser.emailAddress"/> + </div> + <div class="input-edit-user-div" > + <div class="">*Login ID</div> + <input class="input-text-edit-user" type="text" disabled ng-model="newUser.loginId"/> + </div> + <div class="input-edit-user-div" > + <div class="">*Login Password</div> + <input class="input-text-edit-user" type="password" ng-model="newUser.loginPwd" ng-change="changePwdText()"/> + </div> + <div class="input-edit-user-div" > + <div class="">*Confirm Login Password</div> + <input class="input-text-edit-user" type="password" ng-model="newUser.loginPwdCheck"/> + </div> + </div> + <div style="bottom: 2px; height:50px;width:180px;margin-top:60px"> + <div class="dialog-control"> + <div id="div-updateAdminAppsRoles" class="next-button" ng-click="updateUserFun()" + ng-class="{disabled: false}">Save + </div> + <div id="div-cancel-button" class="cancel-button" ng-click="closeThisDialog()">Cancel</div> + </div> + </div> + + +</div> + +<script type="application/javascript"> + $(document).ready(function(){ + $(".ngdialog-content").css("width","85%") + }); +</script>
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.js b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.js new file mode 100644 index 00000000..2071f4cc --- /dev/null +++ b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.js @@ -0,0 +1,244 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + const HTTP_PROTOCOL_RGX = /https?:\/\//; + class ApplicationsHomeCtrl { + constructor(applicationsService, $log, $window, userProfileService, $scope,$cookies, utilsService) { + //activate spinner + this.isLoading = true; + $scope.getUserAppsIsDone = false; + this.userProfileService = userProfileService; + //get all user's applications on init + $scope.buildNumber = "OpenECOMP Portal Version: 1.0.0" + + userProfileService.getUserProfile() + .then(profile=> { + $log.info('ApplicationsHomeCtrl::getUserProfile', profile); + $scope.userId = profile.orgUserId; + //$scope.appsViewData = ['notempty']; // initialize with length != 0 + $scope.appsViewData = []; + $scope.appsView = []; + + //redirect to login.htm, if the EPService cookie is missing and logged in user is not a guest. + if (!$cookies.get('EPService') && !profile.guestSession) { + this.isLoading = false; + var myHostName; + myHostName = location.host; + $log.info("EPService cookie is missing, so attempting to redirecting to login page."); + if (utilsService.isRunningInLocalDevEnv()) { + $log.info("Portal is running in local development and redirecting to 'http://localhost:8080/ecompportal/login.htm'."); + $window.location.href = "http://localhost:8080/ecompportal/login.htm"; + } else { + $log.info("Redirecting to 'login.htm'."); + $window.location.href = "login.htm"; + } + } + + applicationsService.getUserApps() + .then(res => { + $log.info('ApplicationsHomeCtrl::getUserApps: ', res); + this.apps = res; + let rowNo = 0; + for (let i = 0; i < this.apps.length; i++) { + $scope.appsView[i] = { + sizeX: 2, + sizeY: 2, + headerText: '', + subHeaderText: '', + imageLink: '', + order: '', + url: '' + }; + $scope.appsView[i].headerText = this.apps[i].name; + $scope.appsView[i].subHeaderText = this.apps[i].notes; + $scope.appsView[i].imageLink = this.apps[i].thumbnail || this.apps[i].imageUrl; + $scope.appsView[i].order = this.apps[i].order; + $scope.appsView[i].url = this.apps[i].url; + $scope.appsView[i].restrictedApp = this.apps[i].restrictedApp; + } + $log.info('ApplicationsHomeCtrl::getUserApps: apps count : ' + $scope.appsView.length); + + if ($cookies.getObject($scope.userId+'_apps') == undefined || $cookies.getObject($scope.userId+'_apps') == null || $cookies.getObject($scope.userId+'_apps').length == 0) { + if (($scope.appsView != undefined) && ($scope.appsView != null) && ($scope.appsView.length > 0)){ + $scope.appsViewData = $scope.appsView; + $cookies.putObject($scope.userId + '_apps', $scope.appsView); + } + } + else{ + this.listChanged = false; + this.appsListFromCookie = $cookies.getObject($scope.userId+'_apps'); + this.finalAppsList = []; + // + // If an app is still valid for this user from backend(appsView) and + // it was in the cookie already, put it in the list in the same order + // it was in within the cookie. + // + let finalCount = 0; + for (let i = 0; i < this.appsListFromCookie.length; i++) { + this.foundAppInListFromBackend = false; + for (let j = 0; j < $scope.appsView.length; j++) { + if (this.appsListFromCookie[i].url == $scope.appsView[j].url) { + this.finalAppsList[finalCount] = { + sizeX: 2, + sizeY: 2, + headerText: '', + subHeaderText: '', + imageLink: '', + order: '', + url: '' + }; + this.finalAppsList[finalCount].headerText = this.appsListFromCookie[i].headerText; + this.finalAppsList[finalCount].subHeaderText = this.appsListFromCookie[i].subHeaderText; + this.finalAppsList[finalCount].imageLink = this.appsListFromCookie[i].imageLink; + this.finalAppsList[finalCount].order = this.appsListFromCookie[i].order; + this.finalAppsList[finalCount].url = this.appsListFromCookie[i].url; + this.finalAppsList[finalCount].restrictedApp = this.appsListFromCookie[i].restrictedApp; + finalCount++; + this.foundAppInListFromBackend = true; + break; + } + } + if (this.foundAppInListFromBackend == false) { + this.listChanged = true; + } + } + + // + // Fill in the rest of the list with the Apps retrieved from the backend that we did not already add. There could have been + // new Apps configured for the user that are not in the cookie. + // + for (let i = 0; i < $scope.appsView.length; i++) { + this.found = false; + for (let j = 0; j < this.finalAppsList.length; j++) { + if ($scope.appsView[i].url == this.finalAppsList[j].url) { + // already present + this.found = true; + break; + } + } + if (this.found == false) { + this.finalAppsList[finalCount] = { + sizeX: 2, + sizeY: 2, + headerText: '', + subHeaderText: '', + imageLink: '', + order: '', + url: '' + }; + this.finalAppsList[finalCount].headerText = $scope.appsView[i].headerText; + this.finalAppsList[finalCount].subHeaderText = $scope.appsView[i].subHeaderText; + this.finalAppsList[finalCount].imageLink = $scope.appsView[i].imageLink; + this.finalAppsList[finalCount].order = $scope.appsView[i].order; + this.finalAppsList[finalCount].url = $scope.appsView[i].url; + this.finalAppsList[finalCount].restrictedApp = $scope.appsView[i].restrictedApp; + finalCount++; + this.listChanged = true; + } + } + + if ((this.finalAppsList != undefined) && (this.finalAppsList != null) && (this.finalAppsList.length > 0)) { + if (this.listChanged) { + $scope.appsViewData = this.finalAppsList; + $cookies.putObject($scope.userId + '_apps', this.finalAppsList); + } else { + $scope.appsViewData = $cookies.getObject($scope.userId+'_apps'); + } + } + this.isLoading = false; + $scope.getUserAppsIsDone = true; + } + }).catch(err => { + $log.error('oh no! couldnt get applications list...', err); + this.isLoading = false; + $scope.getUserAppsIsDone = true; + }); + }); + + + this.gridsterOpts = { + columns: 6, + colWidth: 190, + rowHeight: 190, + margins: [20, 20], + outerMargin: true, + pushing: true, + floating: true, + swapping: true, + draggable : { + stop: function () { + $cookies.putObject($scope.userId + '_apps', $scope.appsViewData); + } + } + }; + + //navigate to application url in new tab + this.goToPortal = (item) => { + $log.info("goToPortal called with item: " + item); + let url = item.url; + let restrictedApp = item.restrictedApp; + if (!url) { + $log.info('No url found for this application, doing nothing..'); + return; + } + if (!HTTP_PROTOCOL_RGX.test(url)) { + url = 'http://' + url; + } + if (restrictedApp) { + $window.open(url, '_blank'); + } else { + var tabContent = { id: new Date(), title: item.headerText, url:item.url.split('#')[0] + '?' + (new Date).getTime() + "#" + item.url.split('#')[1]}; + $cookies.putObject('addTab', tabContent ); + } + + }; + // try { + // userProfileService.getUserProfile() + // .then(profile=> { + // if (profile.roles.indexOf('superAdmin') > -1) { + // $scope.buildNumber = "ECOMP Portal Version: 1610.0.2058" + // } + // }).catch(err=> { + // $log.error('Applications Home:: getUserProfile() failed: ' + err); + // }); + // } catch (err) { + // $log.error('Applications Home:: getUserProfile() failed: ' + err); + // } + if(getParameterByName('noUserError')!=null){ + if(getParameterByName('noUserError')=="Show"){ + $("#errorInfo").show(); + } + } + } + } + ApplicationsHomeCtrl.$inject = ['applicationsService', '$log', '$window', 'userProfileService', '$scope','$cookies', 'utilsService']; + angular.module('ecompApp').controller('ApplicationsHomeCtrl', ApplicationsHomeCtrl); +})(); + +function getParameterByName(name, url) { + if (!url) url = window.location.href; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return ''; + if (!results[2]) return ''; + return (results[2].replace(/\+/g, " ")); +} diff --git a/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.spec.js b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.spec.js new file mode 100644 index 00000000..e3782ab2 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.spec.js @@ -0,0 +1,77 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +describe('Controller: ApplicationsHomeCtrl ', function() { + beforeEach(module('ecompApp')); + + let ApplicationsHomeCtrl, $controller, $q, rootScope, $log, $window, $cookies, scope; + let deferredApps, deferredUserProfile, applicationsServiceMock, userProfileServiceMock; + + + beforeEach(inject( (_$controller_, _$q_, _$rootScope_, _$log_, _$window_, _$cookies_, _CacheFactory_)=>{ + rootScope = _$rootScope_; + scope = rootScope.$new(); + $q = _$q_; + $controller = _$controller_; + $log = _$log_; + $window = _$window_; + $cookies = _$cookies_; + + _CacheFactory_.destroyAll(); + + deferredApps = $q.defer(); + deferredUserProfile = $q.defer(); + applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock', ['getUserApps']); + applicationsServiceMock.getUserApps.and.returnValue(deferredApps.promise); + + userProfileServiceMock = jasmine.createSpyObj('userProfileServiceMock',['getUserProfile']); + userProfileServiceMock.getUserProfile.and.returnValue(deferredUserProfile.promise); + + ApplicationsHomeCtrl = $controller('ApplicationsHomeCtrl', { + applicationsService: applicationsServiceMock, + $log: $log, + $window: $window, + userProfileService: userProfileServiceMock, + $scope: scope, + $cookies: $cookies + }); + scope.$digest(); + })); + + it('should populate this.apps with data from portals service getUserApps', inject(function ( _$q_) { + $q = _$q_; + + let profile = {roles: 'superAdmin', userId: 'userid'}; + + deferredUserProfile.resolve(profile) + deferredApps.resolve([{name: 'portal1'},{name: 'portal2'},{name: 'portal3'}]); + scope.$digest(); + expect(scope.appsViewData.length).toBe(3); + })); + + it('should call $log error when getAllPortals fail', inject(function ( _$q_) { + $q = _$q_; + spyOn($log, 'error'); + deferredUserProfile.reject('something happened!'); + scope.$digest(); + expect($log.error).toHaveBeenCalled(); + })); + +}); diff --git a/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.less b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.less new file mode 100644 index 00000000..d91acc17 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.less @@ -0,0 +1,164 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-applications-home { + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + .go-button { + .btn-green; + width: 96px; + position: absolute; + border-radius: 0px; + } + + .applications-home-container { + .content_justify; + position: relative; + padding: 15px 0 32px 0; + width: 100%; + + .applications-home-title { + .a24r; + margin: auto; + .content_justify; + } + .portals-list { + min-height: 70vh; + justify-content: center; + flex-flow: row wrap; + width: 1170px; + + margin: auto; + margin-bottom: 63px; + + .app-gridster-header { + background-color: @u; + } + + .app-gridster-footer { + background-color: @u; + } + + .portals-list-item { + background-color: @u; + border-radius: 2px; + box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1); + display: inline-block; + width: 360px; + height: 300px; + background-size: cover; + cursor: pointer; + margin: 15px; + overflow: hidden; + + .portals-item-info { + background-color: @u; + height: 120px; + top: 180px; + position: relative; + box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1); + padding: 16px; + + .info-title { + .a24r; + margin-bottom: 4px; + + text-overflow: ellipsis; + overflow: hidden; + } + .info-description { + .c16r; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .info-button { + .btn-green; + width: 96px; + position: absolute; + bottom: 16px; + left: 16px; + } + + &:hover { + opacity: .93; + z-index: 3; + } + } + } + } + } +} +.w-ecomp-main-error{ + .o14r; + position: absolute; + width: 100%; + line-height: 1.5em; +} +.w-ecomp-main-disclaimer { + text-align: center; + .o14r; + //position: absolute; + bottom: -75px; + line-height: 1.5em; + margin: 0 auto; + width:1170px; + position: relative; + +} +.build-number { + .o12i; +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.slide.ng-hide-add, +.slide.ng-hide-remove, +.slide.ng-enter, +.slide.ng-leave { + transition: all 0.5s ease; +} +.slide.ng-hide, +.slide.ng-enter { + transform: translate(-100%, 0); +} +.slide.ng-enter-active { + transform: translate(0, 0); +} +.slide.ng-leave { + transform: translate(0, 0); +} +.slide.ng-leave-active { + transform: translate(+100%, 0); +} diff --git a/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.tpl.html b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.tpl.html new file mode 100644 index 00000000..645807f6 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.tpl.html @@ -0,0 +1,59 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-applications-home" ng-style="{bottom: tabBottom}"> + <div class="applications-home-container"> + <div id="app-home-title" class="applications-home-title" >Applications</div> + <div align="center" id="errorInfo" style="display:none;font-size:12px;margin-left:5px"> + <span style="color:red">You don't have user account in that application.Please ask system administrator </span></div> + <div class="portals-list"> + <div class="gridster-container override_background"> + <span class="ecomp-spinner" ng-show="applicationsHome.isLoading" ></span> + <div att-gridster att-gridster-options="applicationsHome.gridsterOpts" > + <div att-gridster-item='item' ng-repeat="item in appsViewData" id="app-{{item.headerText.split(' ').join('-')}}" > + <div id="grips-{{item.headerText.split(' ').join('-')}}" ng-hide="users.isLoadingTable && !users.getUserAppsIsDone" + att-gridster-item-header grips-img-path="assets/images/grips.png" + header-text="{{item.headerText | elipsis: 33}}" sub-header-text="{{item.subHeaderText | elipsis: 56}}" class="app-gridster-header"> + </div> + <div id="app-image-{{item.headerText.split(' ').join('-')}}"att-gridster-item-body + ng-style="{'cursor':'pointer', + 'background-image': 'url('+(item.imageLink)+')', + 'order': item.order, + 'background-repeat': 'no-repeat'}" + ng-click="applicationsHome.goToPortal(item)" ng-hide="users.isLoadingTable && !users.getUserAppsIsDone"> + </div> + <div att-gridster-item-footer class="app-gridster-footer" ng-hide="users.isLoadingTable && !users.getUserAppsIsDone"> + <span id="go-{{item.headerText.split(' ').join('-')}}" class="go-button" ng-click="applicationsHome.goToPortal(item)" >GO</span> + </div> + </div> + </div> + </div> + <div> + <div id="app-home-disclaimer" class="w-ecomp-main-error" ng-if="appsViewData.length==0" ng-hide="users.isLoadingTable && !users.getUserAppsIsDone"> + You do not have access to any application/functionality within OpenECOMP Portal. Please contact your administrator to get access to ECOMP Portal</a><br> + </div> + </div> + </div> + <div id="app-home-disclaimer-mylogins" class="w-ecomp-main-disclaimer"> + + <p id="build-number" class="build-number" ng-bind-html="buildNumber"></p> + </div> + + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.js b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.js new file mode 100644 index 00000000..3f624117 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.js @@ -0,0 +1,284 @@ +'use strict'; +(function () { + + class WidgetsHomeCtrl { + constructor(widgetsService, $log, $cookies, $scope, userProfileService,dashboardService) { + this.userProfileService = userProfileService; + this.isLoading = true; + + userProfileService.getUserProfile() + .then(profile=> { + $log.info('WidgetsHomeCtrl::getUserProfile: ', profile); + $scope.attuid = profile.attuid; + $log.info('WidgetsHomeCtrl::getUserProfile: user has the following attuid: ' + profile.attuid); + $scope.widgetsViewData = []; + $scope.widgetsView = []; + + widgetsService.getUserWidgets() + .then(widgets => { + $log.info('WidgetsHomeCtrl::getUserWidgets', widgets); + this.widgets = widgets; + this.enlargeURL = ""; + this.isEnlarged = false; + let rowNo = 0; + for (let i = 0; i < this.widgets.length; i++) { + $scope.widgetsView[i] = { + sizeX: 2, + sizeY: 2, + headerText: '', + width: '', + height: '', + url: '', + selected:false + }; + $scope.widgetsView[i].headerText = this.widgets[i].name; + $scope.widgetsView[i].url = this.widgets[i].url; + //$scope.widgetsView[i].width = this.widgets[i].width; + //$scope.widgetsView[i].height = this.widgets[i].height; + //$scope.widgetsView[i].sizeX = this.widgets[i].width/180; + //$scope.widgetsView[i].sizeY = this.widgets[i].height/150; + } + /*News Events Resources*/ + $scope.widgetsView[this.widgets.length] = { + sizeX: 2, + sizeY: 2, + headerText: 'News', + width: '', + height: '', + url: '', + selected:true + }; + $scope.widgetsView[this.widgets.length+1] = { + sizeX: 2, + sizeY: 2, + headerText: 'Events', + width: '', + height: '', + url: '', + selected:true + }; + $scope.widgetsView[this.widgets.length+2] = { + sizeX: 2, + sizeY: 2, + headerText: 'Resources', + width: '', + height: '', + url: '', + selected:true + }; + + /*Setting News data*/ + $scope.newsData = []; + $scope.updateNews = function() { + $scope.newsData.length=0; + dashboardService.getCommonWidgetData('NEWS').then(function(res) { + // $log.info(res.message); + var newsDataJsonArray = res.response.items; + for (var i = 0; i < newsDataJsonArray.length; i++) { + $scope.newsData.push(newsDataJsonArray[i]); + } + })['catch'](function(err) { + $log.error('dashboard controller: failed to get news list', err); + _this.isLoading = false; + }); + } + $scope.updateNews(); + /*Setting Events data*/ + + $scope.eventData = []; + + $scope.updateEvents = function() { + $scope.eventData.length=0; + dashboardService.getCommonWidgetData('EVENTS').then(function(res) { + var eventDataJsonArray = res.response.items; + for (var i = 0; i < eventDataJsonArray.length; i++) { + if(eventDataJsonArray[i].eventDate !=null) { + // yyyy-mm-dd + eventDataJsonArray[i].year = eventDataJsonArray[i].eventDate.substring(2,4); + eventDataJsonArray[i].mon = eventDataJsonArray[i].eventDate.substring(5,7); + eventDataJsonArray[i].day = eventDataJsonArray[i].eventDate.substring(8,10); + } + $scope.eventData.push(eventDataJsonArray[i]); + } + })['catch'](function(err) { + $log.error('dashboard controller: failed to get Events list', err); + _this.isLoading = false; + }); + } + $scope.updateEvents(); + /*Setting Important Resources data*/ + + $scope.importResData = []; + $scope.updateImportRes = function() { + $scope.importResData.length=0; + dashboardService.getCommonWidgetData('IMPORTANTRESOURCES').then( + function(res) { + // $log.error('couldnt get important list...', res.response.dataBlob); + $log.info(res); + var importResDataJSONArray = res.response.items; + for (var i = 0; i < importResDataJSONArray.length; i++) { + $scope.importResData.push(importResDataJSONArray[i]); + } + })['catch'](function(err) { + $log.error('dashboard controller: failed to get resources list...', err); + _this.isLoading = false; + }); + } + $scope.updateImportRes(); + + /********End hardcoded news events and resources****************/ + + + $log.info('WidgetsHomeCtrl::getUserWidgets count : ' + $scope.widgetsView.length); + + if ($cookies.getObject($scope.attuid + '_widget') == undefined || $cookies.getObject($scope.attuid + '_widget') == null || $cookies.getObject($scope.attuid + '_widget').length == 0) { + if (($scope.widgetsView != undefined) && ($scope.widgetsView != null) && ($scope.widgetsView.length > 0)) { + $scope.widgetsViewData = $scope.widgetsView; + //$cookies.putObject($scope.attuid + '_widget', $scope.widgetsView); + } + } + else { + this.listChanged = false; + this.listFromCookie = $cookies.getObject($scope.attuid + '_widget'); + this.finalList = []; + // + // If a widget is still valid for this user from backend and + // it was in the cookie already, put it in the list in the same order + // it was in within the cookie. + // + let finalCount = 0; + for (let i = 0; i < this.listFromCookie.length; i++) { + this.foundInListFromBackend = false; + for (let j = 0; j < $scope.widgetsView.length; j++) { + if ((this.listFromCookie[i].url == $scope.widgetsView[j].url) && + (this.listFromCookie[i].headerText == $scope.widgetsView[j].headerText)) { + this.finalList[finalCount] = { + sizeX: 2, + sizeY: 2, + headerText: '', + width: '', + height: '', + url: '', + selected:false + }; + this.finalList[finalCount].headerText = this.listFromCookie[i].headerText; + //this.finalList[finalCount].width = this.listFromCookie[i].width; + //this.finalList[finalCount].height = this.listFromCookie[i].height; + this.finalList[finalCount].url = this.listFromCookie[i].url; + //this.finalList[finalCount].sizeX = this.listFromCookie[i].width/180; + //this.finalList[finalCount].sizeY = this.listFromCookie[i].height/150; + finalCount++; + this.foundInListFromBackend = true; + break; + } + } + if (this.foundInListFromBackend == false) { + this.listChanged = true; + } + } + + // + // Fill in the rest of the list with the widgets retrieved from the backend that we did + // not already add. There could have been + // new widgets configured for the user that are not in the cookie. + // + for (let i = 0; i < $scope.widgetsView.length; i++) { + this.found = false; + for (let j = 0; j < this.finalList.length; j++) { + if (($scope.widgetsView[i].url == this.finalList[j].url) && + ($scope.widgetsView[i].headerText == this.finalList[j].headerText)){ + // already present + this.found = true; + break; + } + } + if (this.found == false) { + this.finalList[finalCount] = { + sizeX: 2, + sizeY: 2, + headerText: '', + width: '', + height: '', + url: '', + selected:false + }; + this.finalList[finalCount].headerText = $scope.widgetsView[i].headerText; + //this.finalList[finalCount].width = $scope.widgetsView[i].width; + //this.finalList[finalCount].height = $scope.widgetsView[i].height; + this.finalList[finalCount].url = $scope.widgetsView[i].url; + //this.finalList[finalCount].sizeX = $scope.widgetsView[i].width/180; + //this.finalList[finalCount].sizeY = $scope.widgetsView[i].height/150; + finalCount++; + this.listChanged = true; + } + } + + if ((this.finalList != undefined) && (this.finalList != null) && (this.finalList.length > 0)) { + if (this.listChanged) { + $scope.widgetsViewData = this.finalList; + $cookies.putObject($scope.attuid + '_widget', this.finalList); + } else { + $scope.widgetsViewData = $cookies.getObject($scope.attuid + '_widget'); + } + } + this.isLoading = false; + } + }).catch(err => { + $log.error('WidgetsHomeCtrl::getUserWidgets: oh no! couldnt get widgets list...', err); + this.isLoading = false; + }); + }); + + + this.gridsterOpts = { + columns: 6, + colWidth: 190, + rowHeight: 190, + margins: [20, 20], + outerMargin: true, + pushing: true, + floating: true, + swapping: true, + draggable: { + handle: 'img', + stop: function () { + $cookies.putObject($scope.attuid + '_widget', $scope.widgetsViewData); + } + } + }; + } + } + + WidgetsHomeCtrl.$inject = ['widgetsService', '$log', '$cookies', '$scope', 'userProfileService','dashboardService']; + angular.module('ecompApp').controller('WidgetsHomeCtrl', WidgetsHomeCtrl); + + angular.module('ecompApp').constant('refreshInterval', '30000'); + + angular.module('ecompApp').directive('refreshIframe', ['$interval', 'refreshInterval', function ($interval, refreshInterval) { + + function link(scope, element, attrs) { + var timeoutId; + + function updateIframe() { + if(attrs.isEnlarged == "false") + { + element.attr('src', element.attr('src')); + } + } + + element.on('$destroy', function () { + $interval.cancel(timeoutId); + }); + + // start the UI update process; save the timeoutId for cancelling + /*timeoutId = $interval(function () { + updateIframe(); // update DOM + }, refreshInterval);*/ + } + + return { + link: link + }; + } ]); + +})(); diff --git a/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.spec.js b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.spec.js new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.spec.js @@ -0,0 +1 @@ + diff --git a/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.less b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.less new file mode 100644 index 00000000..46e5b1fd --- /dev/null +++ b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.less @@ -0,0 +1,137 @@ +.w-ecomp-widgets-home{ + //.bg_u;//white for 1702 + .bg_w; // gray for 1610 + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + .widget-gridster-header { + background-color:#ffffff; + } + + .widget-gridster-footer { + background-color:#ffffff + } + + .widget-iframe { + width: 100%; + height: 90%; + } + + .widget-image { + cursor: pointer; + width: 20px; + height: 25px; + } + + .enlarge-content{ + top: 120px; + left: 400px; + width:1170px; + height:600px; + right: 0; + bottom: 0; + left: 0; + background: none repeat scroll 0 0 #f2f2f2; + } + + .enlarge-close-button{ + .btn-blue; + position: relative; + top : -20px; + left : 1145px; + width : 25px; + } + + .widgets-home-container { + .content_justify; + position: relative; + padding-right: 0; + padding-left: 0; + padding-bottom: 32px; + width: 100%; + + .widgets-home-title { + .a24r; + margin: auto; + .content_justify; + } + .portals-list { + min-height: 70vh; + //display: flex; + justify-content: center; + flex-flow: row wrap; + width: 1170px; + + margin: auto; + margin-bottom: 63px; + + .portals-list-item { + background-color: @u; + border-radius: 2px; + box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1); + display: inline-block; + width: 360px; + height: 300px; + background-size: cover; + cursor: pointer; + margin: 15px; + overflow: hidden; + + .portals-item-info { + background-color: @u; + height: 50px; + top: 180px; + // position: relative; + box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1); + padding: 16px; + + .info-title { + .a24r; + margin-bottom: 4px; + + text-overflow: ellipsis; + overflow: hidden; + } + .info-description { + .c16r; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .info-button { + .btn-green; + width: 96px; + // position: absolute; + bottom: 16px; + left: 16px; + } + + &:hover { + opacity: .93; + z-index: 3; + } + } + } + } + } +} + +.gridster-item-container .gridster-item-body{ + bottom:0px; + overflow:hidden; +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} diff --git a/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.tpl.html b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.tpl.html new file mode 100644 index 00000000..8e385c1e --- /dev/null +++ b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.tpl.html @@ -0,0 +1,123 @@ +<div class="w-ecomp-widgets-home" id="contentId"> + <div class="widgets-home-container"> + <div id="widgets-home-title" class="widgets-home-title">Widgets</div> + + + <div class="portals-list"> + <div class="gridster-container override_background"> + <span class="ecomp-spinner" ng-show="widgetsHome.isLoading"></span> + <div ng-hide="widgetsHome.isEnlarged" att-gridster + att-gridster-options="widgetsHome.gridsterOpts" > + <div att-gridster-item='item' ng-repeat="item in widgetsViewData"> + <div id="widget-grips-{{item.headerText.split(' ').join('-')}}" + att-gridster-item-header + grips-img-path="assets/images/grips.png" + header-text={{item.headerText}} + class="widget-gridster-header"> + </div> + <div att-gridster-item-body ng-if="item.headerText=='News'"> + <div id="newsContainer" class="information-section-gridsterContent"> + <div class="news"> + <ul id="newsContent" ng-show="newsData.length!=0"> + <li ng-repeat="news in newsData"><a href="{{news.href}}" target="_blank" ng-bind="news.title"></a></li> + </ul> + <div ng-hide="newsData.length!=0"> + <div class="activity-error-container" style="background: rgb(255, 255, 255); overflow: auto!important; width: 100%;"> + <div class="activity-error-block"> + <i class="ion-information-circled full-linear-ion-information-circled" style="margin-left:125px; font-size: 90px"></i> <br> + <div class="activity-error-msg1">There's currently no news available.</div> + </div> + </div> + </div> + </div> + </div> + </div> + <div att-gridster-item-body ng-if="item.headerText=='Events'"> + <div class="information-section-gridsterContent"> + <div class="events"> + <ul ng-show="eventData.length!=0"> + <li ng-repeat="event in eventData"> + <div ng-click="event_content_show=!event_content_show"> + <div class="events-date">{{event.mon}}/{{event.day}}/{{event.year}} </div> + <div> + <div class="event-title-div"> + <p ng-bind="event.title"></p> + </div> + <div> + <span class="ion-ios-arrow-up" ng-if="event_content_show" style="color: #888; font-size: 22px;"></span> + <span class="ion-ios-arrow-down" ng-if="!event_content_show" style="color: #888; font-size: 22px;"></span> + + </div> + <div style="clear: both;"></div> + </div> + </div> + <div class="events-content" ng-show="event_content_show"> + <div class="events-content-body"> + <a class="events-link" ng-href="{{event.href}}" target="_blank"> + <span ng-bind="event.content"></span> + </a> + </div> + <div></div> + </div> + </li> + + + </ul> + <div ng-hide="eventData.length!=0"> + <div class="activity-error-container" style="background: rgb(255, 255, 255); overflow: hidden!important; width: 100%;"> + <div class="activity-error-block"> + <i class="ion-information-circled full-linear-ion-information-circled" style="margin-left:125px; font-size: 90px"></i> <br> + <div class="activity-error-msg1">There's currently no event available.</div> + </div> + </div> + </div> + </div> + </div> + </div> + + <div att-gridster-item-body ng-if="item.headerText=='Resources'"> + <div class=""> + <div class="resources"> + <ul ng-show="importResData.length!=0"> + <li ng-repeat="item in importResData"><a href="{{item.href}}" target="_blanl">{{item.title}}</a></li> + </ul> + <div ng-hide="importResData.length!=0"> + <div class="activity-error-container" style="background: rgb(255, 255, 255); overflow: hidden!important; width: 100%;"> + <div class="activity-error-block"> + <i class="ion-information-circled full-linear-ion-information-circled" style="margin-left:125px; font-size: 90px"></i> <br> + <div class="activity-error-msg1">There's currently no links available.</div> + </div> + </div> + </div> + </div> + </div> + </div> + + <div att-gridster-item-body ng-if="item.headerText!='News' && item.headerText!='Events' && item.headerText!='Resources'"> + <iframe id="widget-iframe-{{item.headerText.split(' ').join('-')}}" + class="widget-iframe" + ng-src="{{item.url | trusted}}" + refresh-iframe + is-enlarged="{{widgetsHome.isEnlarged}}"> + </iframe> + </div> + <div id="widget-footer-{{item.headerText.split(' ').join('-')}}" + att-gridster-item-footer class="widget-gridster-footer"> + </div> + </div> + </div> + </div> + <div ng-show="widgetsHome.isEnlarged" > + <div id="widgets-button-enlarge-close" class="enlarge-close-button" ng-click="widgetsHome.isEnlarged=false">X</div> + <iframe id="iframe-widget-enlarge-close" ng-src="{{widgetsHome.enlargeURL | trusted}}" class="enlarge-content" > + </iframe> + </div> + + </div> + + <div id="widgets-disclaimer" class="w-ecomp-main-disclaimer"> + To request access to an application, please visit the <a + ng-href="getAccess">Get Access</a> page. + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js new file mode 100644 index 00000000..b5216101 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js @@ -0,0 +1,85 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class NewPortalAdminModalCtrl { + constructor($log, portalAdminsService, $scope, confirmBoxService) { + + let init = () => { + this.isSaving = false; + if($scope.ngDialogData && $scope.ngDialogData.selectedUser && $scope.ngDialogData.dialogState){ + this.selectedUser = $scope.ngDialogData.selectedUser; + this.dialogState = $scope.ngDialogData.dialogState; + }else{ + this.selectedUser = null; + this.dialogState = 1; + } + $log.info('NewPortalAdminModalCtrl:: initiated'); + }; + + this.addNewPortalAdmin = () => { + portalAdminsService.getPortalAdmins().then(result=> { + var dupNameCheck = JSON.stringify(result).search(this.selectedUser.orgUserId); + if (dupNameCheck != -1) { + $log.error("NewPortalAdminModalCtrl::addNewPortalAdmin: userId already exists as a portal admin! dupNameCheck=",dupNameCheck); + confirmBoxService.showInformation('This user already exists as a portal admin!').then(function (isConfirmed) { + $scope.closeThisDialog(true); + }); + } else { + confirmBoxService.makeAdminChanges('Are you sure you want to add "' + this.selectedUser.firstName + ' ' + this.selectedUser.lastName + '" as a Portal Admin?') + .then(isConfirmed => { + if(isConfirmed) { + if (!this.selectedUser || !this.selectedUser.orgUserId) { + $log.error('NewPortalAdminModalCtrl::makeAdminChanges: No portal admin or ID... cannot add'); + return; + } + portalAdminsService.addPortalAdmin(this.selectedUser.orgUserId) + .then(() => { + $log.debug("NewPortalAdminModalCtrl::addNewPortalAdmin: portal admin added successfully"); + $scope.closeThisDialog(true); + }).catch(err => { + $log.error('NewPortalAdminModalCtrl::addNewPortalAdmin error status: ' + err.status); + confirmBoxService.showInformation('There was a unknown problem adding the portal admin. ' + 'Please try again later. Error Status: '+ err.status).then(function (isConfirmed) {}); + }); + } + }).catch(err => { + $log.error('portalAdminsService.addPortalAdmin error status: '+ err.status); + }); + } + }).catch(err=> { + $log.error('NewPortalAdminModalCtrl::addNewPortalAdmin error getting portal admins list:',err); + }); + }; + + this.setSelectedUser = (user) => { + $log.debug('NewPortalAdminModalCtrl::setSelectedUser: selected user: ', user); + this.selectedUser = user; + }; + + init(); + + $scope.$on('$stateChangeStart', e => { + e.preventDefault(); + }); + } + } + NewPortalAdminModalCtrl.$inject = ['$log', 'portalAdminsService', '$scope', 'confirmBoxService']; + angular.module('ecompApp').controller('NewPortalAdminModalCtrl', NewPortalAdminModalCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html new file mode 100644 index 00000000..f235b391 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html @@ -0,0 +1,32 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="new-admin-modal"> + <div class="search-users"> + <search-users search-title="Add New Portal Admin" + selected-user="newPortalAdmin.selectedUser"></search-users> + <div class="dialog-control"> + <div id="pa-search-users-button-save" class="save-button" + ng-click="newPortalAdmin.selectedUser && newPortalAdmin.addNewPortalAdmin()" + ng-class="{disabled: !newPortalAdmin.selectedUser}">Save + </div> + <div id="pa-search-users-button-cancel" class="cancel-button" ng-click="closeThisDialog()">Cancel</div> + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less new file mode 100644 index 00000000..dcc4fc52 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less @@ -0,0 +1,99 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .new-portal-admin-modal { + display:block; + overflow:auto; + min-height: 450px; + + .search-users { + } + + .admin-app-roles { + .title { + .n18r; + border-bottom: @a 3px solid; + + } + + .app-roles-main { + margin-top: 16px; + .app-roles-main-title { + .n14r; + margin-bottom: 8px; + .left { + display: inline-block; + } + .right { + display: inline-block; + color: @a; + float: right; + cursor: pointer; + } + } + + .select-input{ + width: 460px; + } + + .new-administrated-app { + height: 30px; + line-height: 30px; + + border: 1px solid @p; + margin-bottom: 8px; + border-radius: 2px; + padding-left: 6px; + padding-top: 0; + .o14r; + } + + .admin-roles-list { + height: 240px; + overflow-y: auto; + } + + .administrated-application { + width: 460px; + height: 30px; + border: 1px solid @p; + margin-bottom: 8px; + border-radius: 2px; + padding: 6px; + .o14r; + display: inline-block; + + } + + .delete-application { + .ico_trash_default; + display: inline-block; + vertical-align: 4px; + cursor: pointer; + position: relative; + top: 6px; + color: transparent; + margin-left: 8px; + } + + } + + } +} + diff --git a/ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js new file mode 100644 index 00000000..10043005 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js @@ -0,0 +1,127 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class PortalAdminsCtrl { + constructor($log, portalAdminsService, ngDialog, confirmBoxService) { + + let updateTableData = () => { + this.isLoadingTable = true; + portalAdminsService.getPortalAdmins().then(result=> { + $log.debug('PortalAdminsCtrl::updateTableData: result: ' + JSON.stringify(result)); + if (!result || !result.length) { + $log.info('PortalAdminsCtrl::updateTableData: no Portal Admins err handling'); + this.portalAdminsTableData = []; + return; + } + this.portalAdminsTableData = result; + }).catch(err=> { + $log.error('PortalAdminsCtrl::updateTableData error :',err); + }).finally(() => { + this.isLoadingTable = false; + }); + }; + + let init = () => { + $log.info('portalAdminsService.getPortalAdmins::initializing...'); + this.isLoadingTable = false; + + this.searchString= ''; + this.portalAdminsTableHeaders = ['First Name', 'Last Name', 'User ID', 'Delete']; + this.portalAdminsTableData = []; + updateTableData(); + }; + + init(); + + this.removePortalAdmin = pAdmin => { + $log.debug('pAdmin = ' + JSON.stringify(pAdmin)); + confirmBoxService.deleteItem(pAdmin.firstName + ' ' + pAdmin.lastName ) + .then(isConfirmed => { + if(isConfirmed){ + if(!pAdmin || !pAdmin.userId){ + $log.error('PortalAdminsCtrl::removePortalAdmin No portal admin or ID... cannot delete'); + return; + } + portalAdminsService.removePortalAdmin(pAdmin.userId).then(() => { + $log.info("PortalAdminsCtrl::removePortalAdmin removed admin"); + init(); + }).catch(err => { + $log.error('PortalAdminsCtrl::removePortalAdmin.deleteItem error: '+ err); + }); + } + }).catch(err => { + $log.error('PortalAdminsCtrl::removePortalAdmin.deleteItem error: '+ err); + }); + }; + + this.openAddNewPortalAdminModal = (user) => { + let data = null; + if(user){ + data = { + dialogState: 2, + selectedUser:{ + userId: user.orgUserId, + firstName: user.firstName, + lastName: user.lastName + } + } + } + ngDialog.open({ + templateUrl: 'app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html', + controller: 'NewPortalAdminModalCtrl', + controllerAs: 'newPortalAdmin', + data: data + }).closePromise.then(needUpdate => { + if(needUpdate.value === true){ + $log.debug('PortalAdminsCtrl::openAddNewPortalAdminModal: updating Portal Admin table data...'); + updateTableData(); + } + }); + }; + + this.openEditUserModal = (loginId) => { + var data = { + loginId : loginId, + updateRemoteApp : false, + appId : this.selectedApp!=null?this.selectedApp.id:'' + } + var modalInstance = ngDialog.open({ + templateUrl: 'app/views/header/user-edit/edit-user.tpl.html', + controller: 'editUserController', + data: data, + resolve: { + message: function message() { + var message = { + type: 'Contact', + }; + return message; + } + } + }).closePromise.then(needUpdate => { + updateTableData(); + }); + } + + } + } + PortalAdminsCtrl.$inject = ['$log', 'portalAdminsService', 'ngDialog', 'confirmBoxService']; + angular.module('ecompApp').controller('PortalAdminsCtrl', PortalAdminsCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html new file mode 100644 index 00000000..05216f34 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html @@ -0,0 +1,73 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-main"> + <div class="w-ecomp-main-container"> + <div class="portal-admins-page-main" id="contentId"> + <div id="title" class="w-ecomp-main-view-title">Portal Admins</div> + <div class="portal-admins-table"> + <div class="table-control"> + <input id="input-table-search" class="table-search" type="text" + placeholder="Search in entire table" + ng-model="portalAdmin.searchString"/> + <div id="add-portal-admin-button" class="add-button portal-add-button" ng-click="portalAdmin.openAddNewPortalAdminModal()">Add Portal Admin</div> + </div> + <span class="ecomp-spinner" ng-show="portalAdmin.isLoadingTable"></span> + <div class="c-ecomp-att-abs-table default" ng-hide="portalAdmin.isLoadingTable"> + <table att-table + table-data="portalAdmin.portalAdminsTableData" + search-string="portalAdmin.searchString" + view-per-page="portalAdmin.viewPerPageIgnored" + current-page="portalAdmin.currentPageIgnored" + total-page="portalAdmin.totalPageIgnored"> + <thead att-table-row type="header"> + <tr> + <th id="th-first-name" att-table-header key="firstName" default-sort="a">{{portalAdmin.portalAdminsTableHeaders[0]}}</th> + <th id="th-last-name" att-table-header key="lastName" sortable="true">{{portalAdmin.portalAdminsTableHeaders[1]}}</th> + <th id="th-userId" att-table-header key="userId" sortable="true">{{portalAdmin.portalAdminsTableHeaders[2]}}</th> + <th id="portal-admin-th-header-delete" att-table-header sortable="{{false}}">{{portalAdmin.portalAdminsTableHeaders[3]}}</th> + </tr> + </thead> + <tbody att-table-row type="body" + class="table-body" + row-repeat="rowData in portalAdmin.portalAdminsTableData | filter: portalAdmin.portalsRowFilter"> + <tr> + <td att-table-body> + <div id="div-{{rowData.userId}}-{{rowData.firstName}}" ng-bind="rowData.firstName"></div> + </td> + <td att-table-body> + <div id="div-{{rowData.userId}}-{{rowData.lastName}}" ng-bind="rowData.lastName"></div> + </td> + <td att-table-body> + <div id="div-{{rowData.userId}}" style="float: left;" ng-bind="rowData.loginId"></div> + <div> + <span style="float: left; margin-left:15px" class="ion-person" ng-click="portalAdmin.openEditUserModal(rowData.loginId);$event.stopPropagation()"></span> + </div> + </td> + <td att-table-body> + <div id="portal-admin-delete-{{$index}}" class="ion-trash-b" ng-click="portalAdmin.removePortalAdmin(rowData)"></div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less b/ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less new file mode 100644 index 00000000..29fd9e32 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less @@ -0,0 +1,56 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .portal-admins-page-main { + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + + .portal-admins-table { + width: @table-width; + margin: @table-margin; + + .table-control { + .table-dropdown-filter{ + width: @table-dropdown-filter-width; + display: @table-dropdown-filter-display; + } + } + + .table-body { + cursor: pointer; + } + } + + .delete-user{ + .ico_trash_default; + } + + .portal-add-button { + width: 160px; + } +} + diff --git a/ecomp-portal-FE/client/app/views/search/search.controller.js b/ecomp-portal-FE/client/app/views/search/search.controller.js new file mode 100644 index 00000000..60e27593 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/search/search.controller.js @@ -0,0 +1,184 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + + class SearchCtrl { + constructor($log, $scope, $cookies, $timeout, userProfileService, sessionService, dashboardService) { + $scope.firstName=""; + $scope.lastName=""; + + function showHideSearchSnippet() { + + setTimeout(function() { + jQuery("#mainSearchSnippet").click(); + },500); + + setTimeout(function() { + jQuery("#mainSearchText").focus(); + },1000); + } + + jQuery("#mainSearchDiv").keyup(function(event){ + if(event.keyCode == 13){ + // there is a watch on this variable which will trigger the database pull + dashboardService.searchString = jQuery("#mainSearchText").val(); + + // opens the popup + var popupDomObj = jQuery("[content='searchSnippet.html']"); + if(popupDomObj.length == 0) { + showHideSearchSnippet(); + } else { + jQuery("#mainSearchSnippet").click(); + showHideSearchSnippet(); + } + + + + } + }); + + + + } + + + } + + + class SearchSnippetCtrl { + constructor($log, $scope, $cookies, $timeout, userProfileService, sessionService, dashboardService,applicationsService, $window, $state) { + $scope.firstName=""; + $scope.lastName=""; + $scope.goToUrl = goToUrl; + $scope.dService = dashboardService; + + + + function goToUrl (item, type) { + $log.info("goToUrl called") + $log.info(item + "/" + type); + + + if(type == 'intra') { + + var intraSearcLink = "http://to_do_link.com"; + var intraSpecSearcLink = intraSearcLink + encodeURIComponent(dashboardService.searchString); + $window.open(intraSpecSearcLink, '_blank'); + + } else if (type == 'extra') { + var extraSearcLink = "https://to_do_link.com"; + var extraSpecSearcLink = extraSearcLink + encodeURIComponent(dashboardService.searchString); + $window.open(extraSpecSearcLink, '_blank'); + } + + let url = item.target; + let restrictedApp = item.uuid; + let getAccessState = "root.getAccess" + if (!url) { + + applicationsService.goGetAccessAppName = item.name; + if($state.current.name == getAccessState) + $state.reload(); + else + $state.go(getAccessState); + //$log.info('No url found for this application, doing nothing..'); + return; + } + + if (restrictedApp != "true") { + $window.open(url, '_blank'); + } else { + if(item.url=="root.access"){ + $state.go(url); + var tabContent = { id: new Date(), title: 'Home', url: url }; + $cookies.putObject('addTab', tabContent ); + } else { + var tabContent = { id: new Date(), title: item.name, url: url }; + $cookies.putObject('addTab', tabContent ); + } + } + + } + + function getItems(searchString) { + + var items; + var itemMap = dashboardService.getSearchAllByStringResults(searchString) + .then(res => { + $scope.items = res; + + + }).catch(err => { + $scope.items = []; + $log.error('Couldnt get search results...', err) + }); + + } + + $scope.$watch('dService.searchString', function(searchString) { + if(searchString != undefined ) + getItems(searchString); + + }); + + + + } + + + + } + + + + SearchCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService', 'dashboardService']; + SearchSnippetCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService', 'dashboardService','applicationsService', '$window','$state']; + angular.module('ecompApp').controller('searchCtrl', SearchCtrl); + angular.module('ecompApp').controller('searchSnippetCtrl', SearchSnippetCtrl); + angular.module('ecompApp').directive('searchBox', function() { + return { + restrict: "E", + templateUrl: 'app/views/search/search.tpl.html', + link: function(scope, element) { + + //var iframeId = "#tabframe-" + scope.$parent.tab.title.split(' ').join('-'); + // jQuery(iframeId).load(function() { + // alert("hello"); + // }); //.attr("src",'{{tab.content | trusted}}' ); //src='{{tab.content | trusted}}' + // jQuery(iframeId).attr('src', '{{tab.content | trusted}}'); + + //element.childNodes[0].on('load', function() { + // alert('hello'); + //}); + } + } + }); + angular.module( 'ecompApp' ).config( [ + '$compileProvider', + function( $compileProvider ) + { + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|qto):/); + } + ]); + +})(); + + diff --git a/ecomp-portal-FE/client/app/views/search/search.controller.spec.js b/ecomp-portal-FE/client/app/views/search/search.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/search/search.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/search/search.less b/ecomp-portal-FE/client/app/views/search/search.less new file mode 100644 index 00000000..a21559ec --- /dev/null +++ b/ecomp-portal-FE/client/app/views/search/search.less @@ -0,0 +1,55 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .searchLiHeader { +font-weight: bold; +color: #0574ac; +font-size: 16px; +padding-bottom: 10px; +line-height: 1.5; +font-family: arial; + +} + +.searchLiItems{ +cursor: pointer; +font-weight: normal; +font-size: 12px; +color: #444444; +font-family: arial; +} + +.searchUl { +list-style: none; +border-bottom: 1px solid #bbb; +padding-bottom: 20px; +} + +#contentVertical { + height: 300px; + width: 250px; + } + +#contentVertical .scroll-viewport { + height: 300px; + width: 250px; + } +#mainSearchText::-ms-clear{ + display:none; +}
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/search/search.tpl.html b/ecomp-portal-FE/client/app/views/search/search.tpl.html new file mode 100644 index 00000000..bd37f772 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/search/search.tpl.html @@ -0,0 +1,82 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> + +<div class="col-md-12 font-clearview"> + <div class="form-field form-field__small" > + <input id="mainSearchText" type="text" placeholder="What are you looking for?" class="form-field form-field__small" style="height:30px;border-radius: 10px;border: 2px solid rgb(250,250,250);" /> + <i class="ion-ios-search" id="search-icon" style="position:relative;line-height:10px;top:-30px;right:-250px"> </i> + + <div id="mainSearchSnippet" popover="searchSnippet.html" + aria-label="search Snippet" referby="searchSnippet" att-accessibility-click="13,32" + popover-style="" popover-trigger="click" popover-placement="below" > + </div> + </div> + +</div> + + + + +<script type="text/ng-template" id="searchSnippet.html"> + <div style="line-height: normal; right: 167px; min-height: 122px; height: auto; width: auto;" ng-controller="searchSnippetCtrl" > + <div id="reg-header-snippet"> + + <div tabindex="0" class="reg-profileDetails" id="reg-searchPop-id" > + <div id="contentVertical" att-scrollbar="y" class="ng-scope vertical" style="position: relative;"> + <ul class="searchUl"> + <li> + <a class="ion-android-apps"></a> + <a id="searchItem" ng-click="goToUrl(item)" class="searchLiHeader">Applications:</a> + </li> + <li ng-repeat='item in items.application'> + <a id="searchItem" ng-click="goToUrl(item)" class="searchLiItems">{{item.name}}</a> + </li> + </ul> + + <ul class="searchUl"> + <li> + <a class="ion-android-apps"></a> + <a id="searchItem" ng-click="goToUrl(item)" class="searchLiHeader">Functional Menus:</a> + </li> + <li ng-repeat='item in items.menu'> + <a id="searchItem" ng-click="goToUrl(item)" class="searchLiItems">{{item.name}}</a> + </li> + </ul> + + + <ul class="searchUl"> + <li> + <a class="ion-ios-person-outline" style="font-size:16px;"></a> + <a id="searchItem" ng-click="goToUrl(item)" class="searchLiHeader">Users:</a> + </li> + <li ng-repeat='item in items.user'> + <a id="searchItem" ng-href="mailto:{{item.target}}" class="searchLiItems">{{item.name}}</a> + + </li> + </ul> + + + </div> + </div> + </div> + </div> + + +</script> diff --git a/ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js b/ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js new file mode 100644 index 00000000..76d5301c --- /dev/null +++ b/ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js @@ -0,0 +1,143 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class SidebarCtrl { + constructor(userProfileService, $log, $rootScope) { + this.$log = $log; + this.userProfileService = userProfileService; + this.$rootScope = $rootScope; + $rootScope.isAdminPortalAdmin = false; + + //if (bowser.msie || bowser.msedge) + // $log.debug('SidebarCtrl::init: Browser is: Internet Explorer or Edge'); + // else + // $log.debug('SidebarCtrl::init: Browser is: ' + bowser.name + ': ' + bowser.version); + + userProfileService.getUserProfile() + .then(profile=> { + // $log.debug('SidebarCtrl::getUserProfile: profile.roles.indexOf(superAdmin) = ' + profile.roles.indexOf('superAdmin')); + // $log.debug('SidebarCtrl::getUserProfile: profile.roles.indexOf(admin) = ' + profile.roles.indexOf('admin')); + if (profile.roles.indexOf('superAdmin') > -1) { + $rootScope.isAdminPortalAdmin = true; + // this.$log.debug('SidebarCtrl::getUserProfile: user has the superAdmin role'); + this.sidebarModel = { + label: 'ECOMP portal', + navItems: [ + { + name: 'Home', + state: 'root.applicationsHome', + imageSrc : 'ion-home' + }, { + name: 'Application Catalog', + state: 'root.appCatalog', + imageSrc : 'ion-grid' + }, { + name: 'Widgets', + state: 'root.widgetsHome', + imageSrc : 'ion-android-apps' + }, { + name: 'Admins', + state: 'root.admins', + imageSrc : 'ion-android-star' + }, { + name: 'Users', + state: 'root.users', + imageSrc : 'ion-person' + },{ + name: 'Portal Admins', + state: 'root.portalAdmins', + imageSrc : 'ion-gear-a' + }, { + name: 'Application Onboarding', + state: 'root.applications', + imageSrc : 'ion-ios-upload-outline' + }, { + name: 'Widget Onboarding', + state: 'root.widgets', + imageSrc : 'ion-ios-upload-outline' + },{ + name: 'Edit Functional Menu', + state: 'root.functionalMenu', + imageSrc : 'ion-compose' + } + ] + }; + } + else if (profile.roles.indexOf('admin') > -1) { + // this.$log.debug('SidebarCtrl::getUserProfile: user has the admin role'); + this.sidebarModel = { + label: 'ECOMP portal', + navItems: [ + { + name: 'Home', + state: 'root.applicationsHome', + imageSrc : 'ion-home' + }, { + name: 'Application Catalog', + state: 'root.appCatalog', + imageSrc : 'ion-grid' + }, { + name: 'Widgets', + state: 'root.widgetsHome', + imageSrc : 'ion-grid' + }, { + name: 'Users', + state: 'root.users', + imageSrc : 'ion-person' + }, { + name: 'Widget Onboarding', + state: 'root.widgets', + imageSrc : 'ion-grid' + } + ] + }; + } + else { + $rootScope.isAdminPortalAdmin = false; + // this.$log.debug('SidebarCtrl::getUserProfile: user is not superAdmin nor admin'); + this.sidebarModel = { + label: 'ECOMP portal', + navItems: [ + { + name: 'Home', + state: 'root.applicationsHome', + imageSrc : 'ion-home' + }, { + name: 'Application Catalog', + state: 'root.appCatalog', + imageSrc : 'ion-grid' + }, { + name: 'Widgets', + state: 'root.widgetsHome', + imageSrc : 'ion-grid' + } + ] + }; + } + }).catch(err=> { + $log.error('SidebarCtrl: failed to get user profile: ' + err); + }); + + } + } + SidebarCtrl.$inject = ['userProfileService', '$log', '$rootScope']; + angular.module('ecompApp').controller('SidebarCtrl', SidebarCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/sidebar/sidebar.less b/ecomp-portal-FE/client/app/views/sidebar/sidebar.less new file mode 100644 index 00000000..97650fed --- /dev/null +++ b/ecomp-portal-FE/client/app/views/sidebar/sidebar.less @@ -0,0 +1,37 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-sidebar { + position: relative; + left: 0; + right: 0; + top: 5px; + padding-left: 0; +} + + @media screen and (-webkit-min-device-pixel-ratio:0) +{ + .w-ecomp-sidebar { + position: relative; + left: 0; + right: 0; + top: -5px; + padding-left: 0; + } +}
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/sidebar/sidebar.tpl.html b/ecomp-portal-FE/client/app/views/sidebar/sidebar.tpl.html new file mode 100644 index 00000000..a5cbf8ff --- /dev/null +++ b/ecomp-portal-FE/client/app/views/sidebar/sidebar.tpl.html @@ -0,0 +1,20 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<left-menu id="sidebar.sidebarModel" sidebar-model="sidebar.sidebarModel" class="w-ecomp-sidebar"></left-menu> diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js new file mode 100644 index 00000000..368f2fe3 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js @@ -0,0 +1,194 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +(function () { + class ContactUsManageController { + constructor($scope,$log, message, $q, $http, conf,contactUsService,confirmBoxService) { + $scope.contactUsList=[]; + $scope.contactUsAllAppList=[]; + $scope.errMsg=''; + $scope.newContactUs ={ + app:'', + name:'', + email:'', + url:'', + desc:'' + }; + /*Get the existing contact us first, then based on the existing, filter from all apps*/ + $scope.getContactUsList = function(){ + contactUsService.getContactUs().then(res=> { + // $log.info('getting getContactUs',res.response); + if(res!=null && res.response!=null){ + for(var i=0; i<res.response.length;i++){ + if(res.response[i].appId!=1) + $scope.contactUsList.push(res.response[i]); + } + } + /*get all the apps*/ + contactUsService.getListOfApp().then(res=> { + var tableData=[]; + $scope.contactUsAllAppListTemp=[]; + // $log.info('contactUsService::getListOfApp: getting res'); + var result = (typeof(res.data) != "undefined" && res.data!=null)?res.data:null; + // $log.info('contactUsService::getListOfApp: result',result); + var res1 = result; + var realAppIndex = 0; + $scope.contactUsAllAppList.length=0; + for (var i = 1; i <= res1.length; i++) { + if (!res1[i - 1].restrictedApp) { + var okToAdd = true; + for(var j =0; j<$scope.contactUsList.length;j++){ + if(res1[i - 1].title==$scope.contactUsList[j].appName) + okToAdd=false; + } + // not allowed to add(duplicate) another entry if the app is already available in the table + if(okToAdd){ + if(res1[i - 1].title){ + $scope.contactUsAllAppList.push({ + index: realAppIndex, + title: res1[i - 1].title, + value: res1[i - 1].index + }); + } + realAppIndex = realAppIndex + 1; + } + } else { + // $log.debug('contactUsService:getAvailableApps:: Restricted/URL only App will not be used = ' + res1[i - 1].title); + } + } + }).catch(err=> { + $log.error('contactUsService:error:: ', err); + + + }).finally(() => { + //this.isLoadingTable = false; + }); + }); + } + + $scope.getContactUsList(); + + $scope.closeDialog = function(){ + $scope.closeThisDialog( $scope.widgetData); + } + + /*Add new Contact Us*/ + $scope.newContactUsFun = function(){ + if($scope.newContactUs.app.title==null || $scope.newContactUs.app.title=='' ){ + confirmBoxService.showInformation('Please select an App to add').then(isConfirmed => { + return; + }); + } + + if($scope.newContactUs.url !=null && $scope.newContactUs.url != '' && !validateUrl($scope.newContactUs.url)){ + var warningMsg = "Please enter a valid URL"; + confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;}); + return; + } + + contactUsService.addContactUs($scope.newContactUs).then(res=> { + // $log.info('contactUsService: add ContactUs successfully'); + $scope.contactUsList.length=0; + // $log.info('contactUsService: refreshing the Contact US table'); + $scope.getContactUsList(); + $scope.errMsg=''; + /* confirmBoxService.showInformation('You have added a new Contact Us item').then(isConfirmed => { });*/ + var defaultSel={ + index: 0, + title: '', + value: '' + } + $scope.newContactUs ={ + app:defaultSel, + name:'', + email:'', + url:'', + desc:'' + }; + + }).catch(err=> { + $log.error('contactUsService: addContactUs error:: ', err); + // $scope.errMsg=err; + confirmBoxService.showInformation('Add Contact Us list failed: ' + err); + + }).finally(() => { + //this.isLoadingTable = false; + }); + } + /* Edit Contact Us*/ + $scope.editContactUsFun = function(contactObj){ + // $log.info('contactUsService: edit ContactUs save successfully', contactObj); + var contactUsObj={ + appId:contactObj.appId, + appName:contactObj.appName, + description:contactObj.description, + contactName:contactObj.contactName, + contactEmail:contactObj.contactEmail, + url:contactObj.url, + }; + + contactUsService.modifyContactUs(contactUsObj).then(res=> { + // $log.info('contactUsService: edit ContactUs successfully'); + // confirmBoxService.showInformation('You have saved the changes').then(isConfirmed => {}); + $scope.errMsg=''; + + }).catch(err=> { + $log.error('contactUsService: editContactUs error:: ', err); + confirmBoxService.showInformation('Edit Contact Us list failed: ' + err); + // $scope.errMsg=err; + }).finally(() => { + //this.isLoadingTable = false; + }); + + } + /*del Contact Us*/ + $scope.delContactUsFun = function(appObj){ + var confirmMsg = 'Are you sure you want to delete '+appObj.appName +' from the list?' + ' Press OK to delete.'; + confirmBoxService.confirm(confirmMsg).then(function (confirmed) { + if (confirmed == true) { + contactUsService.removeContactUs(appObj.appId).then(res=> { + // $log.info('delContactUsFun: delete ContactUs successfully',res); + $scope.errMsg=''; + $scope.contactUsList.length=0; + $scope.getContactUsList(); + confirmBoxService.showInformation('Item has been deleted').then(isConfirmed => {}); + }).catch(err=> { + $log.error('contactUsService: addContactUs error:: ', err); + confirmBoxService.showInformation('Deletion failed: ' + err); + // $scope.errMsg=err; + }).finally(() => { + //this.isLoadingTable = false; + }); + } + }); + + } + + } + } + ContactUsManageController.$inject = ['$scope','$log', 'message', '$q', '$http', 'conf','contactUsService','confirmBoxService']; + angular.module('ecompApp').controller('ContactUsManageController', ContactUsManageController); + + +})(); +function validateUrl(value){ + return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value); + } diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less new file mode 100644 index 00000000..ef8c88df --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less @@ -0,0 +1,159 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .searchLiHeader { +font-weight: bold; +color: #0574ac; +font-size: 16px; +padding-bottom: 10px; +line-height: 1.5; +font-family: arial; + +} + +.searchLiItems{ +cursor: pointer; +font-weight: normal; +font-size: 12px; +color: #444444; +font-family: arial; +} + +.searchUl { +list-style: none; +border-bottom: 1px solid #bbb; +padding-bottom: 20px; +} + +.manage-contactUs-home-title{ + color: #067ab4; + font-family: arial; + font-size: 24px; + margin-left: 78px; + width: 1170px; + } + +.manage-contactUs-home-manageWidgets-title{ + color: #067ab4; + font-family: arial; + font-size: 24px; + margin-left: 30px; + width: 1170px; + } + +#editWidgetsContent { + height: 300px; + width: 1250px; + } + +#editWidgetsContent .scroll-viewport { + height: 300px; + width: 1250px; + } + + .add-contact-us-field{ + width:250px; + display: inline-block; + margin:10px; + } + .add-contact-us-field-des{ + width:800px; + margin:10px; + } + +.errMsg{ + color:red; +} +.sucessMsg{ + color:green; +} +.contact-us-margin{ + margin-left: 80px; + margin-right: 120px; +} +.contact-us-manage-table { + + + .delete-contact-us{ + font-size:20px; + } + + .portal-add-button { + width: 160px; + } +} + +.select2-container .select2-chosen, .select2-container .select2-container input{ + line-height:39px; +} +.select2-container .select2-choice{ + -webkit-transition: border-color .2s ease-out; + -moz-transition: border-color .2s ease-out; + transition: border-color .2s ease-out; + background: #fff; + border-radius: 6px; + display: block; + border: 1px solid #888; + box-shadow: none; + filter: none; + height: 32px !important; + padding: 0; + position: relative; + z-index: 5; + cursor: pointer; +} + +.c-ecomp-att-abs-table .contactUs{ + + height:300px; + +} + +.ngdialog-overlay { + pointer-events: none !important; +} + +.input-text-area { + font-style: italic; + padding: 7px 10px; + width: 250px; + display: inline-block; + position: relative; + margin-bottom: 10px; + border-radius: 6px; + border: 1px solid #d8d8d8; + height: 32px !important; + border-color: slategrey !important; +} +.input-inline-edit-text { + font-style: italic; + padding: 7px 10px; + display: inline-block; + position: relative; + margin-bottom: 10px; + border-radius: 6px; + border: 1px solid #d8d8d8; + height: 32px; + border-color: slategrey !important; + width:100%; + } +.glyphicon { + position: relative !important; + line-height: 0 !important; +}
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html new file mode 100644 index 00000000..257226a0 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html @@ -0,0 +1,136 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div style="margin-top: 10px; margin-bottom: 10px;"> + +</div> +<br/> + +<div style="margin-top: 25px;"> + <div id="title" class="manage-contactUs-home-title">Manage Contact Us</div> + <div class="contact-us-margin"> + <div class="get-access-table"> + <div class="table-control"> + <div class="c-ecomp-att-abs-table" style="height:300px""> + <table att-table id="table-main" table-data="contactUsList" current-page="ignoredCurrentPage"> + <thead att-table-row type="header"> + <tr> + <th id="th-users-0" att-table-header key="ecomp_function" default-sort="a" >App Name</th> + <th id="th-users-1" att-table-header key="app_name" sortable="true" >Contact Name</th> + <th id="th-users-2" att-table-header key="app_name" sortable="true" >Contact Email</th> + <th id="th-users-3" att-table-header key="role_name" sortable="true">Contact URL</th> + <th id="th-users-4" att-table-header key="role_name" sortable="true" >Description</th> + <th id="th-users-5" att-table-header key="role_name" sortable="true" >Edit</th> + <th id="th-users-6" att-table-header key="role_name" sortable="true" >Delete</th> + </tr> + </thead> + <!-- Use track-by="UNIQUE KEY HERE" or leave out if no unique keys in data --> + <tbody att-table-row type="body" class="table-body" track-by="$index" row-repeat="rowData in contactUsList"> + <tr id="tr-rowData" ng-click=""> + <td att-table-body> + <div id="users-page-td-appName" ng-bind="rowData.appName"></div> + </td> + <td att-table-body> + <div id="users-page-td-name" ng-hide="rowData.showEdit" ng-bind="rowData.contactName"></div> + <input class="input-inline-edit-text" type="text" ng-show="rowData.showEdit" ng-model="contactUsList[$index].contactName"/> + </td> + <td att-table-body> + <div id="users-page-td-email" ng-hide="rowData.showEdit" ng-bind="rowData.contactEmail"></div> + <input class="input-inline-edit-text" type="text" ng-show="rowData.showEdit" ng-model="contactUsList[$index].contactEmail"/> + </td> + <td att-table-body> + <div id="users-page-td-url" ng-hide="rowData.showEdit" ng-bind="rowData.url"></div> + <input class="input-inline-edit-text" type="text" ng-show="rowData.showEdit" ng-model="contactUsList[$index].url"/> + </td> + <td att-table-body> + <div id="users-page-td-descr" ng-hide="rowData.showEdit" ng-bind=" rowData.description"></div> + <input class="input-inline-edit-text" type="text" ng-show="rowData.showEdit" ng-model="contactUsList[$index].description"/> + </td> + <td att-table-body> + <div class="delete-contact-us" ng-hide="rowData.showEdit" ng-click="rowData.showEdit=true"> + <span class="ion-edit"></span> + </div> + <span ng-show="rowData.showEdit" > + <a att-button btn-type="primary" ng-click="editContactUsFun(rowData); rowData.showEdit=false" class="button button--primary" size="small" >Save</a> + </span> + </td> + <td att-table-body> + <div class="delete-contact-us" ng-click="delContactUsFun(rowData)"> + <span class="ion-trash-b"></span> + </div> + </td> + </tr> + </tbody> + </table> + </div> + + <div att-divider-lines></div> + <div style="margin-top: 15px; margin-left: -78px;"> + <div id="addWidgetHeader" class="contact-us-margin"> + <h1 style="font-size:18px;">Add Application Contact Information</h1> + <!-- <div class="errMsg">{{errMsg}}</div> --> + </div> + <div id="addWidget" class="contact-us-margin"> + <div> + <div class="add-contact-us-field"> + <div id="mots-property-label" class="property-label">App Name</div> + <div class="form-field" att-select="contactUsAllAppList" ng-model="newContactUs.app" placeholder="Select an App"></div> + </div> + <br> + <div class="add-contact-us-field" > + <div class="property-label">Contact Name</div> + <input class="input-text-area" type="text" ng-model="newContactUs.name"/> + </div> + <div class="add-contact-us-field" > + <div class="property-label">Contact Email</div> + <input class="input-text-area" type="text" ng-model="newContactUs.email"/> + </div> + <div class="add-contact-us-field" > + <div class="property-label">Contact URL</div> + <input class="input-text-area" type="text" ng-model="newContactUs.url"/> + </div> + <div class="add-contact-us-field-des"> + <div class="property-label">Description</div> + <textarea style="margin-top: 0px; margin-bottom: 0px; height: 100px" ng-model="newContactUs.desc"></textarea> + </div> + </div> + <div style="height:50px;"> + <a style="float:right; margin-top:20px" att-button btn-type="primary" class="button button--primary" size="small" ng-click="newContactUsFun()">Add New</a> + </div> + + <div att-divider-lines></div> + + <div style="height:50px;"> + <a style="float:right; margin-right: -162px;" att-button btn-type="primary" class="button button--primary" size="small" ng-click="closeDialog()">Close</a> + </div> + + </div> + + </div> + + </div> + </div> + </div> +</div> + + <script type="application/javascript"> + $(document).ready(function(){ + $(".ngdialog-content").css("width","85%") + }); +</script>
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js new file mode 100644 index 00000000..63e38a48 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js @@ -0,0 +1,166 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +(function () { + + class ContactUsCtrl { + constructor($log, contactUsService, applicationsService, $modal, ngDialog, $state) { + + contactUsService.getContactUSPortalDetails().then(res => { + // $log.info('ContactUsCtrl:: contactUsService getContactUSPortalDetails res',res); + // $log.info('getting res',res); + var result = (typeof(res.response) != "undefined" && res.response!=null)?res.response:null; + // $log.info('result',result); + // $log.info('done'); + var source = JSON.parse(result); + // $log.info(source); + this.ush_TicketInfoUrl = source.ush_ticket_url; + this.portalInfo_Address = source.feedback_email_address; + this.feedback_Url = source.portal_info_url; + }).catch(err=> { + $log.error('ContactUsCtrl:error:: ', err); + }).finally(() => { + }); + + let init = () => { + // $log.info('ecomp app::contact-us-controller::initializing...'); + this.appTable=[]; + this.functionalTableData=[]; + }; + init(); + + let updateContactUsTable = () => { + contactUsService.getAppsAndContacts().then(res=> { + // $log.info('ContactUsCtrl:: contactUsService getAppsAndContacts res',res); + var tableData=[]; + // $log.info('getting res',res); + var result = (typeof(res.response) != "undefined" && res.response!=null)?res.response:null; + // $log.info('result',result); + // $log.info('done'); + var source = result; + // $log.info(source); + // Drop Portal app, empty name entries + for(var i=0;i<source.length; i++) { + var dataArr = source[i]; + if ( !dataArr.appName || dataArr.appId == 1) { + continue; + } + var dataTemp={ + app_name: dataArr.appName, + contact_name: dataArr.contactName, + contact_email: dataArr.contactEmail, + desc: dataArr.description, + url_Info: dataArr.url, + app_Id: dataArr.appId, + } + tableData.push(dataTemp); + } + this.appTable=tableData; + }).catch(err=> { + $log.error('ContactUsCtrl.updateContactUsTable:error:: ', err); + }) + }; + + contactUsService.getAppCategoryFunctions().then(res=> { + // $log.info('ContactUsCtrl:: contactUsService getAppCategoryFunctionsthen res',res); + var tablefunctionalData=[]; + // $log.info('getting res',res); + var result = (typeof(res.response) != "undefined" && res.response!=null)?res.response:null; + // $log.info('result',result); + // $log.info('done'); + var source = result; + // $log.info(source); + for(var i=0;i<source.length; i++) { + var datafunctionalArr = source[i]; + var datafuntionalTemp={ + category: datafunctionalArr.category, + app_Name: datafunctionalArr.application, + functions: datafunctionalArr.functions, + app_Id: datafunctionalArr.appId, + } + tablefunctionalData.push(datafuntionalTemp); + } + this.functionalTableData=tablefunctionalData; + }).catch(err=> { + $log.error('ContactUsCtrl:error:: ', err); + }) + + updateContactUsTable(); + this.editContactUsModalPopup = () => { + // $log.debug('ContactUsCtrl::editContactUsModalPopup updating table data...'); + var modalInstance = ngDialog.open({ + templateUrl: 'app/views/support/contact-us/contact-us-manage/contact-us-manage.html', + controller: 'ContactUsManageController', + resolve: { + message: function message() { + var message = { + type: 'Contact', + }; + return message; + } + } + }).closePromise.then(needUpdate => { + updateContactUsTable(); + }); + }; + + this.goToSection = (id) => { + var targetDiv = document.getElementById(id); + var offSetHeight = 0; + for(var i=0;i<this.appTable.length;i++){ + if(this.appTable[i].app_Id==id) + break; + if(this.appTable[i].showFlag==true){ + offSetHeight+=document.getElementById('collapse'+i).clientHeight; + } + } + this.appTable.forEach(d => d.showFlag = false); + // let index = this.appTable.findIndex(a => a.app_Id == id); + var index =-1; + for(var i=0; i<this.appTable.length;i++){ + if(this.appTable[i].app_Id==id){ + index = i; + break; + } + } + console.log(index); + if (index > -1) { + // setting the showFlag to true based on index comparing with the app_Id + this.appTable[index].showFlag = true; + $('#contentId').animate({ + scrollTop: targetDiv.offsetTop-offSetHeight + 'px' + }, 'fast'); + } + + }; + + // Take the user to the application on the get access page. + this.goGetAccess = (appName) => { + // $log.debug('ContactUsCtrl::goGetAccess received name ' + appName); + applicationsService.goGetAccessAppName = appName; + $state.go('root.getAccess'); + }; + + } + } + ContactUsCtrl.$inject = ['$log','contactUsService', 'applicationsService', '$modal', 'ngDialog', '$state']; + angular.module('ecompApp').controller('ContactUsCtrl', ContactUsCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.spec.js b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.less b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.less new file mode 100644 index 00000000..b0c0af4f --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.less @@ -0,0 +1,104 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-contactUs-home { + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + padding-left: @padding-left-side; + + .contactUs-home-container { + .content_justify; + position: relative; + //padding-top: 15px; + padding-right: 0; + padding-left: 0; + padding-bottom: 32px; + width: 100%; + + .contactUs-home-title { + .a24r; + margin: auto; + .content_justify; + } + + .contactUs-general-div { + margin: auto; + padding-top: 15px; + padding-bottom: 15px; + .content_justify; + } + } + +.collapsibleArrow { + width: 30px; + height: 30px; + margin-left: 10px; + } + +.contactUs-collapsible-panel { + margin: auto; + width: 1170px; + + } + + .contactUs-collapsible-table { + margin: auto; + width: 1170px; + } + + .contactUs-txt { + font-weight: normal; + color: #666666; + font-size: 15px; + font-family: arial; + margin:10px; + } + +.contactUs-panel-header { + font-weight: normal; + color: #666666; + font-size: 18px; + font-family: sans-serif; + border-top: 1px solid #ccc; + outline : none; + height: 35px; + width: 1050px; + } + + .contactUs-panel-header:hover{ + color: #428bca; + background: #eae8e8; + cursor: pointer; cursor: hand; + } + +.contactUs-panel-labels { + font-weight: normal; + color: #666666; + font-size: 15px; + font-family: sans-serif; + padding-top: 5px; + padding-bottom: 5px; +} + +} diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html new file mode 100644 index 00000000..0b403ece --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html @@ -0,0 +1,125 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-contactUs-home"> + <div class="contactUs-home-container"> + <div class="admins-page-main" id="contentId"> + <div id="title" class="contactUs-home-title">Contact Us + <span ng-if="isAdminPortalAdmin == true" class="ion-edit" style="float: right;" ng-click="contact.editContactUsModalPopup()"></span> + </div> + + <div class="contactUs-general-div"> + <span class="contactUs-txt">To give feedback or report an issue to the OpenECOMP Portal team, send email to <a + ng-href="mailto:{{contact.portalInfo_Address}}" target="_top">{{contact.portalInfo_Address}}</a>. + </div> + + <br/> + <!-- Function Categories --> + <div id="appFunctionInfo" style="font-size: 20px;" class="contactUs-home-title"> Application Functions + <div class="contactUs-general-div"> + <div style="width: 1050px; height:auto;" class="c-ecomp-att-abs-table default"> + <table att-table id="table-main" + table-data="contact.functionalTableData" + current-page="ignoredCurrentPage"> + <thead att-table-row type="header"> + <tr> + <th id="th-functionalItem-0" style="width:20%;cursor: default" att-table-header sortable="false">Category</th> + <th id="th-functionalItem-1" style="width:60%;cursor: default" att-table-header sortable="false">ECOMP Functions</th> + <th id="th-functionalItem-2" style="cursor: default" att-table-header sortable="false">ECOMP Application</th> + <!-- <th id="th-functionalItem-3" att-table-header >Contact</th> --> + </tr> + </thead> + <!-- Use track-by="UNIQUE KEY HERE" or leave out if no unique keys in data --> + <tbody att-table-row + type="body" + class="table-body" + track-by="$index" + row-repeat="rowData in contact.functionalTableData"> + <tr id="tr-rowData" ng-click=""> + <td att-table-body> + <div id="functional-item-td-category" + ng-show="$index == 0 || contact.functionalTableData[$index-1].category != rowData.category" + ng-bind="rowData.category"> + </div> + </td> + <td att-table-body> + <div id="functional-item-td-functions" ng-bind="rowData.functions"></div> + </td> + <td att-table-body> + <a ng-click="contact.goToSection(rowData.app_Id, $index);">{{rowData.app_Name}}</a> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + <br/> + <!-- Display applicationInfo panels --> + <div id="allAppInfo"> + <div style="font-size: 20px;" class="contactUs-home-title"> All Applications + </div> + <div ng-repeat="rowData in contact.appTable" + class="contactUs-collapsible-panel"> + <div id="{{rowData.app_Id}}" style="height:60px; " class="contactUs-panel-header" ng-click="rowData.showFlag = !rowData.showFlag" > + <span ng-bind="rowData.app_name" style="position:relative; top:20px"></span> + + <span style="float: right; margin-right: 30px; margin-top:20px;"> + <span ng-hide="rowData.showFlag" class="ion-ios-arrow-down" alt="Expand app contact section"> </span> + <span ng-show="rowData.showFlag" class="ion-ios-arrow-up" alt="App contact section"> </span> + </span> + </div> + + <div id="collapse{{$index}}" ng-show="rowData.showFlag" + class="contactUs-collapsible-panel"> + <div class="contactUs-txt" ng-hide="rowData.contact_name || rowData.contact_email || rowData.url_Info || rowData.desc"> + <span style="margin-bottom: 20px;"> No application information is available. Please use the links above to contact the OpenECOMP Portal team. </span> + </div> + <div class="contactUs-txt" ng-show="rowData.contact_name || rowData.contact_email || rowData.url_Info || rowData.desc"> + <table style="border-spacing: 10px 5px; max-width:950px; margin:10px;"> + <tr> + <td class="contactUs-panel-labels" style="width: 120px;">Contact:</td> + <td class="contactUs-panel-labels" style="width: 830px;">{{rowData.contact_name}}</td> + </tr> + <tr> + <td class="contactUs-panel-labels">Email:</td> + <td class="contactUs-panel-labels"><a ng-href="mailto:{{rowData.contact_email}}" target="_top">{{rowData.contact_email}}</a></td> + </tr> + <tr> + <td class="contactUs-panel-labels">Info URL:</td> + <td class="contactUs-panel-labels"><a ng-href="{{rowData.url_Info}}" target="_blank">{{rowData.url_Info}}</a></td> + </tr> + <tr> + <td class="contactUs-panel-labels">Description:</td> + <td class="contactUs-panel-labels">{{rowData.desc}}</td> + </tr> + <tr> + <td class="contactUs-panel-labels">Get access:</td> + <td class="contactUs-panel-labels"><a href="" ng-click="contact.goGetAccess(rowData.app_name);">Click for application and role information</a></td> + </tr> + </table> + </div> + </div> + + </div> + </div> + + </div> +</div> +</div>
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js new file mode 100644 index 00000000..b9b254ac --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js @@ -0,0 +1,70 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class GetAccessCtrl { + constructor($log, $stateParams, getAccessService,applicationsService, ngDialog) { + // $log.debug('GetAccessCtrl: appService param is: ' + applicationsService.goGetAccessAppName); + + this.updateAppsList = () => { + getAccessService.getListOfApp().then(res=> { + var tableData=[]; + // $log.info('GetAccessCtrl::updateAppsList: getting res'); + var result = (typeof(res.data) != "undefined" && res.data!=null)?res.data:null; + // $log.info('GetAccessCtrl::updateAppsList: result',result); + // $log.info('GetAccessCtrl::updateAppsList: done'); + var source = result; + // $log.info('GetAccessCtrl::updateAppsList source: ', source); + for(var i=0;i<source.length; i++){ + var dataArr = source[i]; + var dataTemp={ + ecomp_function: dataArr.ecompFunction, + app_name:dataArr.appName, + role_name:dataArr.roleName + } + tableData.push(dataTemp); + } + this.appTable=tableData; + + if( $stateParams.appName != null) + this.searchString = $stateParams.appName; + else + this.searchString = applicationsService.goGetAccessAppName; + // the parameter has been used; clear the value. + applicationsService.goGetAccessAppName = ''; + }).catch(err=> { + $log.error('GetAccessCtrl:error:: ', err); + }).finally(() => { + this.isLoadingTable = false; + }); + }; + let init = () => { + // $log.info('GetAccessCtrl:: initializing...'); + this.searchString = ''; + this.getAccessTableHeaders = ['ECOMP Function', 'Application Name', 'Role Name']; + this.appTable=[]; + this.updateAppsList(); + }; + init(); + } + } + GetAccessCtrl.$inject = ['$log', '$stateParams', 'getAccessService', 'applicationsService', 'ngDialog']; + angular.module('ecompApp').controller('GetAccessCtrl', GetAccessCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.spec.js b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.less b/ecomp-portal-FE/client/app/views/support/get-access/get-access.less new file mode 100644 index 00000000..20b406e9 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.less @@ -0,0 +1,67 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-get-access-home { + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + padding-left: @padding-left-side; + + .get-access-home-container { + .content_justify; + position: relative; + padding-right: 0; + padding-left: 0; + padding-bottom: 32px; + width: 100%; + + .get-access-home-title { + .a24r; + margin: auto; + .content_justify; + } + + .get-access-general-div { + margin: auto; + padding-top: 15px; + padding-bottom: 15px; + .content_justify; + } + } + + .get-access-table { + width: @table-width; + margin: @table-margin; + + .table-control { + .table-dropdown-filter { + width: @table-dropdown-filter-width; + display: @table-dropdown-filter-display; + } + } + + .table-body { + cursor: pointer; + } + } +} diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html b/ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html new file mode 100644 index 00000000..3ed92f14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html @@ -0,0 +1,88 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-get-access-home"> + <div class="get-access-home-container"> + <div class="admins-page-main" id="contentId"> + <div id="title" class="get-access-home-title">Get Access</div> + <div class="get-access-general-div"> + <span> + Request access for following functions by sending email to the Application's Admin. For Admin contact details, please <a href='contactUs'>click here</a>. + </span> + </div> + <div class="get-access-table"> + <div class="table-control"> + <input id="input-table-search" + class="table-search" + type="text" + placeholder="Search" + ng-model="access.searchString"/> + <span class="ecomp-spinner" ng-show="access.isLoadingTable"></span> + <div style="height: auto;" class="c-ecomp-att-abs-table default" ng-hide="access.isLoadingTable"> + <!-- expects data to arrive sorted by first column: ecomp_function --> + <table att-table id="table-main" + table-data="access.appTable" + search-string="access.searchString" + view-per-page="access.viewPerPageIgnored" + current-page="access.currentPageIgnored" + total-page="access.totalPageIgnored"> + <thead att-table-row type="header"> + <tr> + <th id="th-access-0" att-table-header key="ecomp_function" sortable="false">{{access.getAccessTableHeaders[0]}}</th> + <th id="th-access-1" att-table-header key="app_name" sortable="false">{{access.getAccessTableHeaders[1]}}</th> + <th id="th-access-2" att-table-header key="role_name" sortable="false">{{access.getAccessTableHeaders[2]}}</th> + </tr> + </thead> + <!-- Use track-by="UNIQUE KEY HERE" or leave out if no unique keys in data --> + <tbody att-table-row type="body" + class="table-body" + track-by="$index" + row-repeat="rowData in access.appTable"> + <tr id="tr-rowData" ng-click=""> + <td att-table-body> + <div id="access-page-function" + ng-show="$index == 0 || access.appTable[$index-1].ecomp_function != rowData.ecomp_function" + ng-bind="rowData.ecomp_function"> + </div> + </td> + <td att-table-body> + <div id="access-page-appName" + ng-show="$index == 0 || access.appTable[$index-1].app_name != rowData.app_name" + ng-bind="rowData.app_name"></div> + </td> + <td att-table-body> + <div id="access-page-roleName" ng-bind="rowData.role_name"></div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + </div> +</div> +</div> +<style> +.tablesorter-default{ + cursor: default; +} +.admins-page-main .admins-table .table-body { + cursor: default; +} +</style> diff --git a/ecomp-portal-FE/client/app/views/tabs/tabframe.html b/ecomp-portal-FE/client/app/views/tabs/tabframe.html new file mode 100644 index 00000000..bc428832 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/tabs/tabframe.html @@ -0,0 +1,22 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<iframe id= "tabframe-{{tab.title.split(' ').join('-')}}" scrolling='yes' frameBorder='0' width='100%' style='height: 90vh;' +src='{{tab.content | trusted}}' +></iframe> diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.controller.js b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.js new file mode 100644 index 00000000..0c970f0e --- /dev/null +++ b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.js @@ -0,0 +1,231 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + const HTTP_PROTOCOL_RGX = /https?:\/\//; + class TabsCtrl { + constructor(applicationsService, $log, $window, conf, userProfileService, $scope,$cookies,$rootScope,confirmBoxService) { + + var counter = 1; + var tabLimit = 6; + this.conf = conf; + var cookieDomain = this.conf.cookieDomain; + $scope.tabs = []; + $scope.notificationShow=true; + $rootScope.showFooter = ""; + console.log("****************** get cookie domain from config json file " + this.conf.cookieDomain); + $cookies.putObject('show_app_header', false,{domain: cookieDomain, path : '/'}); + + + let noRefresh = function () { + window.onbeforeunload = function(e) { + + var isQtoHref = false; + try{ + isQtoHref = e.srcElement.activeElement.href.includes("mailto"); + } catch(err) { + + } + + if ($scope.tabs.length > 1 && isQtoHref == false) { + return "Changes you made may not be saved. Are you sure you want to refresh?"; + } else { + return null; + } + } + } + var addTab = function (title, content) { + if($scope.tabs.length==tabLimit){ + confirmBoxService.showInformation('You have reached your maximum limit of tabs allowed.').then(isConfirmed => {}); + } else { + if(title!=='Home' && content.indexOf('https') == -1){ + console.log('App URL: '+content+'. The application URL you are trying to open is not HTTPS. We recommend to use secured HTTPS URL while on-boarding the application.'); + } + + $scope.tabs.push({ title: title, content: content }); + counter++; + $scope.selectedIndex = $scope.tabs.length - 1; + if ($scope.tabs.length > 1) { + noRefresh(); + } + $cookies.putObject('cookieTabs', $scope.tabs,{domain: cookieDomain, path : '/'}); + $cookies.putObject('visInVisCookieTabs', $scope.tabs,{domain: cookieDomain, path : '/'}); + } + }; + + var adjustTitle = function (title) { + var index = 15; + var nonEmptyCharPattern = /(\s|\w)/; + var adjustedTitle = title.substring(0,index); + var ext = title.charAt(index).replace(nonEmptyCharPattern,'...'); + return adjustedTitle.concat(ext); + + + }; + + var removeTab = function (event, index) { + event.preventDefault(); + event.stopPropagation(); + $scope.tabs.splice(index, 1); + $cookies.putObject('cookieTabs', $scope.tabs,{domain: cookieDomain, path : '/'}); + }; + + var selectTab = function (title) { + if(title=='Home') { + $rootScope.ContentModel.IsVisible=true; + $rootScope.showFooter = true; + $rootScope.tabBottom = 75; + } + else { + $rootScope.ContentModel.IsVisible=false; + $rootScope.showFooter = false; + $rootScope.tabBottom = 0; + } + }; + + $scope.addTab = addTab; + $scope.removeTab = removeTab; + $scope.selectTab = selectTab; + $scope.adjustTitle = adjustTitle; + + + $rootScope.ContentModel = { + IsVisible : false, + ViewUrl : null, + }; + + + var sessionActive = applicationsService.ping() + .then(sessionActive => { + $log.debug('TabsCtrl::addTab: applicationsService.ping() = ' + JSON.stringify(sessionActive)); + + var cookieTabs = $cookies.getObject('cookieTabs'); + if(cookieTabs!=null){ + for(var t in cookieTabs){ + console.log('TabsCtrl::addTab: cookieTabs title: '+cookieTabs[t].title); + if(cookieTabs[t].title!=null && cookieTabs[t].title==='Home'){ + cookieTabs[t].content = ""; + $rootScope.ContentModel.IsVisible=true; + } + + addTab( cookieTabs[t].title, cookieTabs[t].content) ; + } + } else { + for (var i = 0; i < 1; i++) { + var content=""; + var title=""; + if(i==0){ + title="Home"; + $rootScope.ContentModel.IsVisible=true; + } + addTab(title, content); + } + } + + $scope.selectedIndex = 0; + }); + + $scope.$watchCollection(function() { return $cookies.getObject('addTab'); }, function(newValue) { + var tabContent = $cookies.getObject('addTab'); + if(tabContent!=null && tabContent.url!=null){ + var tabExists = false; + for(var x in $scope.tabs){ + console.log($scope.tabs[x].content); + if($scope.tabs[x].title==tabContent.title){ + tabExists = true; + $scope.selectedIndex = x; + } + } + if(!tabExists){ + addTab( tabContent.title, tabContent.url) ; + } + $cookies.remove('addTab'); + } + }); + + + } + + + } + + TabsCtrl.$inject = ['applicationsService', '$log', '$window', 'conf','userProfileService', '$scope','$cookies','$rootScope','confirmBoxService']; + angular.module('ecompApp').controller('TabsCtrl', TabsCtrl); + + angular.module('ecompApp').directive('mainArea', function() { + return { + restrict: "E", + templateUrl: 'app/views/tabs/tabframe.html', + link: function(scope, element) { + + } + } + }); + + + + angular.module('ecompApp').directive('tabHighlight', [function () { + return { + restrict: 'A', + link: function (scope, element) { + var x, y, initial_background = '#c3d5e6'; + + element + .removeAttr('style') + .mousemove(function (e) { + if(!element.hasClass('md-active')) + { + x = e.pageX - this.offsetLeft; + y = e.pageY - this.offsetTop; + + element + .css({ background: '-moz-radial-gradient(circle at ' + x + 'px ' + y + 'px, rgba(255,255,255,0.4) 0px, rgba(255,255,255,0.0) 45px), ' + initial_background }) + .css({ background: '-webkit-radial-gradient(circle at ' + x + 'px ' + y + 'px, rgba(255,255,255,0.4) 0px, rgba(255,255,255,0.0) 45px), ' + initial_background }) + .css({ background: 'radial-gradient(circle at ' + x + 'px ' + y + 'px, rgba(255,255,255,0.4) 0px, rgba(255,255,255,0.0) 45px), ' + initial_background }); + } + }) + .mouseout(function () { + element.removeAttr('style'); + }); + } + }; + }]); + + + +})(); + +function getParameterByName(name, url) { + if (!url) url = window.location.href; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return ''; + if (!results[2]) return ''; + return (results[2].replace(/\+/g, " ")); +} + +function isCascadeFrame(ref) { + if (self != top) { + var e = document.body; + e.parentNode.removeChild(e); + window.location = "unKnownError"; + } +} diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js new file mode 100644 index 00000000..44aea239 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js @@ -0,0 +1,80 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +describe('Controller: TabsCtrl ',() => { + beforeEach(module('ecompApp')); + + beforeEach(inject((_CacheFactory_)=>{ + _CacheFactory_.destroyAll(); + })); + + let TabsCtrl, $controller, $q, $rootScope, $log, $window, $cookies,$scope; + + beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_, _$window_, _$cookies_)=>{ + [$controller, $q, $rootScope, $log, $window, $cookies] = [_$controller_, _$q_, _$rootScope_, _$log_, _$window_, _$cookies_]; + })); + + var deferredApps, deferredUserProfile; + beforeEach(()=>{ + deferredApps = $q.defer(); + deferredUserProfile = $q.defer(); + let applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock', ['getUserApps']); + applicationsServiceMock.getUserApps.and.returnValue(deferredApps.promise); + + let userProfileServiceMock = jasmine.createSpyObj('userProfileServiceMock',['getUserProfile']); + userProfileServiceMock.getUserProfile.and.returnValue(deferredUserProfile.promise); + + $scope = $rootScope.$new(); + TabsCtrl = $controller('TabsCtrl', { + applicationsService: applicationsServiceMock, + $log: $log, + $window: $window, + userProfileService: userProfileServiceMock, + $scope: $scope, + $cookies: $cookies, + $rootScope: $rootScope + }); + }); + + it('should populate this.apps with data from portals service getUserApps', ()=>{ + var profile = {roles:'superAdmin',userId:'userid'}; + deferredUserProfile.resolve(profile); + deferredApps.resolve([{name: 'portal1'},{name: 'portal2'},{name: 'portal3'}]); + $rootScope.$apply(); + expect($scope.appsViewData.length).toBe(3); + }); + + it('should call $log error when getAllPortals fail', ()=>{ + spyOn($log, 'error'); + deferredUserProfile.reject('something happened!'); + $rootScope.$apply(); + expect($log.error).toHaveBeenCalled(); + }); + + it('should open link in a new window when clicking app thumbnail', () => { + spyOn($window, 'open'); + let someUrl = 'http://some/url/'; + TabsCtrl.goToPortal(someUrl); + expect($window.open).toHaveBeenCalledWith(someUrl, '_self'); + }); + + +}); diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.less b/ecomp-portal-FE/client/app/views/tabs/tabs.less new file mode 100644 index 00000000..0247e3d0 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/tabs/tabs.less @@ -0,0 +1,658 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-tabs { + .bg_s; + + position: fixed; + left: 0; + right: 0; + bottom: @footer-height; + overflow-y: hidden; + top: @header-height; + padding-left: 0; + + .go-button { + .btn-green; + width: 96px; + position: absolute; + border-radius: 0px; + } + + .tabs-container { + .content_justify; + position: relative; + padding: 15px 0 32px 0; + width: 100%; + + .tabs-title { + .a24r; + margin: auto; + .content_justify; + } + .portals-list { + min-height: 70vh; + justify-content: center; + flex-flow: row wrap; + width: 1170px; + + margin: auto; + margin-bottom: 63px; + + .app-gridster-header { + background-color: @u; + } + + .app-gridster-footer { + background-color: @u; + } + + .portals-list-item { + background-color: @u; + border-radius: 2px; + box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1); + display: inline-block; + width: 360px; + height: 300px; + background-size: cover; + cursor: pointer; + margin: 15px; + overflow: hidden; + + .portals-item-info { + background-color: @u; + height: 120px; + top: 180px; + position: relative; + box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1); + padding: 16px; + + .info-title { + .a24r; + margin-bottom: 4px; + + text-overflow: ellipsis; + overflow: hidden; + } + .info-description { + .c16r; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .info-button { + .btn-green; + width: 96px; + position: absolute; + bottom: 16px; + left: 16px; + } + + &:hover { + opacity: .93; + z-index: 3; + } + } + } + } + } +} + + +.build-number { + .o12i; +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + + + +.tab-container { + background: #8dc8fb; + margin: 0; + padding: 0; + max-height: 40px; +} +.tab-container ul.nav-tabs { + margin: 0; + list-style-type: none; + line-height: 40px; + max-height: 40px; + overflow: hidden; + display: inline-block; + display: -webkit-flex; + display: flex; + padding-right: 20px; + border-bottom: 5px solid #f7f7f7; +} +.tab-container ul.nav-tabs > li { + margin: 5px -14px 0; + -moz-border-radius-topleft: 28px 145px; + -webkit-border-top-left-radius: 28px 145px; + border-top-left-radius: 28px 145px; + -moz-border-radius-topright: 28px 145px; + -webkit-border-top-right-radius: 28px 145px; + border-top-right-radius: 28px 145px; + padding: 0 30px 0 25px; + height: 170px; + background: #c3d5e6; + position: relative; + -moz-box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5); + -webkit-box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5); + width: 200px; + max-width: 200px; + min-width: 20px; + border: 1px solid #aaa; +} +.tab-container ul.nav-tabs > li:first-child { + margin-left: 0; +} +.tab-container ul.nav-tabs > li:last-of-type { + margin-right: 0; +} +.tab-container ul.nav-tabs > li > a { + display: block; + max-width: 100%; + text-decoration: none; + color: #222; + padding: 3px 7px; +} +.tab-container ul.nav-tabs > li > a span { + overflow: hidden; + white-space: nowrap; + display: block; +} +.tab-container ul.nav-tabs > li > a:focus, .tab-container ul.nav-tabs > li > a:hover { + background-color: transparent; + border-color: transparent; +} +.tab-container ul.nav-tabs > li > a .glyphicon-remove { + color: #777; + display: inline-block; + padding: 3px; + font-size: 10px; + position: absolute; + z-index: 10; + top: 7px; + right: -10px; + -moz-border-radius: 50%; + -webkit-border-radius: 50%; + border-radius: 50%; +} +.tab-container ul.nav-tabs > li > a .glyphicon-remove:hover { + background: #d39ea3; + color: white; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25); + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25); + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); +} +.tab-container ul.nav-tabs > li.active { + z-index: 4; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Y3ZjdmNyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -webkit-gradient(linear, 50% 0%, 50% 30, color-stop(0%, #ffffff), color-stop(100%, #f7f7f7)); + background-image: -moz-linear-gradient(#ffffff, #f7f7f7 30px); + background-image: -webkit-linear-gradient(#ffffff, #f7f7f7 30px); + background-image: linear-gradient(#ffffff, #f7f7f7 30px); +} +.tab-container ul.nav-tabs > li.active > a { + background-color: transparent; + border-color: transparent; + border-bottom-color: transparent; +} +.tab-container ul.nav-tabs > li.active > a:focus, .tab-container ul.nav-tabs > li.active > a:hover { + background-color: transparent; + border-color: transparent; + border-bottom-color: transparent; +} +.tab-container ul.nav-tabs .btn { + float: left; + height: 20px; + width: 35px; + min-width: 35px; + max-width: 35px; + margin: 10px 0 0 0; + border-color: #71a0c9; + outline: none; + -moz-transform: skew(30deg); + -ms-transform: skew(30deg); + -webkit-transform: skew(30deg); + transform: skew(30deg); +} +.tab-container ul.nav-tabs .btn.btn-default { + background: #c3d5e6; +} +.tab-container ul.nav-tabs .btn.btn-default:hover { + background: #d2deeb; +} +.tab-container ul.nav-tabs .btn.btn-default:active { + background: #9cb5cc; +} +.tab-container .tab-pane { + padding: 0px 0px; + text-align: center; +} +.tab-container .tab-pane.active { + border-top: 1px solid #ddd; +} + +.tab-container md-content { + background-color: transparent !important; } + .tab-container md-content md-tabs { + border: 1px solid #e1e1e1; } + .tab-container md-content md-tabs md-tab-content { + background: #f6f6f6; } + .tab-container md-content md-tabs md-tabs-canvas { + background: white; } + .tab-container md-content h1:first-child { + margin-top: 0; } +.tab-container md-input-container { + padding-bottom: 0; } +.tab-container .remove-tab { + margin-bottom: 40px; } +.tab-container .demo-tab > div > div { + padding: 25px; + box-sizing: border-box; } +.tab-container .edit-form input { + width: 100%; } +.tab-container md-tabs { + border-bottom: 1px solid rgba(0, 0, 0, 0.12); } +.tab-container md-tab[disabled] { + opacity: 0.5; } +.tab-container label { + text-align: left; } +.tab-container .long > input { + width: 264px; } +.tab-container .md-button.add-tab { + transform: translateY(5px); } + +.md-tab{ + font-size: 13px; + line-height: 30px; + margin: 5px -3px 0; + border-top-left-radius: 88px 205px; + border-top-right-radius: 88px 205px; + padding: 0 30px 0 25px; + height: 40px; + background: #c3d5e6; + position: relative; + box-shadow: 0 4px 6px rgba(0,0,0,.5); + width: 180px; + max-width: 200px; + min-width: 20px; + border: 1px solid #aaa; + text-transform: capitalize; + text-align: left; +} + +md-tabs .md-tab { + color: #222; +} + +md-tabs-canvas { + border-bottom: 5px solid #f7f7f7; + height: 40px; +} +.md-tab.md-active { + z-index: 4; + background-color: #f5f5f5 !important; +} +.md-tab:first-child{ + margin-left: 10px; +} +md-ink-bar{ + z-index: 5 !important; +} + +.glyphicon { + position: fixed; + line-height: 4; +} +.close_button { + font-size: x-small;width: 10px;margin-left: 130px; +} +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + .close_button { + font-size: x-small;width: 10px;margin-left: 130px;margin-top: 55px; + } +} + +*, +*:after, +*:before +{ + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + + +.button-default +{ + .transition(@transitionDefault color); + background: transparent; + border: none; + cursor: pointer; + margin: 0; + outline: none; + position: relative; +} + +.show-notifications +{ + position: relative; + + &:hover, + &:focus, + &.active + { + #icon-bell + { + fill: @colorWetAsphalt; + } + } + + #icon-bell + { + fill: @colorAsbestos; + } + + + + &.active ~ .notifications + { + opacity: 1; + top: 35px; + } +} +.notifications_li{ + border-top: 1px solid #bdc3c7; + color: #7f8c8d; + cursor: default; + display: block; + padding: 10px; + position: relative; + white-space: nowrap; + width: 350px; +} +.notifications_li:hover{ + background:#eee; +} +.notifications_detail{ + margin-left: 10px; + white-space: normal; + width: 280px; + display: inline-block; + vertical-align: middle; +} + +.notifications_empty{ + display: none; + text-align: center; +} +.notifications_title{ + display: block; +} + +.notifications_date{ + color: #95a5a6; + font-size: .85em; + margin-top: 3px; +} +.notifications +{ + .border-radius(@borderRadius); + .transition(@transitionDefault opacity); + background: @colorClouds; + border: 1px solid @colorSilver; + left: 10px; + opacity: 0; + + &:after + { + border: 10px solid transparent; + border-bottom-color: @colorPeterRiver; + content: ''; + display: block; + height: 0; + left: 10px; + position: absolute; + top: -20px; + width: 0; + } + + h3, + .show-all + { + background: @colorPeterRiver; + color: @colorWhite; + margin: 0; + padding: 10px; + width: 350px; + } + + h3 + { + cursor: default; + font-size: 1.05em; + font-weight: normal; + } + + .show-all + { + display: block; + text-align: center; + text-decoration: none; + + &:hover, + &:focus + { + text-decoration: underline; + } + } + + .notifications-list + { + list-style: none; + margin: 0; + overflow: hidden; + padding: 0; + + .item + { + .transition-transform(@transitionDefault); + border-top: 1px solid @colorSilver; + color: @colorAsbestos; + cursor: default; + display: block; + padding: 10px; + position: relative; + white-space: nowrap; + width: 350px; + + &:before, + .details, + .button-dismiss + { + display: inline-block; + vertical-align: middle; + } + + &:before + { + .border-radius(50%); + background: @colorPeterRiver; + content: ''; + height: 8px; + width: 8px; + } + + .details + { + margin-left: 10px; + white-space: normal; + width: 280px; + + .title, + .date + { + display: block; + } + + .date + { + color: @colorConcrete; + font-size: .85em; + margin-top: 3px; + } + } + + .button-dismiss + { + color: @colorSilver; + font-size: 2.25em; + + &:hover, + &:focus + { + color: @colorConcrete; + } + } + + &.no-data + { + display: none; + text-align: center; + + &:before + { + display: none; + } + } + + &.expired + { + color: @colorSilver; + + &:before + { + background: @colorSilver; + } + + .details + { + .date + { + color: @colorSilver; + } + } + } + + &.dismissed + { + .transform(translateX(100%)); + } + } + } + + &.empty + { + .notifications-list + { + .no-data + { + display: block; + padding: 10px; + } + } + } +} + +/* variables */ +@colorClouds: #ecf0f1; +@colorSilver: #bdc3c7; +@colorWhite: #fefefe; +@colorPeterRiver: #3498db; +@colorConcrete: #95a5a6; +@colorAsbestos: #7f8c8d; +@colorWetAsphalt: #34495e; + +@borderRadius: 2px; + +@transitionDefault: 0.25s ease-out 0.10s; + +/* mixins */ +.background-clip(@value: border-box) +{ + -moz-background-clip: @value; + -webkit-background-clip: @value; + background-clip: @value; +} + +.border-radius(@value: 5px) +{ + -moz-border-radius: @value; + -webkit-border-radius: @value; + border-radius: @value; + .background-clip(padding-box); +} + +.transform(@value) +{ + -webkit-transform: @value; + -moz-transform: @value; + -ms-transform: @value; + -o-transform: @value; + transform: @value; +} + +.transition(@value: all 0.25s ease-out) +{ + -webkit-transition: @value; + -moz-transition: @value; + -o-transition: @value; + transition: @value; +} + +.transition-transform(@transition: 0.25s ease-out) +{ + -webkit-transition: -webkit-transform @transition; + -moz-transition: -moz-transform @transition; + -o-transition: -o-transform @transition; + transition: transform @transition; +} + +.tab-cross-sign{ + position:relative; + margin-left:130px; + font-size:9px; + top:-30px; + @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + top:-70px; + } +} diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.tpl.html b/ecomp-portal-FE/client/app/views/tabs/tabs.tpl.html new file mode 100644 index 00000000..62af73e9 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/tabs/tabs.tpl.html @@ -0,0 +1,46 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-tabs" ng-cloak ng-style="{bottom: tabBottom}"> + <div class="tab-container"> + <md-tabs md-selected="selectedIndex" md-dynamic-height="true" md-autoselect="true"> + <md-tab ng-repeat="tab in tabs" md-on-select="selectTab(tab.title)"> + <md-tab-label> + <span style="overflow: hidden;white-space: nowrap;display: block;" + id="tab-{{tab.title.split(' ').join('-')}}"> + <i tooltip="{{tab.title}}" tooltip-aria-label="Tooltip with HTML Content" + tooltip-style="light" tooltip-placement="right" tooltip-trigger="mouseover">{{adjustTitle(tab.title)}}</i> + </span> + <span class="tab-cross-sign"> + <i class="ion-close-round" ng-click="removeTab($event, $index)" ng-show="'{{tab.title}}'!='Home'"></i> + </span> + </md-tab-label> + <md-tab-body> + <main-area id="tab-home" ng-show="'{{tab.title}}'!='Home'"></main-area> + </md-tab-body> + </md-tab> + </md-tabs> + </div> + <div style=" position: fixed; right: 290px; top: 62px;"> + + </div> + <div ng-controller="searchCtrl" style=" position: fixed; right: 0; top: 58px;"> + <search-box id="mainSearchDiv" /> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/userbar/userbar.controller.js b/ecomp-portal-FE/client/app/views/userbar/userbar.controller.js new file mode 100644 index 00000000..5dae2f86 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/userbar/userbar.controller.js @@ -0,0 +1,262 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; + +(function () { + + class UserbarCtrl { + constructor(userProfileService,userbarUpdateService, $log, $rootScope , $interval,$scope,$timeout,dashboardService) { + this.$log = $log; + this.userProfileService = userProfileService; + this.$rootScope = $rootScope; + $rootScope.isAdminPortalAdmin = false; + $scope.updateRate = 10000; //default online user bar refreshing rate + var intervalPromise = null; + $scope.myservice = userbarUpdateService; + $scope.userList=this.userLists; + var websocket = ''; + var currentUser = ''; + var remoteUser = ''; + var f = ''; + + + function socketSetup(initialPageVisit,_currentUser, _remoteUser, _f) { + + + + if( true) { + + var href = window.location.href; + var hostPatt = new RegExp(window.location.host +"/[^/]*"); + var res = hostPatt.exec(href); + var protocol = window.location.protocol.replace("http","ws"); + var signalingServerPath = protocol + "//" + res + "/opencontact"; + var wsUri = signalingServerPath; + console.log("Connecting to " + wsUri); + websocket = new WebSocket(wsUri); + //localStorage.notifySocket = JSON.stringify(websocket); + //window.top.notifySocket = websocket; + + currentUser = _currentUser; + remoteUser = _remoteUser; + f = socketSend; + + + } + + //var websocket = JSON.parse(localStorage.notifySocket || "{}") ; + if( websocket != null) { + websocket.onopen = function(evt) { onOpen(evt); }; + websocket.onmessage = function(evt) { onMessage(evt); }; + websocket.onerror = function(evt) { onError(evt); }; + + } + + //if(f != undefined) + // f(); + + //window.top.notifySocket.send(""); + } + + function socketSend(currentUser, remoteUser) { + + + + websocket.send(JSON.stringify({ + from: currentUser, + to: remoteUser + })); + + //window.top.notifySocket.send(""); + } + + + + + function onOpen(evt) { + console.log("onOpen"); + //writeToScreen("CONNECTED"); + + if(f != undefined) + f(currentUser, remoteUser); + + } + + function onMessage(evt) { + if (typeof evt.data == "string") { + writeToScreen("RECEIVED (text): " + evt.data); + var chatObject = JSON.parse(evt.data); + if(confirm("User " + chatObject.from + " is requesting a chat session with you. Please click ok to accept")== true) { + + var url = userProfileService.getSortedUserIdCombination(chatObject.from, chatObject.to); + var win = window.open(url, '_blank'); + win.focus(); + } else { + // + } + + + } else { + writeToScreen("RECEIVED (binary): " + evt.data); + } + } + + function onError(evt) { + writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); + } + + function writeToScreen(message) { + console.log(message); + } + + function callAtInterval() { + userProfileService.getActiveUser() + .then(res=> { + $log.info('sasasasasa getting userinfo from shared context.. '); + if(res==null){ + $log.info('failed getting userinfo from shared context.. '); + $log.info('getting userinfo from session '); + }else{ + $log.info('getting ActiveUser successfully',res); + $scope.userList = []; + for(var i=0;i<res.length;i++){ + var data= { + userId:res[i][0], + linkQ:userProfileService.getSortedUserIdCombination(userProfileService.userProfile.userId , res[i][0]) + } + $scope.userList.push(data); + } + $log.info(' ActiveUser =',$scope.userList); + } + }).catch(err=> { + $log.error('Header Controller:: getActiveUser() failed: ' + err); + }).finally(() => { + var footerOff = $('#online-userbar').offset().top; + var headOff = $('#footer').offset().top; + var userbarHeight= parseInt($(".online-user-container").css('height'),10); + var defaultOffSet = 45; + $(".online-user-container").css({ + "height" : headOff - footerOff-defaultOffSet + }); + }); + } + + + var intervalPromise; + dashboardService.getOnlineUserUpdateRate().then(res=> { + // $log.info('getting Online User Update Rate init***********************', res); + if (res == null || res.response == null) { + $log.error('UserbarCtlr: failed to get online user update rate!'); + } else { + // $log.debug('UserbarCtlr: got online User Update Rate ' + res.response); + var rate = parseInt(res.response.onlineUserUpdateRate); + // var updateRate = parseInt(res.response.onlineUserUpdateRate); + var duration = parseInt(res.response.onlineUserUpdateDuration); + userbarUpdateService.setMaxRefreshCount(parseInt(duration/rate)+1); + userbarUpdateService.setRefreshCount(userbarUpdateService.maxCount); + // $scope.refreshCount = userbarUpdateService.getRefreshCount(); + + if (rate != NaN && duration != NaN) { + // $log.debug('UserbarCtlr: scheduling function at interval ' + millis); + $scope.updateRate=rate; + $scope.start($scope.updateRate); + } + } + }).catch(err=> { + $log.error('Header Controller:: getOnlineUserUpdateRate() failed: ' + err); + }); + + $scope.start = function(rate) { + // stops any running interval to avoid two intervals running at the same time + $scope.stop(); + // store the interval promise + intervalPromise = $interval(updateActiveUsers, rate); + }; + + $scope.stop = function() { + $interval.cancel(intervalPromise); + }; + + $scope.$watch('myservice.getRefreshCount()', function (newVal,oldVal) { + //$log.info("refresh "+$scope.myservice.refreshCount + " more time(s)"); + if (newVal<=0) { + // $log.info("UserbarCtrl: stopped refreshing online users"); + $scope.stop(); + } else if (newVal== $scope.myservice.maxCount){ + // $log.info("UserbarCtrl: start refreshing online users at every "+($scope.updateRate/1000)+" sec"); + // initial update of activate users + userProfileService.getUserProfile().then(res=> { + if (res == null || res.orgUserId == null) { + $log.error('UserbarCtlr: failed to get profile of the user!'); + } + else { + updateActiveUsers(); + $scope.start($scope.updateRate); + socketSetup(null, res.orgUserId, null, "socketSend"); + } + }); + + + } + }); + function updateActiveUsers() { + userProfileService.getActiveUser() + .then(res=> { + $log.info('getting Active User init***********************'); + // decrement the counter every time updateActiveUser is called; + userbarUpdateService.decrementRefreshCount(); + + if(res==null ||res.status!='OK'){ + $log.error('failed updateActiveUsers res is null.. '); + + }else{ + $log.info('getting ActiveUser successfully',res); + $scope.userList = []; + var maxItems = 10; + var users = res.response; + if (users.length < maxItems) + maxItems = users.length; + for(var i=0;i<maxItems;i++){ + var data= { + userId:users[i][0], + linkQ:userProfileService.getSortedUserIdCombination(userProfileService.userProfile.orgUserId , users[i][0]) + } + $scope.userList.push(data); + } + + $log.info(' ActiveUser =',$scope.userList); + } + }).catch(err=> { + $log.error('Header Controller:: getActiveUser() failed: ' + err); + }).finally(() => { + var footerOff = $('#online-userbar').offset().top; + var headOff = $('#footer').offset().top; + var userbarHeight= parseInt($(".online-user-container").css('height'),10); + var defaultOffSet = 45; + $(".online-user-container").css({ + "height" : headOff - footerOff-defaultOffSet + }); + }); + } + } + } + UserbarCtrl.$inject = ['userProfileService', 'userbarUpdateService', '$log', '$rootScope','$interval','$scope','$timeout','dashboardService']; + angular.module('ecompApp').controller('UserbarCtrl', UserbarCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/userbar/userbar.less b/ecomp-portal-FE/client/app/views/userbar/userbar.less new file mode 100644 index 00000000..489185f7 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/userbar/userbar.less @@ -0,0 +1,41 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .w-ecomp-sidebar { + + position: relative; + left: 0; + right: 0; + top: 5px; + padding-left: 0; + + } + + @media screen and (-webkit-min-device-pixel-ratio:0) +{ + .w-ecomp-sidebar { + + position: relative; + left: 0; + right: 0; + top: -5px; + padding-left: 0; + + } +}
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/userbar/userbar.tpl.html b/ecomp-portal-FE/client/app/views/userbar/userbar.tpl.html new file mode 100644 index 00000000..dab62910 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/userbar/userbar.tpl.html @@ -0,0 +1,20 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<right-menu id="userbar.userList" user-list="userList" class="w-ecomp-sidebar"></right-menu> diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js new file mode 100644 index 00000000..182ffe8f --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js @@ -0,0 +1,211 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; +(function () { + class NewUserModalCtrl { + constructor($scope, $log, usersService, applicationsService, confirmBoxService) { + let init = () => { + $log.info('NewUserModalCtrl::init'); + this.isSaving = false; + this.anyChanges = false; + this.isGettingAdminApps = false; + if($scope.ngDialogData && $scope.ngDialogData.selectedUser && $scope.ngDialogData.dialogState){ + this.selectedUser = $scope.ngDialogData.selectedUser; + this.dialogState = $scope.ngDialogData.dialogState; + this.isShowBack = false; + if(this.dialogState === 3){ + this.getUserAppsRoles(); + } + }else{ + this.isShowBack = true; + this.selectedUser = null; + this.dialogState = 1; + } + }; + + this.appChanged = (index) => { + let myApp = this.adminApps[index]; + $log.debug('NewUserModalCtrl::appChanged: index: ', index, '; app id: ', myApp.id, 'app name: ',myApp.name); + myApp.isChanged = true; + this.anyChanges = true; + } + + this.deleteApp = (app) => { + let appMessage = this.selectedUser.firstName + ' ' + this.selectedUser.lastName; + confirmBoxService.deleteItem(appMessage).then(isConfirmed => { + if(isConfirmed){ + app.isChanged = true; + this.anyChanges = true; + app.isDeleted = true; + app.appRoles.forEach(function(role){ + role.isApplied = false; + }); + } + }).catch(err => { + $log.error('NewUserModalCtrl::deleteApp error: ',err); + }); + }; + + this.getUserAppsRoles = () => { + if (!this.selectedUser || !this.selectedUser.orgUserId) { + $log.error('NewUserModalCtrl::getUserAppsRoles error: No user is selected'); + this.dialogState = 1; + return; + } + $log.debug('NewUserModalCtrl::getUserAppsRoles: about to call getAdminAppsSimpler'); + this.isGettingAdminApps = true; + applicationsService.getAdminAppsSimpler().then((apps) => { + $log.debug('NewUserModalCtrl::getUserAppsRoles: beginning of then for getAdminAppsSimpler'); + this.isGettingAdminApps = false; + if (!apps || !apps.length) { + $log.error('NewUserModalCtrl::getUserAppsRoles error: no apps found'); + return null; + } + $log.debug('NewUserModalCtrl::getUserAppsRoles: then for getAdminAppsSimpler: step 2'); + $log.debug('NewUserModalCtrl::getUserAppsRoles: admin apps: ', apps); + this.adminApps = apps; + this.dialogState = 3; + this.userAppRoles = {}; + this.numberAppsProcessed = 0; + this.isLoading = true; + apps.forEach(app => { + $log.debug('NewUserModalCtrl::getUserAppsRoles: app: id: ', app.id, 'name: ',app.name); + app.isChanged = false; + app.isLoading = true; + app.isError = false; + app.isDeleted = false; + app.printNoChanges = false; + app.isUpdating = false; + app.isErrorUpdating = false; + app.isDoneUpdating = false; + app.errorMessage = ""; + usersService.getUserAppRoles(app.id, this.selectedUser.orgUserId).then((userAppRolesResult) => { + $log.debug('NewUserModalCtrl::getUserAppsRoles: got a result for app: ',app.id,': ',app.name,': ',userAppRolesResult); + app.appRoles = userAppRolesResult; + app.isLoading = false; + + }).catch(err => { + $log.error(err); + app.isError = true; + app.isLoading = false; + app.errorMessage = err.headers('FEErrorString'); + $log.debug('NewUserModalCtrl::getUserAppsRoles: in new-user.controller: response header: '+err.headers('FEErrorString')); + }).finally(()=>{ + this.numberAppsProcessed++; + if (this.numberAppsProcessed == this.adminApps.length) { + this.isLoading = false; + } + }); + }) + return; + }).catch(err => { + $log.error(err); + }) + + } + + this.getAdminApps = () => { + if (!this.selectedUser || !this.selectedUserorgUserId) { + $log.error('NewUserModalCtrl::getAdminApps: No user is selected'); + this.dialogState = 1; + return; + } + applicationsService.getAdminApps().promise().then(apps => { + if (!apps || !apps.length) { + $log.error('NewUserModalCtrl::getAdminApps: no apps found'); + return null; + } + $log.debug('NewUserModalCtrl::getAdminApps: admin apps: ', apps); + this.adminApps = apps; + this.dialogState = 3; + return; + }).catch(err => { + $log.error('NewUserModalCtrl::getAdminApps: ', err); + }) + + } + + this.updateUserAppsRoles = () => { + $log.debug('NewUserModalCtrl::updateUserAppsRoles: entering updateUserAppsRoles'); + if(!this.selectedUser || !this.selectedUser.orgUserId || !this.adminApps){ + $log.debug('NewUserModalCtrl::updateUserAppsRoles: returning early'); + return; + } + this.isSaving = true; + $log.debug('NewUserModalCtrl::updateUserAppsRoles: going to update user: ' + this.selectedUser.orgUserId); + this.numberAppsProcessed = 0; + this.numberAppsSucceeded = 0; + this.adminApps.forEach(app => { + if (app.isChanged) { + $log.debug('NewUserModalCtrl::updateUserAppsRoles: app roles have changed; going to update: id: ', app.id, '; name: ', app.name); + app.isUpdating = true; + usersService.updateUserAppRoles({orgUserId: this.selectedUser.orgUserId, appId: app.id, appRoles: app.appRoles}) + .then(res => { + $log.debug('NewUserModalCtrl::updateUserAppsRoles: User app roles updated successfully on app: ',app.id); + app.isUpdating = false; + app.isDoneUpdating = true; + this.numberAppsSucceeded++; + }).catch(err => { + $log.error(err); + app.isErrorUpdating = true; + }).finally(()=>{ + this.numberAppsProcessed++; + if (this.numberAppsProcessed == this.adminApps.length) { + this.isSaving = false; + } + if (this.numberAppsSucceeded == this.adminApps.length) { + $scope.closeThisDialog(true); + } + }) + } else { + $log.debug('NewUserModalCtrl::updateUserAppsRoles: app roles have NOT changed; NOT going to update: id: ', app.id, '; name: ', app.name); + app.noChanges = true; + app.isError = false; + this.numberAppsProcessed++; + this.numberAppsSucceeded++; + if (this.numberAppsProcessed == this.adminApps.length) { + this.isSaving = false; + } + if (this.numberAppsSucceeded == this.adminApps.length) { + $scope.closeThisDialog(true); + } + } + }); + }; + + this.navigateBack = () => { + if (this.dialogState === 1) { + } + if (this.dialogState === 3) { + this.dialogState = 1; + } + }; + + init(); + + $scope.$on('$stateChangeStart', e => { + e.preventDefault(); + }); + } + } + NewUserModalCtrl.$inject = ['$scope', '$log', 'usersService', 'applicationsService', 'confirmBoxService']; + angular.module('ecompApp').controller('NewUserModalCtrl', NewUserModalCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js new file mode 100644 index 00000000..54c564b7 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js @@ -0,0 +1,222 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; + +describe('Controller: NewUserModalCtrl ', () => { + beforeEach(module('testUtils')); + beforeEach(module('ecompApp')); + + let promisesTestUtils; + beforeEach(inject((_CacheFactory_, _promisesTestUtils_)=> { + _CacheFactory_.destroyAll(); + promisesTestUtils = _promisesTestUtils_; + })); + + let newUser, $controller, $q, $rootScope, $log, $scope; + + let applicationsServiceMock, usersServiceMock, confirmBoxServiceMock; + let deferredAdminApps, deferredUsersAccounts, deferredUsersAppRoles, deferredUsersAppRoleUpdate; + + beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_)=> { + $rootScope = _$rootScope_; + $q = _$q_; + $controller = _$controller_; + $log = _$log_; + })); + + beforeEach(()=> { + [deferredAdminApps, deferredUsersAccounts, deferredUsersAppRoles, deferredUsersAppRoleUpdate] = [$q.defer(),$q.defer(), $q.defer(), $q.defer()]; + + /*applicationsServiceMock = { + getAdminApps: () => { + var promise = () => {return deferredAdminApps.promise}; + var cancel = jasmine.createSpy(); + return { + promise: promise, + cancel: cancel + } + } + };*/ + + confirmBoxServiceMock = { + deleteItem: () => { + var promise = () => {return deferredAdminApps.promise}; + var cancel = jasmine.createSpy(); + return { + promise: promise, + cancel: cancel + } + } + }; + + applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock', ['getAdminAppsSimpler']); + applicationsServiceMock.getAdminAppsSimpler.and.returnValue(deferredAdminApps.promise); + + usersServiceMock = jasmine.createSpyObj('usersServiceMock', ['getAccountUsers','getUserAppRoles','updateUserAppsRoles']); + + usersServiceMock.getAccountUsers.and.returnValue(deferredUsersAccounts.promise); + usersServiceMock.getUserAppRoles.and.returnValue(deferredUsersAppRoles.promise); + usersServiceMock.updateUserAppsRoles.and.returnValue(deferredUsersAppRoleUpdate.promise); + + $scope = $rootScope.$new(); + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + }); + + it('should open modal window without user when no user is selected', ()=> { + expect(newUser.selectedUser).toBe(null); + }); + + it('should open modal window with selectedUser apps roles when user is selected', ()=> { + let roles = {apps: [{id: 1, appRoles: [{id: 3, isApplied: true}]}]}; + let someUser = {userId: 'asdfjl'}; + + deferredUsersAppRoles.resolve(roles); + deferredAdminApps.resolve(roles.apps); + + $scope.ngDialogData = { + selectedUser: someUser, + dialogState: 2 + }; + + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + + newUser.getUserAppsRoles(); + $scope.$apply(); + + expect(newUser.selectedUser).toBe(someUser); + expect(newUser.adminApps).toEqual(roles.apps); + }); + + it('should push to apps order list only apps that has applied roles when initializing', () => { + let roles = {apps: [{appId: 13, appRoles: [{id: 3, isApplied: true}]},{appId: 20, appRoles: [{id: 3, isApplied: false}]}]}; + let someUser = {userId: 'asdfjl'}; + + deferredUsersAppRoles.resolve(roles); + + $scope.ngDialogData = { + selectedUser: someUser, + dialogState: 2 + }; + + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + + $scope.$apply(); + + }); + + it('should push app to apps order list when applying at least one role to user from app', () => { + let roles = {apps: [{appId: 13, appRoles: [{id: 3, isApplied: true}]},{appId: 20, appRoles: [{id: 3, isApplied: false}]}]}; + let someUser = {userId: 'asdfjl'}; + + deferredUsersAppRoles.resolve(roles); + + $scope.ngDialogData = { + selectedUser: someUser, + dialogState: 2 + }; + + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + + $scope.$apply(); + + }); + + + it('should remove app from list when removing all user roles in it', () => { + let roles = {apps: [{appName: 'aaa', appId: 13, appRoles: [{id: 3, isApplied: true}]},{appName: 'vvv', appId: 20, appRoles: [{id: 3, isApplied: true}]}]}; + let someUser = {userId: 'asdfjl'}; + + promisesTestUtils.resolvePromise(confirmBoxServiceMock, 'deleteItem', true); + + deferredUsersAppRoles.resolve(roles); + + $scope.ngDialogData = { + selectedUser: someUser, + dialogState: 2 + }; + + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + + $scope.$apply(); + newUser.deleteApp(roles.apps[0]); + $scope.$apply(); + + }); + + it('should close the modal when update changes succeeded', () => { + let roles = {apps: [{appName: 'aaa', appId: 13, appRoles: [{id: 3, isApplied: true}]},{appName: 'vvv', appId: 20, appRoles: [{id: 3, isApplied: true}]}]}; + let someUser = {userId: 'asdfjl'}; + deferredUsersAppRoles.resolve(roles); + deferredUsersAppRoleUpdate.resolve(); + deferredAdminApps.resolve(roles.apps); + + $scope.ngDialogData = { + selectedUser: someUser, + dialogState: 2 + }; + + newUser = $controller('NewUserModalCtrl', { + $scope: $scope, + $log: $log, + usersService: usersServiceMock, + applicationsService: applicationsServiceMock, + confirmBoxService: confirmBoxServiceMock + }); + $scope.closeThisDialog = function(){}; + spyOn($scope, 'closeThisDialog'); + + newUser.getUserAppsRoles(); + $scope.$apply(); + newUser.updateUserAppsRoles(); + $scope.$apply(); + expect($scope.closeThisDialog).toHaveBeenCalledWith(true); + }); + }); diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html new file mode 100644 index 00000000..e50c9d4a --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html @@ -0,0 +1,70 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="new-user-modal"> + <div class="search-users" ng-show="newUser.dialogState===1"> + <search-users search-title="New User" + selected-user="newUser.selectedUser"></search-users> + <div class="dialog-control"> + <div id="search-user-next-button" class="next-button" ng-click="newUser.selectedUser && newUser.getUserAppsRoles()" + ng-class="{disabled: !newUser.selectedUser}">Next + </div> + <div id="search-user-cancel-button" class="cancel-button" ng-click="closeThisDialog()">Cancel</div> + </div> + </div> + <div class="user-apps-roles" ng-show="newUser.dialogState===3"> + <div class="title" + ng-bind="newUser.selectedUser.firstName + ' ' + newUser.selectedUser.lastName + ' (' + newUser.selectedUser.orgUserId + ')'"></div> + <div class="app-roles-main"> + <div class="app-roles-main-title"> + <span class="left">Access and roles:</span> + </div> + <div class="app-roles-list"> + <div class="app-item" ng-repeat="app in (newUser.adminApps) track by app.id" ng-show="!app.isDeleted"> + <div class="app-item-left" id="div-app-name-{{app.name.split(' ').join('-')}}">{{app.name | elipsis: 27}}</div> + <div class="app-item-right" ng-show="!app.isError && !app.isLoading && !app.noChanges && !app.isUpdating && !app.isDoneUpdating && !app.isErrorUpdating"> + <multiple-select id="app-roles" + unique-data="{{$index}}" + placeholder="Select roles" + ng-model="app.appRoles" + on-change="newUser.appChanged($index)" + name-attr="roleName" + value-attr="isApplied"></multiple-select> + </div> + <div id="app-item-no-contact" class="app-item-right-error" ng-show="app.isError">{{app.errorMessage}}</div> + <div id="app-item-contacting" class="app-item-right-contacting" ng-show="app.isLoading">Contacting application...</div> + <div id="app-item-no-changes" class="app-item-right-contacting" ng-show="app.noChanges">No changes</div> + <div id="app-item-no-updating" class="app-item-right-contacting" ng-show="app.isUpdating">Updating application...</div> + <div id="app-item-done-updating" class="app-item-right-contacting" ng-show="app.isDoneUpdating">Finished updating application</div> + <div id="app-item-cannot-update" class="app-item-right-error" ng-show="app.isErrorUpdating">Could not update application...</div> + <i id="app-item-delete" class="ion-trash-b" ng-click="newUser.deleteApp(app)" ng-show="!app.isLoading && !app.isError"></i> + <div id='ecomp-small-spinner' class="ecomp-small-spinner" ng-show="app.isLoading"></div> + </div> + </div> + <div class="dialog-control"> + <span id="ecomp-save-spinner" class="ecomp-save-spinner" ng-show="newUser.isSaving || newUser.isGettingAdminApps"></span> + <div id="new-user-back-button" ng-show="newUser.isShowBack" class="back-button" ng-click="newUser.navigateBack()">Back</div> + <div id="new-user-next-button" class="next-button" ng-click="newUser.updateUserAppsRoles()" + ng-class="{disabled: !newUser.anyChanges}">Save + </div> + <div id="new-user-cancel-button" class="cancel-button" ng-click="closeThisDialog()">Cancel</div> + </div> + </div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less new file mode 100644 index 00000000..9f86b022 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less @@ -0,0 +1,126 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .new-user-modal { + display:block; + overflow:auto; + min-height: 450px; + + .user-apps-roles{ + .title{ + .n18r; + border-bottom: @a 3px solid; + } + + .app-roles-list{ + height: 286px; + overflow-y: auto; + + .app-item{ + border: 1px solid #d8d8d8; + border-radius: 2px; + background-color: #f8f8f8; + + padding: 10px; + margin-top: 8px; + + .app-item-left{ + padding-top: 0; + line-height: 30px; + height: 30px; + vertical-align: middle; + display:inline-block; + width: 45%; + border-radius: 2px; + border: 1px solid #d8d8d8; + margin-right: 10px; + padding-left: 4px; + background: #fff; + white-space: nowrap; + + } + .app-item-right{ + display:inline-block; + width: 45%; + border-radius: 2px; + border: 1px solid #d8d8d8; + background: #fff; + vertical-align: middle; + } + + .app-item-right-error{ + .k; + padding: 7px 7px 7px 7px; + display:inline-block; + width: 45%; + border-radius: 2px; + border: 1px solid #d8d8d8; + background: #fff; + vertical-align: middle; + } + + .app-item-right-contacting{ + .e; + padding: 7px 7px 7px 7px; + display:inline-block; + width: 45%; + border-radius: 2px; + border: 1px solid #d8d8d8; + background: #fff; + vertical-align: middle; + } + + .app-select-left{ + width: 45%; + margin-right: 10px; + vertical-align: middle; + + + .select-field{ + padding-top: 0; + line-height: 30px; + height: 30px; + vertical-align: middle; + border-radius: 2px; + border: 1px solid #d8d8d8; + margin-right: 10px; + padding-left: 4px; + background: #fff; + display:inline-block; + } + } + + + .app-item-delete{ + .ico_trash_default; + display: inline-block; + vertical-align: 2px; + cursor: pointer; + position: relative; + top: 6px; + color: transparent; + margin-left: 8px; + + } + + } + } + + } +} diff --git a/ecomp-portal-FE/client/app/views/users/users.controller.js b/ecomp-portal-FE/client/app/views/users/users.controller.js new file mode 100644 index 00000000..433b24e4 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/users.controller.js @@ -0,0 +1,187 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class UsersCtrl { + constructor($log, applicationsService, usersService, $scope, ngDialog,$timeout) { + this.$log = $log; + $scope.adminAppsIsNull = false; + $scope.appsIsDown = false; + + $log.info('UsersCtrl:: initializing...'); + let activeRequests = []; + let clearReq = (req) => { + activeRequests.splice(activeRequests.indexOf(req), 1); + }; + + let getAdminApps = () => { + $log.debug('UsersCtrl::getAdminApps: - Starting getAdminApps'); + try { + this.isLoadingTable = true; + var adminAppsReq = applicationsService.getAdminApps(); + + activeRequests.push(adminAppsReq); + adminAppsReq.promise().then(apps => { + $log.debug('UsersCtrl::getAdminApps: Apps for this user are: ' + JSON.stringify(apps)); + $log.debug('UsersCtrl::getAdminApps: Apps length: ' + apps.length); + var res1 = apps.sort(getSortOrder("name")); + if (!res1 || !res1.length) { + $log.error('UsersCtrl::getAdminApps: - no apps found'); + return null; + } + for (let i = 0; i < apps.length; i++) { + res1[i].index = i; + res1[i].value = apps[i].name; + res1[i].title = apps[i].name; + } + + this.adminApps = apps; + this.selectedApp = apps[0]; + clearReq(adminAppsReq); + $scope.adminAppsIsNull = false; + }).catch(e => { + $scope.adminAppsIsNull = true; + $log.error('UsersCtrl::getAdminApps: - getAdminApps() failed = '+ e.message); + clearReq(adminAppsReq); + }).finally(() => { + this.isLoadingTable = false; + }); + } catch (e) { + $scope.adminAppsIsNull = true; + $log.error('UsersCtrl::getAdminApps: - getAdminApps() failed!'); + this.isLoadingTable = false; + } + }; + + let getSortOrder = (prop) => { + return function(a, b) { + if (a[prop] > b[prop]) { + return 1; + } else if (a[prop] < b[prop]) { + return -1; + } + return 0; + } + } + + this.updateUsersList = () => { + $scope.appsIsDown = false; + $log.debug('UsersCtrl::updateUsersList: Starting updateUsersList'); + this.searchString = ''; + this.isAppSelectDisabled = true; + this.isLoadingTable = true; + usersService.getAccountUsers(this.selectedApp.id) + .then(accountUsers => { + $log.debug('UsersCtrl::updateUsersList length: '+ Object.keys(accountUsers).length); + this.isAppSelectDisabled = false; + this.accountUsers = accountUsers; + }).catch(err => { + this.isAppSelectDisabled = false; + $log.error('UsersCtrl::updateUsersList: ' + err); + $scope.appsIsDown = true; + }).finally(() => { + this.isLoadingTable = false; + }); + }; + + + let init = () => { + this.isLoadingTable = false; + this.selectedApp = null; + this.isAppSelectDisabled = false; + getAdminApps(); + + this.searchString = ''; + this.usersTableHeaders = ['First Name', 'Last Name', 'User ID', 'Roles']; + this.accountUsers = []; + }; + + this.openAddNewUserModal = (user) => { + let data = null; + if (user) { + data = { + dialogState: 3, + selectedUser: { + orgUserId: user.orgUserId, + firstName: user.firstName, + lastName: user.lastName + } + } + } + ngDialog.open({ + templateUrl: 'app/views/users/new-user-dialogs/new-user.modal.html', + controller: 'NewUserModalCtrl', + controllerAs: 'newUser', + data: data + }).closePromise.then(needUpdate => { + if (needUpdate.value === true) { + $log.debug('UsersCtrl::openAddNewUserModal updating table data...'); + this.updateUsersList(); + } + }); + }; + + this.openEditUserModal = (loginId) => { + var data = { + loginId : loginId, + updateRemoteApp : true, + appId : this.selectedApp!=null?this.selectedApp.id:'' + } + var modalInstance = ngDialog.open({ + templateUrl: 'app/views/header/user-edit/edit-user.tpl.html', + controller: 'editUserController', + data: data, + resolve: { + message: function message() { + var message = { + type: 'Contact', + }; + return message; + } + } + }).closePromise.then(needUpdate => { + //update selected app's database for this user. + console.log("'''''''''''''''''' now updating user list after update remote server"); + $timeout(this.updateUsersList, 1500); + }); + } + + + $scope.$watch('users.selectedApp', (newVal, oldVal) => { + if (!newVal || _.isEqual(newVal, oldVal)) { + return; + } + $log.debug('UsersCtrl::openAddNewUserModal:$watch selectedApp -> Fire with: ', newVal); + this.accountUsers = []; + this.updateUsersList(); + }); + + $scope.$on('$destroy', () => { + activeRequests.forEach(req => { + req.cancel(); + }); + }); + + init(); + } + } + UsersCtrl.$inject = ['$log', 'applicationsService', 'usersService', '$scope', 'ngDialog','$timeout']; + angular.module('ecompApp').controller('UsersCtrl', UsersCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/users/users.controller.spec.js b/ecomp-portal-FE/client/app/views/users/users.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/users.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/users/users.less b/ecomp-portal-FE/client/app/views/users/users.less new file mode 100644 index 00000000..ff2d815f --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/users.less @@ -0,0 +1,59 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .users-page-main{ + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + .users-table { + width: @table-width; + margin: @table-margin; + + } + + .error-text { + width: 1170px; + margin: auto; + padding: 20px; + left: 20px; + font-weight: bold; + font-size: 16px; + text-align: left; + color: @err; + background-color: @u; + + .error-help { + color: @o; + font-weight: normal; + } + + .error-help-bold { + color: @o; + font-weight: bold; + } + + } +} diff --git a/ecomp-portal-FE/client/app/views/users/users.tpl.html b/ecomp-portal-FE/client/app/views/users/users.tpl.html new file mode 100644 index 00000000..88a3b62d --- /dev/null +++ b/ecomp-portal-FE/client/app/views/users/users.tpl.html @@ -0,0 +1,105 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-main"> + <div class="w-ecomp-main-container"> + <div class="users-page-main" id="contentId"> + <div id="users-page-title" class="w-ecomp-main-view-title">Users</div> + <div class="users-table"> + + + <div class="table-control"> + <div class="c-ecomp-att-abs-select default"> + <div class="form-field" id="form-field" + att-select="users.adminApps" + ng-model="users.selectedApp" + ng-disabled="users.isAppSelectDisabled" + ng-class="{disabled: users.isAppSelectDisabled}"></div> + </div> + <input class="table-search" type="text" id="input-search" + placeholder="Search in entire table" + ng-model="users.searchString"/> + + <div id="users-page-button-add" class="add-button" ng-click="users.openAddNewUserModal()">Add User</div> + </div> + <div ng-hide="users.isLoadingTable"> + <div class="error-text" + id="div-error-app-down" + ng-show="appsIsDown===true"> + <h1 class="error-help-bold">Attention:</h1> + <p> </p> + <p class="error-help">Select "Add User" in order to add User and Roles to the '{{users.selectedApp.name}}' Application.</p> + </div> + </div> + <span class="ecomp-spinner" ng-show="users.isLoadingTable"></span> + <div class="c-ecomp-att-abs-table default" ng-hide="users.isLoadingTable"> + <table att-table id="table-main" + table-data="users.accountUsers" + search-string="users.searchString" + view-per-page="users.viewPerPageIgnored" + current-page="users.currentPageIgnored" + total-page="users.totalPageIgnored"> + <thead att-table-row type="header"> + <tr> + <th id="th-users-0" att-table-header key="firstName" default-sort="a">{{users.usersTableHeaders[0]}}</th> + <th id="th-users-1" att-table-header key="lastName" sortable="true">{{users.usersTableHeaders[1]}}</th> + <th id="th-users-2" att-table-header key="userId" sortable="true">{{users.usersTableHeaders[2]}}</th> + <th id="th-users-3" att-table-header key="roles" sortable="false">{{users.usersTableHeaders[3]}}</th> + </tr> + </thead> + <tbody att-table-row type="body" + class="table-body" + track-by="$index" + row-repeat="rowData in users.accountUsers"> + <tr id="tr-rowData" ng-click="users.openAddNewUserModal(rowData)"> + <td class="td-first" att-table-body> + <div id="users-page-td-firstName-{{rowData.userId}}" ng-bind="rowData.firstName"></div> + </td> + <td att-table-body> + <div id="users-page-td-lastName-{{rowData.userId}}" ng-bind="rowData.lastName"></div> + </td> + <td att-table-body> + <div id="users-page-td-userId-{{rowData.userId}}" style="float: left;" ng-bind="rowData.orgUserId"></div> + <div> + <span style="float: left; margin-left:15px" class="ion-person" ng-click="users.openEditUserModal(rowData.orgUserId);$event.stopPropagation()"></span> + </div> + </td> + <td> + <div id="users-page-td-role-name-{{rowData.userId}}" ng-repeat="role in rowData.roles" ng-bind="role.name"></div> + </td> + </tr> + </tbody> + </table> + </div> + + </div> + <div class="error-text" + id="div-error-403" + ng-show="adminAppsIsNull==true"> + <h1>Attention:</h1> + <p> </p> + <p class="error-help">It appears that you have not been added as an admin yet to an application.</p> + <p> </p> + <p class="error-help">Click on the Admins link to the left and check and see if you are listed as an admin for an application. + If not, you can add yourself to the appropriate application.</p> + </div> + </div> + </div> + +</div> diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.js b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.js new file mode 100644 index 00000000..f50e4683 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.js @@ -0,0 +1,202 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + +'use strict'; +(function () { + class WidgetDetailsModalCtrl { + constructor($scope, $log, applicationsService, widgetsService, errorMessageByCode, ECOMP_URL_REGEX, $window,userProfileService,$cookies) { + + let newWidgetModel = { + name: null, + appId: null, + appName: null, + width: 360, + height: 300, + url: null + }; + + let getAvailableApps = () => { + applicationsService.getAppsForSuperAdminAndAccountAdmin().then(apps => { + this.availableApps=[]; + for(var i=0;i<apps.length;i++) { + if (!apps[i].restrictedApp) { + $log.debug('WidgetDetailsModalCtrl::getAvailableApps: pushing {id: ', apps[i].id, 'name: ', apps[i].name, + 'restrictedApp: ', apps[i].restrictedApp, '}'); + this.availableApps.push({ + id: apps[i].id, + name: apps[i].name, + restrictedApp: apps[i].restrictedApp + }); + } + } + + if (this.isEditMode) { + this.selectedApp = _.find(apps, {id: this.widget.appId}); + if(!this.selectedApp){ + $scope.widgetForm.app.$dirty = true; + } + } else { + this.selectedApp = null; + } + this.updateSelectedApp(); + }).catch(err => { + $log.error(err); + }); + }; + /**/ + + let init = () => { + $log.info('AppDetailsModalCtrl::init'); + this.isSaving = false; + if ($scope.ngDialogData && $scope.ngDialogData.widget) { + $log.debug('WidgetDetailsModalCtrl::getAvailableApps: Edit widget mode for', $scope.ngDialogData.widget); + this.isEditMode = true; + this.widget = _.clone($scope.ngDialogData.widget); + } else { + $log.debug('WidgetDetailsModalCtrl::init: New app mode'); + this.isEditMode = false; + this.widget = _.clone(newWidgetModel); + } + getAvailableApps(); + }; + + this.ECOMP_URL_REGEX = ECOMP_URL_REGEX; + + this.conflictMessages = {}; + this.scrollApi = {}; + let handleConflictErrors = err => { + if(!err.data){ + return; + } + if(!err.data.length){ + err.data = [err.data] + } + _.forEach(err.data, item => { + _.forEach(item.fields, field => { + this.conflictMessages[field.name] = errorMessageByCode[item.errorCode]; + $scope.widgetForm[field.name].$setValidity('conflict', false); + watchOnce[field.name](); + }); + }); + this.scrollApi.scrollTop(); + }; + + let resetConflict = fieldName => { + delete this.conflictMessages[fieldName]; + if($scope.widgetForm[fieldName]){ + $scope.widgetForm[fieldName].$setValidity('conflict', true); + } + }; + + let watchOnce = { + name: () => { + let unregisterName = $scope.$watchGroup(['widgetDetails.selectedApp','widgetDetails.widget.name'], (newVal, oldVal) => { + if(newVal.toLowerCase() !== oldVal.toLowerCase()){ + resetConflict('name'); + unregisterName(); + } + }); + }, + url: () => { + let unregisterUrl = $scope.$watch('widgetDetails.widget.url', (newVal, oldVal) => { + if(newVal.toLowerCase() !== oldVal.toLowerCase()) { + resetConflict('url'); + unregisterUrl(); + } + }); + } + }; + + this.updateSelectedApp = () => { + if (!this.selectedApp) { + return; + } + this.widget.appId = this.selectedApp.id; + this.widget.appName = this.selectedApp.name; + }; + + let emptyCookies = () => { + userProfileService.getUserProfile() + .then(profile=> { + $log.info('AppDetailsModalCtrl::emptyCookies profile: ', profile); + $scope.userId = profile.orgUserId; + $log.info('user has the following userId: ' + profile.userId); + if ($cookies.getObject($scope.userId + '_widget') != undefined && $cookies.getObject($scope.userId + '_widget') != null) { + $cookies.remove($scope.userId + '_widget'); + } + }); + }; + + this.saveChanges = () => { + if($scope.widgetForm.$invalid){ + return; + } + this.isSaving = true; + if(this.isEditMode){ + widgetsService.updateWidget(this.widget.id, this.widget) + .then(() => { + $log.debug('WidgetDetailsModalCtrl::saveChanges: Widget update succeeded!'); + $scope.closeThisDialog(true); + emptyCookies(); + }).catch(err => { + if(err.status === 409){ + handleConflictErrors(err); + } + $log.error(err); + }).finally(()=>{ + this.isSaving = false; + var objOffsetVersion = objAgent.indexOf("MSIE"); + if (objOffsetVersion != -1) { + $log.debug('WidgetDetailsModalCtrl::saveChanges: Browser is IE, forcing Refresh'); + $window.location.reload(); + } + }); + }else{ + widgetsService.createWidget(this.widget) + .then(() => { + $log.debug('WidgetDetailsModalCtrl::createWidget: Widget creation succeeded!'); + $scope.closeThisDialog(true); + emptyCookies(); + }).catch(err => { + if(err.status === 409){ + handleConflictErrors('WidgetDetailsModalCtrl::createWidget error: ',err); + } + $log.error('WidgetDetailsModalCtrl::createWidget error: ',err); + }).finally(()=>{ + this.isSaving = false; + var objOffsetVersion = objAgent.indexOf("MSIE"); + if (objOffsetVersion != -1) { + $log.debug('WidgetDetailsModalCtrl::createWidget: Browser is IE, forcing Refresh'); + $window.location.reload(); + } + }); + } + }; + + init(); + + $scope.$on('$stateChangeStart', e => { + e.preventDefault(); + }); + } + } + WidgetDetailsModalCtrl.$inject = ['$scope', '$log', 'applicationsService', 'widgetsService', 'errorMessageByCode', 'ECOMP_URL_REGEX', '$window','userProfileService','$cookies']; + angular.module('ecompApp').controller('WidgetDetailsModalCtrl', WidgetDetailsModalCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.html b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.html new file mode 100644 index 00000000..87be6f15 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.html @@ -0,0 +1,132 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="widget-details-modal"> + <div id="'widgets-details-title" class="title">Widget Details</div> + <div class="widget-properties-main" scroll-top="widgetDetails.scrollApi"> + <form id="widgets-details-form" name="widgetForm" novalidate autocomplete="off"> + <script type="text/javascript"> + document.getElementById("appForm").reset(); + </script> + <div class="item required"> + <div class="item-label">Application Name</div> + <div class="custom-select-wrap"> + <select id="widgets-details-select-app" + class="select-field" + ng-model="widgetDetails.selectedApp" + ng-change="widgetDetails.updateSelectedApp();" + ng-options="app.name for app in widgetDetails.availableApps track by app.id" + ng-disabled="!widgetDetails.availableApps || !widgetDetails.availableApps.length" + name="app" + required> + <option id="widgets-details-select-app-disabled" value="" disabled style="display: none;">Select application</option> + </select> + </div> + <div class="error-container" ng-show="widgetForm.app.$dirty"> + <div ng-messages="widgetForm.app.$error" class="error-container"> + <small id="widgets-details-select-app-error-required" class="err-message" ng-message="required">Application is required</small> + </div> + </div> + </div> + <div class="item required"> + <div class="item-label">Widget Name</div> + <input id="widgets-details-input-name" + class="input-field" + type="text" + ng-model="widgetDetails.widget.name" + name="name" + maxlength="100" + ng-pattern="/^[a-zA-Z0-9_\s\&]*$/" + required/> + <div class="error-container" ng-show="widgetDetails.conflictMessages.name"> + <small id="widgets-details-input-name-conflict" class="err-message" ng-bind="widgetDetails.conflictMessages.name"></small> + </div> + <div class="error-container" ng-show="widgetForm.name.$dirty || widgetDetails.isEditMode"> + <div ng-messages="widgetForm.name.$error" class="error-container"> + <small id="widgets-details-input-name-required" class="err-message" ng-message="required">Widget Name is required</small> + <small id="widgets-details-input-name-pattern" class="err-message" ng-message="pattern">Widget Name must be letters, numbers, or underscore</small> + </div> + </div> + </div> + <div class="item required"> + <div class="left-item"> + <div class="item-label">Width</div> + <input id="widgets-details-input-width" + class="input-field" + type="number" + ng-model="widgetDetails.widget.width" + name="width" + min="300" + required + disabled/> + <div class="error-container" ng-show="widgetForm.width.$dirty || widgetDetails.isEditMode"> + <div ng-messages="widgetForm.width.$error" class="error-container"> + <small id="widgets-details-input-width-required" class="err-message" ng-message="required">Widget width is required</small> + <small id="widgets-details-input-min-width" class="err-message" ng-message="min">Minimum width is 300</small> + </div> + </div> + </div> + <div class="right-item required"> + <div class="item-label">Height</div> + <input id="widgets-details-input-height" + class="input-field" + type="number" + ng-model="widgetDetails.widget.height" + name="height" + min="200" + required + disabled/> + <div class="error-container" ng-show="widgetForm.height.$dirty || widgetDetails.isEditMode"> + <div ng-messages="widgetForm.height.$error" class="error-container"> + <small id="widgets-details-input-height-required" class="err-message" ng-message="required">Widget height is required</small> + <small id="widgets-details-input-height-minimum" class="err-message" ng-message="min">Minimum height is 200</small> + </div> + </div> + </div> + </div> + <div class="item required"> + <div class="item-label">URL</div> + <input id="widgets-details-input-URL" + class="input-field" + type="url" + ng-model="widgetDetails.widget.url" + name="url" + maxlength="256" + ng-pattern="widgetDetails.ECOMP_URL_REGEX" + required/> + <div class="error-container" ng-show="widgetDetails.conflictMessages.url"> + <small id="widgets-details-input-URL-conflict" class="err-message" ng-bind="widgetDetails.conflictMessages.url"></small> + </div> + <div class="error-container" ng-show="widgetForm.url.$dirty || widgetDetails.isEditMode"> + <div ng-messages="widgetForm.url.$error" class="error-container"> + <small id="widgets-details-input-URL-required" class="err-message" ng-message="required">Widget URL is required</small> + <small id="widgets-details-input-URL-pattern" class="err-message" ng-message="pattern">Incorrect URL pattern</small> + </div> + </div> + + </div> + </form> + </div> + <div class="dialog-control"> + <span class="ecomp-save-spinner" ng-show="widgetDetails.isSaving"></span> + <div id="widgets-details-next-button" class="next-button" + ng-class="{disabled: widgetForm.$invalid}" ng-click="widgetDetails.saveChanges()">Save</div> + <div id="widgets-details-cancel-button" class="cancel-button" ng-click="closeThisDialog()">Cancel</div> + </div> +</div> diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less new file mode 100644 index 00000000..6e031b1d --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less @@ -0,0 +1,94 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .widget-details-modal { + height: 430px; + + .title { + .n18r; + border-bottom: @a 3px solid; + + } + + .widget-properties-main { + padding: 16px; + height: 306px; + overflow-y: auto; + + .item{ + position: relative; + margin-bottom: 18px; + + .input-field{ + .custom-input-field; + width: 100%; + &.url{ + width: 78%; + display: inline-block; + } + } + + .select-field { + .custom-select-field; + } + + .item-label{ + .o14r; + } + + .right-item{ + position: relative; + display: inline-block; + width: 48%; + float: right; + } + .left-item{ + display: inline-block; + width: 48%; + } + + .url-validation-button{ + .btn-blue; + width: 20%; + display: inline-block; + float: right; + } + + .error-container{ + position: absolute; + width: 280px; + display: block; + height: 12px; + line-height: 12px; + + .err-message{ + color: red; + font-size: 9px; + } + .valid-message{ + color: green; + font-size: 9px; + } + } + + } + + } + +} diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.controller.js b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.js new file mode 100644 index 00000000..c24f49cd --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.js @@ -0,0 +1,151 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +'use strict'; +(function () { + class WidgetsCtrl { + constructor($log, applicationsService, widgetsService, ngDialog, confirmBoxService, + userProfileService, $cookies, $scope) { + $log.info('WidgetsCtrl::init: Starting Up'); + $scope.infoMessage = true; + + let populateAvailableApps = widgets => { + let allPortalsFilterObject = {index: 0, title: 'All applications', value: ''}; + this.availableApps = [allPortalsFilterObject]; + this.filterByApp = this.availableApps[0]; + applicationsService.getAppsForSuperAdminAndAccountAdmin().then(myApps => { + var reSortedApp = myApps.sort(getSortOrder("name")); + var realAppIndex = 1; + for (let i = 1; i <= reSortedApp.length; i++) { + if (!reSortedApp[i-1].restrictedApp) { + $log.debug('WidgetsCtrl::populateAvailableApps: pushing {index: ', realAppIndex, 'title: ', reSortedApp[i - 1].name, + 'value: ', reSortedApp[i - 1].name, '}'); + this.availableApps.push({ + index: realAppIndex, + title: reSortedApp[i - 1].name, + value: reSortedApp[i - 1].name + }) + realAppIndex = realAppIndex + 1; + } + } + }).catch(err => { + $log.error(err); + }); + }; + + let getOnboardingWidgets = () => { + this.isLoadingTable = true; + widgetsService.getManagedWidgets().then(res => { + var reSortedWidget = res.sort(getSortOrder("name")); + this.widgetsList = reSortedWidget; + populateAvailableApps(reSortedWidget); + }).catch(err => { + $log.error('WidgetsCtrl::getOnboardingWidgets error: ' + err); + }).finally(()=> { + this.isLoadingTable = false; + }); + }; + + let getSortOrder = (prop) => { + return function(a, b) { + if (a[prop].toLowerCase() > b[prop].toLowerCase()) { + return 1; + } else if (a[prop].toLowerCase() < b[prop].toLowerCase()) { + return -1; + } + return 0; + } + } + + $scope.hideMe = function () { + $scope.infoMessage = false; + } + + let init = () => { + this.isLoadingTable = false; + getOnboardingWidgets(); + + + this.searchString = ''; + + this.widgetsTableHeaders = [ + {name: 'Widget Name', value: 'name', isSortable: false}, + {name: 'Application', value: 'appName', isSortable: true}, + {name: 'Width', value: 'width', isSortable: false}, + {name: 'Height', value: 'height', isSortable: false} + ]; + this.widgetsList = []; + }; + + this.filterByDropdownValue = item => { + if(this.filterByApp.value === ''){ + return true; + } + return item.appName === this.filterByApp.value; + }; + + this.openWidgetDetailsModal = (selectedWidget) => { + let data = null; + if(selectedWidget){ + if(!selectedWidget.id){ + $log.error('Widget id not found'); + return; + } + data = { + widget: selectedWidget + } + } + ngDialog.open({ + templateUrl: 'app/views/widgets/widget-details-dialog/widget-details.modal.html', + controller: 'WidgetDetailsModalCtrl', + controllerAs: 'widgetDetails', + data: data + }).closePromise.then(needUpdate => { + if(needUpdate.value === true){ + $log.debug('WidgetsCtrl::openWidgetDetailsModal: updating table data...'); + getOnboardingWidgets(); + } + }); + }; + + this.deleteWidget = widget => { + confirmBoxService.deleteItem(widget.name).then(isConfirmed => { + if(isConfirmed){ + if(!widget || !widget.id){ + $log.error('WidgetsCtrl::deleteWidget: No widget or ID... cannot delete'); + return; + } + widgetsService.deleteWidget(widget.id).then(() => { + this.widgetsList.splice(this.widgetsList.indexOf(widget), 1); + }).catch(err => { + $log.error('WidgetsCtrl::deleteWidget error:',err); + }); + } + }).catch(err => { + $log.error('WidgetsCtrl::deleteWidget error:',err); + }); + }; + + init(); + } + } + WidgetsCtrl.$inject = ['$log', 'applicationsService', 'widgetsService', 'ngDialog', 'confirmBoxService', + 'userProfileService','$cookies', '$scope']; + angular.module('ecompApp').controller('WidgetsCtrl', WidgetsCtrl); +})(); diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.controller.spec.js b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.spec.js new file mode 100644 index 00000000..34042c14 --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.spec.js @@ -0,0 +1,19 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.less b/ecomp-portal-FE/client/app/views/widgets/widgets.less new file mode 100644 index 00000000..4b83b66f --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widgets.less @@ -0,0 +1,60 @@ +/*- + * ================================================================================ + * eCOMP Portal + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ + .widgets-page-main{ + .bg_w; + position: @page-main-position; + top: @page-main-top; + left: @page-main-left; + right: @page-main-right; + bottom: @page-main-bottom; + padding-top: @padding-top; + overflow-y: @page-main-overflow-y; + padding-left: @padding-left-side; + + .widgets-table{ + width: @table-width; + margin: @table-margin; + + .table-control{ + + } + + .delete-widget{ + .ico_trash_default; + } + } + .error-text { + width: 1170px; + margin: auto; + padding: 20px; + left: 20px; + font-weight: bold; + font-size: 16px; + text-align: left; + color: @err; + background-color: @u; + + .error-help { + color: @o; + font-weight: normal; + } + } + +}
\ No newline at end of file diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html b/ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html new file mode 100644 index 00000000..d255174f --- /dev/null +++ b/ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html @@ -0,0 +1,77 @@ +<!-- + ================================================================================ + eCOMP Portal + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ================================================================================ + --> +<div class="w-ecomp-main"> + <div class="w-ecomp-main-container"> + <div class="widgets-page-main" id="contentId"> + <div id="widget-onboarding-title" class="w-ecomp-main-view-title">Widget Onboarding</div> + <div class="widgets-table"> + <div class="table-control"> + <div class="c-ecomp-att-abs-select default"> + <div class="form-field" id="widegets-available-apps" + att-select="widgets.availableApps" + ng-model="widgets.filterByApp"></div> + </div> + <input class="table-search" type="text" id="widget-onboarding-table-search" + placeholder="Search in entire table" + ng-model="widgets.searchString"/> + + <div id="widget-onboarding-button-add" class="add-button" ng-click="widgets.openWidgetDetailsModal()">Add Widget</div> + </div> + <div class="error-text" ng-show="infoMessage"> + <span class="error-help">Only widgets for active applications are displayed.</span> + <button type="button" class="close" ng-click="hideMe()">×</button> + </div> + + <span class="ecomp-spinner" ng-show="widgets.isLoadingTable"></span> + <div class="c-ecomp-att-abs-table default" ng-hide="widgets.isLoadingTable"> + <table att-table + table-data="widgets.widgetsList" + search-string="widgets.searchString" + view-per-page="widgets.viewPerPageIgnored" + current-page="widgets.currentPageIgnored" + total-page="widgets.totalPageIgnored"> + <thead att-table-row type="header"> + <tr> + <th id="widget-onboarding-th-header-name" ng-repeat="header in widgets.widgetsTableHeaders" att-table-header key="{{header.value}}" sortable="{{header.isSortable}}">{{header.name}}</th> + <th id="widget-onboarding-th-header-url" att-table-header key="url" sortable="{{false}}">URL</th> + <th id="widget-onboarding-th-header-delete" att-table-header sortable="{{false}}">Delete</th> + </tr> + </thead> + <tbody att-table-row type="body" + class="table-body" + row-repeat="rowData in widgets.widgetsList | filter:widgets.filterByDropdownValue"> + <tr > + <td att-table-body ng-repeat="header in widgets.widgetsTableHeaders" ng-click="widgets.openWidgetDetailsModal(rowData)"> + <div id="widget-onboarding-div-{{rowData[header.value].split(' ').join('-')}}" ng-bind="rowData[header.value]"></div> + </td> + <td att-table-body ng-click="widgets.openWidgetDetailsModal(rowData)"> + <div id="widget-onboarding-div-url-{{rowData[header.value].split(' ').join('-')}}" ng-bind="rowData.url | trusted"></div> + </td> + <td att-table-body> + <div id="widget-onboarding-div-delete-widget-{{$index}}" class="delete-widget" ng-click="widgets.deleteWidget(rowData)"><span class="ion-trash-b"></span></div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + </div> +</div> |