summaryrefslogtreecommitdiffstats
path: root/ecomp-portal-FE-common/client/app/views
diff options
context:
space:
mode:
Diffstat (limited to 'ecomp-portal-FE-common/client/app/views')
-rw-r--r--ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.html125
-rw-r--r--ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.js171
-rw-r--r--ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.less195
-rw-r--r--ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.controller.js72
-rw-r--r--ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.less24
-rw-r--r--ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.tpl.html76
-rw-r--r--ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.controller.js252
-rw-r--r--ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js134
-rw-r--r--ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.modal.html77
-rw-r--r--ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.modal.less81
-rw-r--r--ecomp-portal-FE-common/client/app/views/admins/admins.controller.js161
-rw-r--r--ecomp-portal-FE-common/client/app/views/admins/admins.controller.spec.js19
-rw-r--r--ecomp-portal-FE-common/client/app/views/admins/admins.less45
-rw-r--r--ecomp-portal-FE-common/client/app/views/admins/admins.tpl.html66
-rw-r--r--ecomp-portal-FE-common/client/app/views/applications/application-details-dialog/application-details.modal.less106
-rw-r--r--ecomp-portal-FE-common/client/app/views/applications/applications.less31
-rw-r--r--ecomp-portal-FE-common/client/app/views/catalog/add-catalog-dialogs/new-catalog.modal.less125
-rw-r--r--ecomp-portal-FE-common/client/app/views/catalog/catalog.controller.js238
-rw-r--r--ecomp-portal-FE-common/client/app/views/catalog/catalog.less413
-rw-r--r--ecomp-portal-FE-common/client/app/views/catalog/catalog.tpl.html90
-rw-r--r--ecomp-portal-FE-common/client/app/views/catalog/catalogconfirmation.controller.js62
-rw-r--r--ecomp-portal-FE-common/client/app/views/catalog/information-box.tpl.html42
-rw-r--r--ecomp-portal-FE-common/client/app/views/confirmation-box/admin-confirmation-box.tpl.html31
-rw-r--r--ecomp-portal-FE-common/client/app/views/confirmation-box/confirmation-box.controller.js49
-rw-r--r--ecomp-portal-FE-common/client/app/views/confirmation-box/confirmation-box.less35
-rw-r--r--ecomp-portal-FE-common/client/app/views/confirmation-box/confirmation-box.tpl.html28
-rw-r--r--ecomp-portal-FE-common/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html31
-rw-r--r--ecomp-portal-FE-common/client/app/views/confirmation-box/information-box.tpl.html27
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-manage.html152
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameter-manage.html86
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameters.controller.js81
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameters.controller.less28
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget.controller.js422
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget.controller.less82
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/dashboard.controller.js504
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/dashboard.controller.spec.js78
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/dashboard.less901
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/dashboard.tpl.html179
-rw-r--r--ecomp-portal-FE-common/client/app/views/dashboard/newsticker.controller.js52
-rw-r--r--ecomp-portal-FE-common/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less97
-rw-r--r--ecomp-portal-FE-common/client/app/views/functionalMenu/functionalMenu.less60
-rw-r--r--ecomp-portal-FE-common/client/app/views/header/header.controller.js449
-rw-r--r--ecomp-portal-FE-common/client/app/views/header/header.controller.spec.js19
-rw-r--r--ecomp-portal-FE-common/client/app/views/header/header.less496
-rw-r--r--ecomp-portal-FE-common/client/app/views/header/header.tpl.html266
-rw-r--r--ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.html205
-rw-r--r--ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.js336
-rw-r--r--ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.less234
-rw-r--r--ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.controller.js87
-rw-r--r--ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.less26
-rw-r--r--ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.tpl.html75
-rw-r--r--ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.controller.js83
-rw-r--r--ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.less41
-rw-r--r--ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.tpl.html100
-rw-r--r--ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js95
-rw-r--r--ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js19
-rw-r--r--ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html36
-rw-r--r--ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less80
-rw-r--r--ecomp-portal-FE-common/client/app/views/portal-admin/portal-admin-controller.js110
-rw-r--r--ecomp-portal-FE-common/client/app/views/portal-admin/portal-admin.tpl.html64
-rw-r--r--ecomp-portal-FE-common/client/app/views/portal-admin/portal-admins.less42
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/popup_modal_rolefunction.html55
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/role-controller.js219
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/role-function-list-controller.js160
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/role-list-controller.js147
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/role.html96
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/role.less50
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/role_childrole_popup.html51
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/role_function_list.html107
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/role_functions_popup.html49
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/role_list.html55
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/rolefunctionpopupController.js95
-rw-r--r--ecomp-portal-FE-common/client/app/views/role/rolepopupmodelController.js251
-rw-r--r--ecomp-portal-FE-common/client/app/views/search/search.less66
-rw-r--r--ecomp-portal-FE-common/client/app/views/sidebar/sidebar.controller.js63
-rw-r--r--ecomp-portal-FE-common/client/app/views/sidebar/sidebar.less37
-rw-r--r--ecomp-portal-FE-common/client/app/views/sidebar/sidebar.tpl.html20
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js202
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less152
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html180
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.controller.js171
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.controller.spec.js19
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.less93
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.tpl.html126
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/get-access/get-access.controller.js123
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/get-access/get-access.controller.spec.js19
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/get-access/get-access.less55
-rw-r--r--ecomp-portal-FE-common/client/app/views/support/get-access/get-access.tpl.html102
-rw-r--r--ecomp-portal-FE-common/client/app/views/tabs/tabframe.html22
-rw-r--r--ecomp-portal-FE-common/client/app/views/tabs/tabs.controller.js314
-rw-r--r--ecomp-portal-FE-common/client/app/views/tabs/tabs.controller.spec.js81
-rw-r--r--ecomp-portal-FE-common/client/app/views/tabs/tabs.less660
-rw-r--r--ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.Json.details.controller.js36
-rw-r--r--ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.Json.details.modal.page.html48
-rw-r--r--ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.controller.js196
-rw-r--r--ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.json.details.modal.page.less48
-rw-r--r--ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.less128
-rw-r--r--ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.controller.js747
-rw-r--r--ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.less157
-rw-r--r--ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.page.html161
-rw-r--r--ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.tpl.html117
-rw-r--r--ecomp-portal-FE-common/client/app/views/userbar/userbar.less22
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.ack.html32
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.confirm.html83
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.controller.js577
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.modal.html70
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.modal.less60
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.controller.js216
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.controller.spec.js255
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.modal.html84
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.modal.less112
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/users.controller.js243
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/users.controller.spec.js141
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/users.less47
-rw-r--r--ecomp-portal-FE-common/client/app/views/users/users.tpl.html98
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.controller.js350
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.controller.spec.js20
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.less156
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.tpl.html76
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.controller.js381
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.controller.spec.js154
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.modal.html156
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.modal.less102
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.controller.js204
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.controller.spec.js20
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.less32
-rw-r--r--ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.tpl.html113
-rw-r--r--ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.controller.js226
-rw-r--r--ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js154
-rw-r--r--ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.modal.html152
-rw-r--r--ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.modal.less75
-rw-r--r--ecomp-portal-FE-common/client/app/views/widgets/widgets.controller.js168
-rw-r--r--ecomp-portal-FE-common/client/app/views/widgets/widgets.controller.spec.js19
-rw-r--r--ecomp-portal-FE-common/client/app/views/widgets/widgets.less48
-rw-r--r--ecomp-portal-FE-common/client/app/views/widgets/widgets.tpl.html81
135 files changed, 18596 insertions, 0 deletions
diff --git a/ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.html b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.html
new file mode 100644
index 00000000..91027f9e
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.html
@@ -0,0 +1,125 @@
+<div class="account-add-details-modal">
+ <div id="account-add-details-title" class="account-details-title">Account
+ Details</div>
+
+ <div id="accounts">
+ <div class="account-properties-main"
+ scroll-top="accountAddDetails.scrollApi">
+ <form id="accounts-details-form" name="serviceForm" novalidate
+ autocomplete="off">
+ <div class="item required">
+ <div class="item-label">Account Name</div>
+ <input id="account-details-input-name"
+ type="text" name="name" ng-pattern="/^[\w -]*$/" maxlength="100"
+ ng-model="accountAddDetails.account.applicationName"
+ ng-change="accountAddDetails.updateAccountName()" required />
+
+ <div class="error-container"
+ ng-show="(accountAddDetails.emptyAccountName || serviceForm.name.$dirty)">
+ <div ng-messages="serviceForm.name.$error" class="error-container">
+ <small id="accounts-details-input-name-required"
+ class="err-message" ng-message="required">account Name
+ is required</small> <small id="accounts-details-input-name-pattern"
+ class="err-message" ng-message="pattern">account Name
+ must be letters, numbers, or underscore</small>
+ </div>
+ </div>
+
+ <div class="error-container"
+ ng-show="accountAddDetails.dupliateName == true">
+ <small id="accounts-details-input-name-dupliated"
+ class="err-message">Name not available - choose different
+ name </small>
+ </div>
+ </div>
+
+ <div class="item required">
+ <div class="item-label">Username</div>
+ <input id="account-details-input-username"
+ type="text" name="username" maxlength="100"
+ ng-model="accountAddDetails.account.username"
+ ng-change="accountAddDetails.updateUsername()" />
+
+ <div class="error-container"
+ ng-show="(accountAddDetails.emptyAccountUsername
+ || (serviceForm.username.$dirty && accountAddDetails.account.username == ''))">
+ <small id="account-details-input-username-required"
+ class="err-message">Username is required</small>
+ </div>
+
+ </div>
+
+ <div class="item">
+ <div class="item-label">Password</div>
+ <input id="account-details-input-password"
+ type="password" name="password" maxlength="100"
+ ng-model="accountAddDetails.account.password" />
+ </div>
+
+ <div class="item">
+ <div class="item-label">Retype Password</div>
+ <input id="account-details-input-repassword"
+ type="password" name="repassword" maxlength="100"
+ ng-model="accountAddDetails.account.repassword"
+ ng-change="accountAddDetails.confirmPassword()" />
+
+ <div class="error-container"
+ ng-show="accountAddDetails.passwordMatched == false">
+ <small id="accounts-details-input-name-dupliated"
+ class="err-message">Password does not match the confirm
+ password</small>
+ </div>
+ </div>
+
+
+ <div class="add-endpoint-item">
+ <div class="item-label add-label-left">Add Endpoint</div>
+ <div class="icon-add add-label-right"
+ ng-click="accountAddDetails.addEndpoint()"></div>
+ </div>
+ <div class="item">
+ <div class="item-label"
+ ng-show="accountAddDetails.account.endpointList.length > 0">
+ Account endpoint</div>
+
+ <div id="account-details-user-paramters"
+ ng-repeat="endpoint in accountAddDetails.account.endpointList">
+
+ <div class="endpoint-item-left">
+ <input id="account-details-input-endpoint-name"
+ type="text" name="endpointName"
+ maxlength="200" ng-model="endpoint.name"
+ ng-change="accountAddDetails.updateAccountEndpoint(endpoint)"/>
+ </div>
+
+ <div class="icon-circle-action-remove endpoint-item-middle"
+ ng-click="accountAddDetails.removeEndpointItem(endpoint)"></div>
+
+ <div class="error-container endpoint-item-right"
+ ng-show="endpoint.valid == false">
+ <small id="accounts-details-input-invalid-endpoint"
+ class="err-message">Invalid end point format</small>
+ </div>
+
+ </div>
+
+ <div class="account-property">
+ <input id="accounts-checkbox-app-is-enabled" type="checkbox"
+ class="checkbox-field"
+ ng-model="accountAddDetails.account.active" />
+ <div class="property-label checkbox-label">Active</div>
+ </div>
+ <div class="item">
+ <div id="account-details-next-button" class="submit-button"
+ ng-click="accountAddDetails.closeThisDialog()">close</div>
+ <div id="account-details-next-button" class="submit-button"
+ ng-click="accountAddDetails.saveChanges()">Save</div>
+ </div>
+ </div>
+
+ </form>
+ </div>
+
+
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.js b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.js
new file mode 100644
index 00000000..ff89dce3
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.js
@@ -0,0 +1,171 @@
+'use strict';
+(function () {
+ class AccountAddDetailsCtrl {
+ constructor($scope, $log, $interval, basicAuthAccountSerivce, errorMessageByCode, ECOMP_URL_REGEX, $window, confirmBoxService, $cookies) {
+
+
+ this.addEndpoint = () => {
+ this.account.endpointList.push({
+ valid: true
+ });
+ }
+
+ let init = () => {
+ this.account = [];
+ this.account.endpointList = [];
+ this.passwordMatched = true;
+ this.dupliateName = false;
+ this.emptyAccountName = false;
+ this.emptyAccountUsername = false;
+ this.accountList = $scope.ngDialogData.list;
+
+ if ($scope.ngDialogData && $scope.ngDialogData.account) {
+ this.isEditMode = true;
+ this.account = _.clone($scope.ngDialogData.account);
+ this.account.repassword = this.account.password;
+ this.account.endpointList = this.account.endpoints;
+ if(this.account.isActive == 'Y')
+ this.account.active = true;
+ else
+ this.account.active = false;
+ } else {
+ this.isEditMode = false;
+ this.account.active = true;
+ }
+ console.log(this.account);
+ };
+
+ let resetConflict = fieldName => {
+ delete this.conflictMessages[fieldName];
+ if($scope.widgetForm[fieldName]){
+ $scope.widgetForm[fieldName].$setValidity('conflict', true);
+ }
+ };
+
+
+ this.closeThisDialog = () => {
+ $scope.closeThisDialog(true);
+ }
+
+ this.removeEndpointItem = (endpoint) => {
+ for(var i = 0; i < this.account.endpointList.length; i++){
+ if(this.account.endpointList[i].name == endpoint.name){
+ this.account.endpointList.splice(i, 1);
+ return;
+ }
+ }
+ }
+
+ this.confirmPassword = () => {
+ this.passwordMatched = true;
+ }
+
+ this.updateUsername = () => {
+ this.emptyAccountUsername = false;
+ }
+
+ this.updateAccountName = () => {
+ this.dupliateName = false;
+ for(var i = 0; i < this.accountList.length; i++){
+ if(this.accountList[i].applicationName == this.account.applicationName){
+ this.dupliateName = true;
+ return;
+ }
+ }
+ }
+
+ this.updateAccountEndpoint = (endpoint) => {
+ endpoint.valid = true;
+ }
+
+ this.saveChanges = () => {
+
+ var isValid = true;
+ var r = /\/[^ "]+$/;
+
+ for(var i = 0; i < this.account.endpointList.length; i++){
+ if(this.account.endpointList[i].name == undefined
+ || this.account.endpointList[i].name == null
+ || this.account.endpointList[i].name == ""){
+ this.account.endpointList.splice(i, 1);
+ i--;
+ }else{
+ if(!this.account.endpointList[i].name.startsWith("/")){
+ this.account.endpointList[i].name = "/" + this.account.endpointList[i].name;
+ }
+ if(!r.test(this.account.endpointList[i].name)){
+ this.account.endpointList[i].valid = false;
+ isValid = false;
+ }
+
+ }
+ }
+
+ if(this.account.applicationName == ''
+ || this.account.applicationName == undefined){
+ this.emptyAccountName = true;
+ isValid = false;
+ }
+
+ if(this.account.username == ''
+ || this.account.username == undefined){
+ this.emptyAccountUsername = true;
+ isValid = false;
+ }
+
+ if(this.dupliateName == true){
+ isValid = false;
+ }
+
+ if(this.account.password != this.account.repassword){
+ this.passwordMatched = false;
+ isValid = false;
+ }
+
+ if(!isValid)
+ return;
+
+
+
+ var active = 'N';
+ if(this.account.active == true)
+ active = 'Y';
+
+ var newAccount = {
+ applicationName: this.account.applicationName,
+ username: this.account.username,
+ password: this.account.password,
+ endpoints: this.account.endpointList,
+ isActive: active
+ };
+
+
+ if(this.isEditMode){
+ var message = "Are you sure you want to change '" + this.account.applicationName + "'?"
+ confirmBoxService.editItem(message).then(isConfirmed => {
+ if(isConfirmed){
+ basicAuthAccountSerivce.updateAccount(this.account.id, newAccount).then(() => {
+ $scope.closeThisDialog(true);
+ });
+ }
+ });
+ }else{
+ basicAuthAccountSerivce.createAccount(newAccount).then(() => {
+ $scope.closeThisDialog(true);
+ });
+ }
+
+
+
+ }
+
+
+ init();
+ $scope.$on('$stateChangeStart', e => {
+ e.preventDefault();
+ });
+ }
+ }
+ AccountAddDetailsCtrl.$inject = ['$scope', '$log', '$interval', 'basicAuthAccountSerivce', 'errorMessageByCode', 'ECOMP_URL_REGEX', '$window', 'confirmBoxService', '$cookies'];
+ angular.module('ecompApp').controller('AccountAddDetailsCtrl', AccountAddDetailsCtrl);
+})(); \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.less b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.less
new file mode 100644
index 00000000..6a916d6c
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-add-details/account-add-details.less
@@ -0,0 +1,195 @@
+.account-add-details-modal{
+ height: 550px;
+
+ overflow-y: auto;
+
+ .account-details-title {
+ color: #191919;
+ font-family: "Omnes-ECOMP-W02", Arial;;
+ font-size: 24px;
+ padding-bottom: 15px;
+ padding-top:30px;
+ margin-left: 25px;
+ }
+ .account-properties-main {
+ padding-top: 20px;
+ padding-left: 20px;
+ padding-right: 20px;
+ margin-bottom: 50px;
+
+ height: 100%;
+ width:100%;
+
+ .account-property{
+ margin-top: 10px;
+ position: relative;
+ .property-label{
+ .dGray14r;
+ }
+ .checkbox-label{
+ display: inline-block;
+ padding-left: 3px;
+ }
+ .checkbox-field{
+ padding: 0;
+ margin: 0;
+ vertical-align: middle;
+ position: relative;
+ top: -1px;
+ }
+ }
+ .add-endpoint-item{
+ position: relative;
+ .add-label-left{
+ line-height: 25px;
+ height: 30px;
+ vertical-align: middle;
+ display:inline-block;
+ margin-right: 10px;
+ .dGray14r;
+ }
+ .add-label-right{
+ height: 14px;
+ width: 14px;
+ display:inline-block;
+ }
+ }
+ .item{
+ position: relative;
+ margin-bottom: 15px;
+ width: 400px;
+ .service-select{
+ select{
+ cursor: pointer;
+ position: relative;
+ border: 1px solid #5a5a5a;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ line-height: 15px;
+ height: 32px;
+ padding-left: 10px;
+ padding-right: 10px;
+ border-radius: 0px;
+ }
+ display:inline-block;
+ width: 100%;
+ }
+ .error-container{
+ position: absolute;
+ width: 280px;
+ display: block;
+ height: 12px;
+ line-height: 12px;
+
+ .err-message{
+ color: @funcRed;
+ font-size: 9px;
+ }
+ .valid-message{
+ color: @funcGreen;
+ font-size: 9px;
+ }
+ }
+ .auth-item-left{
+ padding-top: 0;
+ line-height: 30px;
+ height: 30px;
+ vertical-align: middle;
+ display:inline-block;
+ width: 15%;
+ //border-radius: 2px;
+ //border: 1px solid @attLGray;
+ margin-right: 10px;
+ //background: @attWhite;
+ white-space: nowrap;
+ .dGray14r;
+ }
+ .auth-item-right{
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid;
+ //background: @attWhite;
+ vertical-align: middle;
+ }
+
+ .endpoint-item-left{
+ line-height: 30px;
+ height: 30px;
+ vertical-align: middle;
+ display:inline-block;
+ width: 80%;
+ //border-radius: 2px;
+ //border: 1px solid;
+ margin-right: 10px;
+ //background: @attWhite;
+ white-space: nowrap;
+ .dGray14r;
+ }
+ .endpoint-item-middle{
+ margin-top: 10px;
+ height: 14px;
+ width: 14px;
+ display:inline-block;
+ }
+
+ .endpoint-item-right{
+ margin-left: 5px;
+ width: 15%;
+ height: 30px;
+ display:inline-block;
+ }
+
+ .endpoint-label-item-left{
+ line-height: 30px;
+ height: 30px;
+ vertical-align: middle;
+ display:inline-block;
+ width: 80%;
+ margin-right: 10px;
+ //background: @attWhite;
+ white-space: nowrap;
+ .dGray14r;
+ }
+ .endpoint-label-item-right{
+ line-height: 30px;
+ height: 30px;
+ display:inline-block;
+ width: 20%;
+ //background: @attWhite;
+ vertical-align: middle;
+ white-space: nowrap;
+ .dGray14r;
+ }
+
+
+ .textarea-field{
+ position: relative;
+ .custom-input-field;
+ height:50px;
+ }
+ .submit-button {
+ margin-top: 25px;
+ position: relative;
+ float: right;
+ width: 20%;
+ .btn-blue;
+ }
+
+
+
+ .add-endpoint-button{
+ height: 14px;
+ width: 14px;
+ float: right;
+ }
+ .item-label{
+ .dGray14r;
+ }
+ }
+ }
+}
+#account-scroll-end{
+ height: 20px;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.controller.js b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.controller.js
new file mode 100644
index 00000000..deed9dc8
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.controller.js
@@ -0,0 +1,72 @@
+'use strict';
+(function () {
+ class AccountOnboardingCtrl {
+ constructor($log, ngDialog, confirmBoxService, basicAuthAccountService, $cookies, $scope) {
+
+
+ let init = () => {
+ $scope.accountList = [];
+ getOnboardingAccounts();
+
+ this.accoutTableHeaders = [
+ {name: 'Account Name', value: 'applicationName', isSortable: true},
+ {name: 'Username', value: 'username', isSortable: false}
+ ];
+ };
+
+ let getOnboardingAccounts = () => {
+ basicAuthAccountService.getAccountList().then(res => {
+ $scope.accountList = res;
+ }).catch(err => {
+ $log.error('AccountOnboardingCtrl::getOnboardingAccounts caught error', err);
+ });
+ };
+
+ this.openAddNewAccountModal = (selectedAccount) => {
+ let data = null;
+ if(selectedAccount){
+ data = {
+ account:selectedAccount,
+ list: $scope.accountList
+ }
+ }else{
+ data = {
+ list: $scope.accountList
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/account-onboarding/account-add-details/account-add-details.html',
+ controller: 'AccountAddDetailsCtrl',
+ controllerAs: 'accountAddDetails',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if(needUpdate.value === true){
+ if(needUpdate.value === true){
+ getOnboardingAccounts();
+ }
+ }
+ });
+ };
+
+
+ this.deleteAccount = account => {
+ console.log(account);
+ confirmBoxService.deleteItem(account.applicationName).then(isConfirmed => {
+ if(isConfirmed){
+ basicAuthAccountService.deleteAccount(account.id).then(() => {
+ $scope.accountList.splice($scope.accountList.indexOf(account), 1);
+ }).catch(err => {
+ $log.error('AccountOnboardingCtrl::deleteAccount error:',err);
+ });
+ }
+ }).catch(err => {
+ $log.error('AccountOnboardingCtrl::deleteAccount error:',err);
+ });
+ };
+
+ init();
+ }
+ }
+ AccountOnboardingCtrl.$inject = ['$log', 'ngDialog', 'confirmBoxService', 'basicAuthAccountService', '$cookies', '$scope'];
+ angular.module('ecompApp').controller('AccountOnboardingCtrl', AccountOnboardingCtrl);
+})(); \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.less b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.less
new file mode 100644
index 00000000..8d3663c3
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.less
@@ -0,0 +1,24 @@
+.account-onboarding{
+ 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;
+
+ .account-table {
+ width: @table-width;
+ margin: 0 auto;
+
+ .add-button{
+ width: 180px;
+ }
+ }
+
+ .delete-account{
+ .ico_trash_default;
+ }
+
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.tpl.html b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.tpl.html
new file mode 100644
index 00000000..5f46a1cc
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/account-onboarding/account-onboarding.tpl.html
@@ -0,0 +1,76 @@
+<div class="w-ecomp-main">
+ <div class="w-ecomp-main-container">
+ <div class="account-onboarding" id="page-content">
+ <div id="account-onboarding-title" class="w-ecomp-main-view-title">Account
+ Onboarding</div>
+ <div class="account-table">
+ <div class="table-control">
+
+ <div class="table-control-buttons">
+ <button class="btn btn-alt btn-small"
+ id="account-onboarding-button-add"
+ ng-click="accountOnboarding.openAddNewAccountModal()">
+ <i class="icon-people-userbookmark" aria-hidden="true"></i>&nbsp;Add
+ Account
+ </button>
+ </div>
+
+
+ <div>
+ <div class="c-ecomp-b2b-abs-table default">
+ <table b2b-table table-data="serviceList"
+ view-per-page="accountOnboardingviewPerPageIgnored"
+ current-page="accountOnboarding.currentPageIgnored"
+ total-page="accountOnboarding.totalPageIgnored">
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="account-catalog-th-header-name"
+ ng-repeat="header in accountOnboarding.accoutTableHeaders"
+ b2b-table-header key="{{header.value}}"
+ sortable="{{header.isSortable}}">{{header.name}}</th>
+
+ <th id="account-catalog-th-header-endpoints" b2b-table-header
+ key="endpoints" sortable="false">Endpoints</th>
+
+ <th id="account-catalog-th-header-delete" b2b-table-header
+ sortable="false">Delete</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" class="table-body"
+ row-repeat="rowData in accountList">
+
+ <tr>
+ <td b2b-table-body
+ ng-repeat="header in accountOnboarding.accoutTableHeaders"
+ ng-click="accountOnboarding.openAddNewAccountModal(rowData)">
+ <div id="account-catalog-name-{{rowData.id}}"
+ ng-bind="rowData[header.value]"></div>
+ </td>
+
+ <td b2b-table-body
+ ng-click="accountOnboarding.openAddNewAccountModal(rowData)">
+ <div ng-show="rowData.endpoints.length > 0">
+ <div ng-repeat="row in rowData.endpoints">
+ <div id="account-catalog-account-endpoint"
+ ng-bind="row.name"></div>
+ </div>
+ </div>
+ <div ng-hide="rowData.endpoints.length > 0">All
+ endpoints</div>
+ </td>
+
+ <td b2b-table-body>
+ <div id="account-onboarding-div-delete-{{$index}}"
+ class="delete-account"
+ ng-click="accountOnboarding.deleteAccount(rowData)"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.controller.js b/ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.controller.js
new file mode 100644
index 00000000..5a164e1a
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.controller.js
@@ -0,0 +1,252 @@
+/*-
+ * ================================================================================
+ * 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;
+ }
+
+ //this.searchUsersInProgress = false;
+ //this.showNewAdminAppDropdown = false;
+ $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 function get the selected admin apps roles
+ */
+ 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);
+ });
+ };
+
+ // Refactor this into a directive
+ 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 function set the selected user
+ * @param user: selected user object
+ */
+ this.setSelectedUser = (user) => {
+ $log.debug('selected user: ', user);
+ this.selectedUser = user;
+ };
+
+ /**
+ * Mark the user as not admin of the selected app
+ * @param app: selected app object
+ */
+ 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);
+ });
+ };
+
+ /**
+ * update the selected admin app with the new roles
+ */
+ 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 => {
+ if(confirmed === true){
+ adminsService.updateAdminAppsRoles({
+ orgUserId: this.selectedUser.orgUserId,
+ appsRoles: this.adminAppsRoles
+ })
+ .then(res => {
+ $log.debug('Admin apps roles updated successfully!', res);
+ //close and resolve dialog promise with true (to update the table)
+ this.remindToAddUserIfNecessary();
+ $scope.closeThisDialog(true);
+ }).catch(err => {
+ $log.error('NewAdminModalCtrl.updateAdminAppsRoles:: Failed - ' + err);
+ }).finally(()=> {
+ this.isSaving = false;
+ })
+ }else{
+ this.isSaving = false;
+ }
+ });
+ };
+
+ /**
+ * Navigate between dialog screens using step number: 1,2,...
+ */
+ this.navigateBack = () => {
+ if (this.dialogState === 1) {
+ //back from 1st screen?
+ }
+ if (this.dialogState === 2) {
+ this.dialogState = 1;
+ }
+ };
+
+ init();
+
+ /**
+ * each time new app is selected in the drop down,
+ * add it to the user administrated apps list
+ */
+ $scope.$watch('newAdmin.selectedNewApp', (newVal) => {
+ if (!newVal || newVal.isAdmin === undefined) {
+ return;
+ }
+ //newVal.isAdmin = true; - track by ruined this, here is the workaround:
+ let app = _.find(this.adminAppsRoles, {id: newVal.id});
+ if (app) {
+ app.isAdmin = true;
+ this.appsOrder.push(app.id);
+ }
+ this.selectedNewApp = null;
+ //this.showNewAdminAppDropdown = false;
+ });
+
+ $scope.$on('$stateChangeStart', e => {
+ //Disable navigation when modal is opened
+ //**Nabil - note: this will cause the history back state to be replaced with current state
+ e.preventDefault();
+ });
+
+ /**
+ * If an Admin was added for an application remind the portal admin to add the admin as a user
+ */
+ 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 ECOMP 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-common/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js b/ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js
new file mode 100644
index 00000000..540459ed
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.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.
+ * ================================================================================
+ */
+/**
+ * Created by nnaffar on 12/8/15.
+ */
+'use strict';
+
+describe('Controller: NewAdminCtrl ', () => {
+ beforeEach(module('ecompApp'));
+
+ //destroy $http default cache before starting to prevent the error 'default cache already exists'
+ 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.orgUserId);
+ 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}];
+ //simulate UI change
+ $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({orgUserId: newCtrl.selectedUser.orgUserId, 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-common/client/app/views/admins/add-admin-dialogs/new-admin.modal.html b/ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.modal.html
new file mode 100644
index 00000000..1b1b9de8
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.modal.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="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">
+ <button id="search-users-button-next" class="btn btn-alt btn-small" ng-click="newAdmin.selectedUser && newAdmin.getAdminAppsRoles()"
+ ng-class="{disabled: !newAdmin.selectedUser}">Next
+ </button>
+ <button id="search-users-button-cancel" class="btn btn-alt btn-small" ng-click="closeThisDialog()">Cancel</button>
+ </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>
+ <!--<span class="right" ng-click="newAdmin.addAdministratedApp()">+Add</span>-->
+ </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 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="icon-misc-trash" ng-click="newAdmin.unadminApp(app)"> </i>
+ </div>
+ </div>
+
+ <div class="dialog-control">
+ <span class="ecomp-save-spinner" ng-show="newAdmin.isSaving"></span>
+ <button id="button-back" ng-show="newAdmin.isShowBack" class="btn btn-alt btn-small" ng-click="newAdmin.navigateBack()">Back</button>
+ <button id="div-updateAdminAppsRoles" class="btn btn-alt btn-small" ng-click="newAdmin.updateAdminAppsRoles()"
+ ng-class="{disabled: false}">Save
+ </button>
+ <button id="div-cancel-button" class="btn btn-alt btn-small" ng-click="closeThisDialog()">Cancel</button>
+ </div>
+
+ </div>
+
+ </div>
+
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.modal.less b/ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.modal.less
new file mode 100644
index 00000000..8b304b43
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/admins/add-admin-dialogs/new-admin.modal.less
@@ -0,0 +1,81 @@
+.new-admin-modal {
+ height: 430px;
+ margin-left: 20px;
+
+ .search-users {
+ }
+
+ .admin-app-roles {
+ .title {
+ //.n18r;
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @blue-active 3px solid;
+
+ }
+
+ .app-roles-main {
+ margin-top: 16px;
+ .app-roles-main-title {
+ .dGray14r;
+ margin-bottom: 8px;
+ .left {
+ display: inline-block;
+ }
+ .right {
+ display: inline-block;
+ color: @blue-active;
+ float: right;
+ cursor: pointer;
+ }
+ }
+
+ .select-input{
+ width: 460px;
+ }
+
+ .new-administrated-app {
+ height: 30px;
+ line-height: 30px;
+
+ border: 1px solid @portalGray;
+ margin-bottom: 8px;
+ border-radius: 2px;
+ padding-left: 6px;
+ padding-top: 0;
+ width: 100%;
+ .dGray14r;
+ }
+
+ .admin-roles-list {
+ height: 240px;
+ overflow-y: auto;
+ }
+
+ .administrated-application {
+ width: 460px;
+ height: 30px;
+ border: 1px solid @portalGray;
+ margin-bottom: 8px;
+ border-radius: 2px;
+ padding: 6px;
+ .dGray14r;
+ 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-common/client/app/views/admins/admins.controller.js b/ecomp-portal-FE-common/client/app/views/admins/admins.controller.js
new file mode 100644
index 00000000..05f9db3b
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/admins/admins.controller.js
@@ -0,0 +1,161 @@
+/*-
+ * ================================================================================
+ * 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.error('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=> {
+ //if(!res || Object.prototype.toString.call(res) !== '[object Array]'){
+ // this.availableApps = [allPortalsFilterObject];
+ // return;
+ //}
+ //this part overrides index param to fix ABS select bug
+ // (index has to be the same as location in array)
+ // todo:change BE object to contain only id and name
+ 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('AdminsCtrl::ctor', err);
+ this.availableApps = [allPortalsFilterObject];
+ });
+
+ // Refactor this into a directive
+ let getSortOrder = (prop) => {
+ return function(a, b) {
+ // $log.debug('a = ' + JSON.stringify(a) + "| b = " + JSON.stringify(b));
+ if (a[prop].toLowerCase() > b[prop].toLowerCase()) {
+ return 1;
+ } else if (a[prop].toLowerCase() < b[prop].toLowerCase()) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ init();
+
+ //Filter function
+ 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-common/client/app/views/admins/admins.controller.spec.js b/ecomp-portal-FE-common/client/app/views/admins/admins.controller.spec.js
new file mode 100644
index 00000000..3841a2b3
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/admins/admins.less b/ecomp-portal-FE-common/client/app/views/admins/admins.less
new file mode 100644
index 00000000..729ef693
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/admins/admins.less
@@ -0,0 +1,45 @@
+.w-ecomp-admins-page-main {
+ .bg_portalWhite;//white for 1702
+ 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;
+#input-table-search::-webkit-input-placeholder,
+{
+font-style: italic;
+ color: #D7D7D7;
+
+}
+ .ecomp-spinner{
+ top: 255px;
+ }
+
+ .admins-home-container {
+ position: relative;
+ padding-right: 0;
+ padding-left: 0;
+ padding-bottom: @container-bottom;
+
+ .admins-table {
+ width: @table-width;
+ //margin-left: @table-margin-left;
+ margin: 0 auto;
+
+ .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-common/client/app/views/admins/admins.tpl.html b/ecomp-portal-FE-common/client/app/views/admins/admins.tpl.html
new file mode 100644
index 00000000..907bf323
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/admins/admins.tpl.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="w-ecomp-admins-page-main">
+ <div class="admins-home-container" id="page-content">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page" >Admins</h1>
+ </div>
+ <div class="admins-table">
+ <div class="table-control">
+ <div class="table-control-fields">
+ <div class="table-dropdown">
+ <select id="dropdown1" name="dropdown1" b2b-dropdown placeholder-text="All application" ng-model="admins.filterByApp.value">
+ <option b2b-dropdown-list option-repeat="d in admins.availableApps" value="{{d.value}}">{{d.title}}</option>
+ </select>
+ </div>
+ <div >
+ <input id="input-table-search" placeholder="Search in entire table" class="table-search-field" type="text" data-ng-model="admins.searchString">
+ </div>
+ <button class="btn btn-alt btn-small" ng-click="admins.openAddNewAdminModal()"><i class="icon-people-userbookmark" aria-hidden="true"></i>&nbsp;Add Admin</button>
+ </div>
+ </div>
+ <span class="ecomp-spinner" ng-show="admins.isLoadingTable"></span>
+ <div b2b-table table-data="admins.adminsTableData" ng-hide="admins.isLoadingTable" search-string="admins.searchString" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr >
+ <th b2b-table-header key="firstName" sortable="true" id="col1">First Name</th>
+ <th b2b-table-header key="lastName" sortable="true" id="col2">Last Name</th>
+ <th b2b-table-header key="orgUserId" sortable="true" id="col3">User ID</th>
+ <th b2b-table-header key="" sortable="falses" id="col4">Applications</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" row-repeat="rowData in admins.adminsTableData | filter: admins.portalsRowFilter">
+ <tr ng-click="admins.openAddNewAdminModal(rowData)">
+ <td b2b-table-body id="rowheader_t1_{{$index}}" headers="col1" ng-bind="rowData.firstName"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col2" ng-bind="rowData.lastName"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col3" ng-bind="rowData.orgUserId"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col4">
+ <div class="ecomp-table-repeat" 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-common/client/app/views/applications/application-details-dialog/application-details.modal.less b/ecomp-portal-FE-common/client/app/views/applications/application-details-dialog/application-details.modal.less
new file mode 100644
index 00000000..1c8ae297
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/applications/application-details-dialog/application-details.modal.less
@@ -0,0 +1,106 @@
+.application-details-modal {
+ height: 700px;
+
+ .title {
+ //.n18r;
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @blue-active 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;
+ margin-right:10px;
+
+ }
+
+ .property{
+ position: relative;
+ margin-bottom: 18px;
+ .property-label{
+ .dGray14r;
+ }
+ .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;
+ .blue14r;
+ }
+ }
+
+ .input-field{
+ .custom-input-field;
+ width: 220px;
+ }
+
+ .input-file-field{
+ width: 220px;
+ border: 0px solid #d2d2d2;
+ box-shadow: 0px 0px 2px -2px rgba(0, 0, 0, 0.08) inset;
+ padding-left: 2px;
+ }
+ .select-field {
+ .custom-select-field;
+ }
+
+ .image-preview{
+ background: @funcBkgGray;
+ 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: @funcRed;
+ font-size: 10px;
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/ecomp-portal-FE-common/client/app/views/applications/applications.less b/ecomp-portal-FE-common/client/app/views/applications/applications.less
new file mode 100644
index 00000000..60b9d81b
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/applications/applications.less
@@ -0,0 +1,31 @@
+.applications-page-main{
+ .bg_portalWhite;//white for 1702
+ 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;
+ #input-table-search::-webkit-input-placeholder,
+ {
+ font-style: italic;
+ color: #D7D7D7;
+ }
+ .apps-table {
+ width: @table-width;
+ //margin-left: @table-margin-left;
+ //margin: @table-margin;
+ margin:auto;
+ .delete-app{
+ .ico_trash_default;
+ }
+ .small-thumbnail{
+ width: 72px;
+ height: 60px;
+ border: 1px solid @portalLGray;
+ border-radius: 2px;
+ }
+ }
+}
diff --git a/ecomp-portal-FE-common/client/app/views/catalog/add-catalog-dialogs/new-catalog.modal.less b/ecomp-portal-FE-common/client/app/views/catalog/add-catalog-dialogs/new-catalog.modal.less
new file mode 100644
index 00000000..b98289e7
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/catalog/add-catalog-dialogs/new-catalog.modal.less
@@ -0,0 +1,125 @@
+.new-catalog-modal {
+ height: 430px;
+
+ .user-catalog-roles{
+ .title{
+ //.n18r;
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @portalDBlue 3px solid;
+ }
+
+ input:not([type="button"]) {
+ height: 13px;
+}
+ .display-userApp-Catalog-Roles
+ {
+ padding-left: 16px;
+ padding-top: 10px;
+ font-family: Omnes-ECOMP-W02,Arial;
+ font-size: 18px;
+ color: #5a5a5a;
+ }
+
+ #pending-checkbox{
+ font-family: Omnes-ECOMP-W02,Arial;
+ font-size: 15px;
+ color: #5a5a5a;
+ }
+ .app-catalog-roles-list{
+ height: 286px;
+
+
+ .app-catalog-item{
+ border: 1px solid @portalLGray;
+ border-radius: 2px;
+ background-color: @funcBkgGray;
+
+ padding: 10px;
+ margin-top: 8px;
+ //margin-right: 6px;
+ //margin-left: 6px;
+
+ .app-catalog-item-left{
+ padding-top: 0;
+ line-height: 30px;
+ height: 30px;
+ vertical-align: middle;
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid @portalLGray;
+ margin-right: 10px;
+ padding-left: 4px;
+ background: @portalWhite;
+ white-space: nowrap;
+
+ }
+ .app-catalog-item-right{
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid @portalLGray;
+ background: @portalWhite;
+ vertical-align: middle;
+ }
+
+ .app-catalog-item-right-error{
+ .portalRed;
+ padding: 7px 7px 7px 7px;
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid @portalLGray;
+ background: @portalWhite;
+ vertical-align: middle;
+ }
+
+ .app-catalog-item-right-contacting{
+ .portalGreen;
+ padding: 7px 7px 7px 7px;
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid @portalLGray;
+ background: @portalWhite;
+ 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 @portalLGray;
+ margin-right: 10px;
+ padding-left: 4px;
+ background: @portalWhite;
+ 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-common/client/app/views/catalog/catalog.controller.js b/ecomp-portal-FE-common/client/app/views/catalog/catalog.controller.js
new file mode 100644
index 00000000..ad34b08d
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/catalog/catalog.controller.js
@@ -0,0 +1,238 @@
+/*-
+ * ================================================================================
+ * 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(conf, catalogService, confirmBoxService, ExternalRequestAccessService,
+ $log, $window, userProfileService, applicationsService, $scope, $state,
+ $timeout, $interval, $modal, ngDialog) {
+
+ this.conf = conf;
+ 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;
+ this.applicationsService = applicationsService;
+ var resultAccessValue = null;
+ $scope.demoNum = 1;
+ $scope.appRoles = [];
+
+ let init = () => {
+ getExternalAccess();
+ };
+
+ var getExternalAccess = () => {
+ ExternalRequestAccessService.getExternalRequestAccessServiceInfo().then(
+ function(property) {
+ resultAccessValue = property.accessValue;
+ // $log.info(res);
+ }).catch(err => {
+ $log.error('CatalogCtrl: failed getExternalRequestAccessServiceInfo: ' + JSON.stringify(err));
+ });
+ };
+
+
+
+ $scope.applyPresentationDetailsToAppsCatalog = function(res, value) {
+
+ _this.apps = res;
+ var rowNo = 0;
+
+ // defend against error string result -
+ // a huge list that should never happen.
+ var maxItems = 333;
+ if (_this.apps.length < maxItems)
+ maxItems = _this.apps.length;
+ for (var i = 0; i < maxItems; i++) {
+ let imgLnk = '';
+ if (_this.apps[i].imageUrl)
+ imgLnk = conf.api.appThumbnail.replace(':appId', _this.apps[i].id);
+ //$log.debug('CatalogCtlr::applyPresn: imgLink = ' + imgLnk);
+ $scope.appCatalog[i] = {
+ sizeX : 2,
+ sizeY : 2,
+ id : _this.apps[i].id,
+ headerText : _this.apps[i].name,
+ imageLink : imgLnk,
+ restricted : _this.apps[i].restricted,
+ select : _this.apps[i].select,
+ access : _this.apps[i].access,
+ pending: _this.apps[i].pending,
+ mlproperty: value
+ };
+ }
+ //$log.debug('CatalogCtrl: getAppCatalog count : '
+ // + $scope.appCatalog.length);
+ _this.isLoading = false;
+ $scope.getAppCatalogIsDone = true;
+ }
+
+ let getAppsCatalog = () => {
+ catalogService.getAppCatalog()
+ .then(appsList => {
+ $scope.applyPresentationDetailsToAppsCatalog(appsList);
+ }).catch(err => {
+ confirmBoxService.showInformation('There was a problem retrieving the Applications. ' +
+ 'Please try again later. Error Status: '+ err.status).then(isConfirmed => {});
+ $log.error('CatalogCtrl:openAddRoleModal: Error: ', err);
+ });
+ };
+
+ this.openAddRoleModal = (item) => {
+ let data = null;
+
+ if((!item.restricted) && (item.mlproperty)){
+ data = {
+ dialogState: 2,
+ selectedUser:{
+ attuid: $scope.attuid,
+ firstName: $scope.firstName,
+ lastName: $scope.lastName,
+ headerText: item.headerText,
+ item: item
+
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/catalog/add-catalog-dialogs/new-catalog.modal.html',
+ controller: 'NewCatalogModalCtrl',
+ controllerAs: 'userInfo',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if(needUpdate.value === true){
+ getAppsCatalog();
+ }
+
+ });
+ }
+ };
+
+ // 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.updateManualAppSort(appData).then(
+ function(result) {
+ // $log.debug('CatalogCtrl:storeSelection result is ', result);
+ })['catch'](function(err) {
+ $log.error('CatalogCtrl:storeSelection: exception: ', err);
+ });
+ 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.orgUserId;
+ $scope.firstName = profile.firstName;
+ $scope.lastName = profile.lastName;
+ $scope.appCatalog = [];
+
+ catalogService.getAppCatalog().then(
+ function(res) {
+ $scope.applyPresentationDetailsToAppsCatalog(res, resultAccessValue);
+ })['catch'](function(err) {
+ $log.error('CatalogCtrl: failed getAppCatalog: ', JSON.stringify(err));
+ _this.isLoading = false;
+ $scope.getAppCatalogIsDone = true;
+ });
+ });
+
+ // applicationsService.getUserApps()
+
+
+ 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();
+ }
+
+ }
+
+ init();
+ };
+
+ CatalogCtrl.$inject = [ 'conf', 'catalogService', 'confirmBoxService', 'ExternalRequestAccessService', '$log',
+ '$window', 'userProfileService', 'applicationsService', '$scope',
+ '$state', '$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, " ");
+}
diff --git a/ecomp-portal-FE-common/client/app/views/catalog/catalog.less b/ecomp-portal-FE-common/client/app/views/catalog/catalog.less
new file mode 100644
index 00000000..dc5e4671
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/catalog/catalog.less
@@ -0,0 +1,413 @@
+.w-ecomp-app-catalog {
+ .bg_portalWhite;//white for 1702
+ 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;
+ input:not([type="button"]) {
+ height: 13px;
+}
+
+ .gridster-item-container .gridster-item-body {
+ overflow-y:auto;
+ overflow-x:hidden;
+ }
+
+ .appCatalogue-boarder{
+ background-color: #eee;
+ border: 1px dashed white;
+ 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;
+ // margin-left: 230px;
+ .content_justify;
+ }
+
+ .app-catalog-list {
+ min-height: 70vh;
+ //display: flex;
+ justify-content: center;
+ flex-flow: row wrap;
+ width: @table-width;
+ //margin-left: 230px;
+ margin-bottom: 63px;
+ margin:auto;
+ .app-gridster-header {
+ background-color: @u;
+ font-size:12px;
+ overflow:hidden
+ }
+
+ .app-gridster-footer {
+ background-color: @u;
+ }
+
+ }
+ }
+}
+
+@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;}
+
+.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;
+ }
+
+/*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: "Omnes-ECOMP-W02", Arial;
+ font-size: 18px;
+ color: #444444;
+ float: left;
+}
+
+.simulateCatGridHeaderRadio{
+ line-height: 20px;
+ margin-top: 10px;
+ margin-left: 10px;
+ font-family: "Omnes-ECOMP-W02", Arial;
+ font-size: 12px;
+ color: #444444;
+ float: left;
+}
+
+.simulateCatGridHeaderDetails{
+ line-height: 20px;
+ margin-left: 10px;
+ font-family: "Omnes-ECOMP-W02", 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;
+}
+
+.portal-checkbox--on .portal-checkbox__indicator, .portal-radio--on .portal-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;
+}
+
+/** gridster style**/
+ul {
+ list-style: none;
+}
+.gridster-box {
+ height: 100%;
+ border: 1px solid #ccc;
+ background-color: #fff;
+ transition: transform 0.5s ease-out;
+}
+.gridster-box-header {
+ background-color: #fff;
+ padding: 0 0px 0 10px;
+ border-bottom: 1px solid #ccc;
+ position: relative;
+ height: 50px !important;
+}
+.gridster-box-header h3 {
+ margin-top: 15px;
+ display: inline-block;
+ font-family: "Omnes-ECOMP-W02", Arial;
+}
+.gridster-box-content {
+ padding: 59px;
+}
+.gridster-box:hover{
+ transform: scale(1.1);
+}
+.gridster-box-header-btns {
+ top: 15px;
+ right: 10px;
+ position: absolute;
+}
+.checkbox .skin {
+ left: 125px;
+ top: -24px;
+}
+.form-row {
+ margin-top: -14px;
+}
+
+.icon-content-gridguide{
+cursor:move;
+font-size: 22px;
+}
+.icon-tickets-contested{
+font-size: 20px;
+margin-left: 126px;
+}
+
+.checkbox, .radio {
+ min-height: 0px;
+ padding-left: 0px;
+
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/catalog/catalog.tpl.html b/ecomp-portal-FE-common/client/app/views/catalog/catalog.tpl.html
new file mode 100644
index 00000000..68bf9ef2
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/catalog/catalog.tpl.html
@@ -0,0 +1,90 @@
+<!--
+ ================================================================================
+ 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">
+ <div class="app-catalog-container" id="page-content">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page">Application Catalog</h1>
+ </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 id="label-all"
+ class="simulateCatGridHeaderRadio"> Filter: <input
+ id="radio-button-all" type="radio" ng-model="radioValue"
+ value="All"> All
+ </label> <label id="label-accessible" class="simulateCatGridHeaderRadio">
+ <input id="radio-button-accessible" type="radio"
+ ng-model="radioValue" value="Accessible"> Accessible
+ </label> <label id="label-homepage" class="simulateCatGridHeaderRadio">
+ <input id="radio-button-homepage" type="radio"
+ ng-model="radioValue" value="Selected"> On Home Page
+ </label>
+
+ </div>
+ <div class="appCatalogue-boarder" >
+ <div gridster="catalog.gridsterOpts">
+ <ul>
+ <li gridster-item="item" ng-repeat="item in appCatalog"
+ ng-if="(item.select && radioValue=='Selected') || (item.access && radioValue=='Accessible') || (radioValue == 'All')">
+ <div class="gridster-box" style="{{!item.access?' color:lightgray !important':''}}">
+ <div class="gridster-box-header"
+ ng-show="(item.select && radioValue=='Selected') || (item.access && radioValue=='Accessible') || (radioValue == 'All')">
+ <i class="icon-content-gridguide"></i>
+ <h3 style="cursor: context-menu">{{item.headerText |
+ elipsis: 13}}</h3>
+ <div class="form-row" ng-show="item.access || isAdminPortalAdmin">
+ <label class="checkbox">
+ <input type="checkbox" ng-model="item.select" ng-change="catalog.storeSelection(item)">
+ <i class="skin"></i>
+ </label>
+ <i ng-show="item.pending" class="icon-tickets-contested"></i>
+ </div>
+ </div>
+ <div class="gridster-box-content"
+ 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.openAddRoleModal(item)"
+ ng-hide="users.isLoadingTable && !users.getAppCatalogIsDone"
+ >
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+ <br>
+
+ <div style="text-align: right;">
+ To request access to an application, please visit the <a
+ ng-href="getAccess">Get Access</a> page.
+ </div>
+
+ <br>
+ </div>
+
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/catalog/catalogconfirmation.controller.js b/ecomp-portal-FE-common/client/app/views/catalog/catalogconfirmation.controller.js
new file mode 100644
index 00000000..83438e9c
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/catalog/information-box.tpl.html b/ecomp-portal-FE-common/client/app/views/catalog/information-box.tpl.html
new file mode 100644
index 00000000..a5b754f6
--- /dev/null
+++ b/ecomp-portal-FE-common/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 at MyLogins.
+ <br><br>
+ You may check this box if access is pending:
+ <input type="checkbox"
+ att-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>
diff --git a/ecomp-portal-FE-common/client/app/views/confirmation-box/admin-confirmation-box.tpl.html b/ecomp-portal-FE-common/client/app/views/confirmation-box/admin-confirmation-box.tpl.html
new file mode 100644
index 00000000..01b48de4
--- /dev/null
+++ b/ecomp-portal-FE-common/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 id="confirm-admin-update" class="btn btn-alt btn-small" ng-click="confirmBox.closeBox(true)">Confirm</div>
+ <div id="cancel-admin-update" class="btn btn-alt btn-small" ng-click="confirmBox.closeBox(false)">Cancel</div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/confirmation-box/confirmation-box.controller.js b/ecomp-portal-FE-common/client/app/views/confirmation-box/confirmation-box.controller.js
new file mode 100644
index 00000000..26d39a76
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/confirmation-box/confirmation-box.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.
+ * ================================================================================
+ */
+/**
+ * Created by nnaffar on 1/18/16.
+ */
+'use strict';
+(function () {
+ class ConfirmationBoxCtrl {
+ constructor($scope,$state) {
+
+ 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);
+
+ };
+
+ init();
+ }
+ }
+ ConfirmationBoxCtrl.$inject = ['$scope','$state'];
+ angular.module('ecompApp').controller('ConfirmationBoxCtrl', ConfirmationBoxCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/confirmation-box/confirmation-box.less b/ecomp-portal-FE-common/client/app/views/confirmation-box/confirmation-box.less
new file mode 100644
index 00000000..8beee1db
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/confirmation-box/confirmation-box.less
@@ -0,0 +1,35 @@
+.confirmation-box-container {
+ .confirmation-message-wrap {
+ display: table;
+ height: 60px;
+ width: 100%;
+
+ .confirmation-message {
+ .dGray14r;
+ display: table-cell;
+ vertical-align: middle;
+ max-width: 100px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ //line-height: 60px;
+ }
+ }
+
+ .confirmation-title-wrap {
+ display: table;
+ height: 20px;
+ width: 100%;
+
+ .confirmation-title {
+ .dGray14r;
+ 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-common/client/app/views/confirmation-box/confirmation-box.tpl.html b/ecomp-portal-FE-common/client/app/views/confirmation-box/confirmation-box.tpl.html
new file mode 100644
index 00000000..01313079
--- /dev/null
+++ b/ecomp-portal-FE-common/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="btn btn-alt btn-small" ng-click="confirmBox.closeBox(true)">OK</div>
+ <div id="confirmation-button-cancel" class="btn btn-alt btn-small" ng-click="confirmBox.closeBox(false)">Cancel</div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html b/ecomp-portal-FE-common/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html
new file mode 100644
index 00000000..a7a3fd46
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/confirmation-box/information-box.tpl.html b/ecomp-portal-FE-common/client/app/views/confirmation-box/information-box.tpl.html
new file mode 100644
index 00000000..912b971b
--- /dev/null
+++ b/ecomp-portal-FE-common/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" >{{confirmBox.message}}</div>
+ </div>
+ <div class="dialog-control">
+ <div class="btn btn-alt btn-small" ng-click="confirmBox.closeBox(false)">Close</div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-manage.html b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-manage.html
new file mode 100644
index 00000000..9883bd14
--- /dev/null
+++ b/ecomp-portal-FE-common/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-portal-abs-table" style="height:300px"">
+ <table b2b-table id="table-main"
+ table-data="ignoredTableData"
+ current-page="ignoredCurrentPage">
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="th-users-1" b2b-table-header key="title" sortable="true" >Title</th>
+ <th id="th-users-2" b2b-table-header key="href" sortable="true" >URL</th>
+ <th ng-show="{{widgetType=='EVENTS'}}" id="th-users-3" b2b-table-header key="event_date" sortable="true" >Date</th>
+ <th ng-show="{{widgetType=='EVENTS'}}" id="th-users-4" b2b-table-header key="content" sortable="true" >Content</th>
+ <th id="th-users-5" b2b-table-header key="sortOrder" sortable="true" >Order</th>
+ <th id="th-users-6" b2b-table-header key="edit" sortable="false" >Edit</th>
+ <th id="th-users-7" b2b-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 b2b-table-row type="body" class="table-body" track-by="$index" row-repeat="rowData in widgetData">
+ <tr id="tr-rowData" ng-click="">
+ <td b2b-table-body>
+ <div id="widget-td-title" ng-hide="rowData.showEdit" ng-bind="rowData.title"></div>
+ <input id="widget-input-title" type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].title"/>
+ </td>
+ <td b2b-table-body>
+ <div id="widget-td-href" ng-hide="rowData.showEdit" ng-bind="rowData.href"></div>
+ <input id="widget-input-href" type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].href"/>
+ </td>
+ <td ng-show="{{widgetType=='EVENTS'}}" b2b-table-body>
+ <div id="widget-td-date" ng-hide="rowData.showEdit" ng-bind="rowData.eventDate"></div>
+ <input id="widget-input-date" type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].eventDate"/>
+ </td>
+ <td ng-show="{{widgetType=='EVENTS'}}" b2b-table-body>
+ <div id="widget-td-content" ng-hide="rowData.showEdit">{{rowData.content | cut:true:20:' ...'}}</div>
+ <input id="widget-input-content" type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].content"/>
+ </td>
+ <td b2b-table-body>
+ <div id="widget-td-order" ng-hide="rowData.showEdit" ng-bind="rowData.sortOrder"></div>
+ <input id="widget-input-order" type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].sortOrder"/>
+ </td>
+ <td b2b-table-body>
+ <div class="delete-contact-us" ng-hide="rowData.showEdit" ng-click="setEdit($index);">
+ <span class="icon-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 b2b-table-body>
+ <div class="delete-contact-us" ng-click="remove($index);">
+ <span class="icon-misc-trash"></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 id="widget-input-add-title" 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 id="widget-input-add-url" 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 id="widget-input-add-order" 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 id="widget-input-add-date" 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-common/client/app/views/dashboard/dashboard-widget-parameter-manage.html b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameter-manage.html
new file mode 100644
index 00000000..59e6f4fa
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameter-manage.html
@@ -0,0 +1,86 @@
+<!--
+ ================================================================================
+ 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="manage-widget-parameters-header"></div>
+<div id="title" class="manage-widget-parameters-title">Manage
+ Widgets Parameters Page</div>
+<div class="widgets-popup-margin">
+ <div class="get-access-table">
+ <div class="table-control manage-widget-parameters-table">
+ <div class="c-ecomp-att-abs-table manage-widget-parameters-table-details">
+ <table b2b-table id="table-main" table-data="ignoredTableData"
+ current-page="ignoredCurrentPage" ng-show="isLoadingTable">
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="th-users-2" b2b-table-header key="href" sortable="true">Parameter
+ Key</th>
+ <th id="th-users-3" b2b-table-header key="event_date"
+ sortable="true">My Value</th>
+ <th id="th-users-4" b2b-table-header key="content"
+ sortable="true">Default Value</th>
+ <th id="th-users-5" b2b-table-header key="edit" sortable="false">Edit</th>
+ <th id="th-users-5" b2b-table-header key="edit" sortable="false">Reset to Default</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" class="table-body"
+ track-by="$index" row-repeat="rowData in widgetParam">
+ <tr id="tr-rowData">
+ <td b2b-table-body>
+ <div id="widget-parameter-td-parameter-key"
+ ng-bind="rowData.param_key"></div>
+ </td>
+ <td b2b-table-body>
+ <div id="widget-parameter-td-parameter-user-value"
+ ng-hide="rowData.showEdit" ng-bind="rowData.user_value"></div>
+ <input id="widget-input-user-value" type="text"
+ ng-show="rowData.showEdit"
+ ng-model="widgetParam[$index].user_value" />
+ </td>
+
+ <td b2b-table-body>
+ <div id="widget-parameter-td-parameter-default-value"
+ ng-bind="rowData.default_value"></div>
+ </td>
+ <td b2b-table-body>
+ <div ng-hide="rowData.showEdit" ng-click="setEdit($index);">
+ <span class="icon-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 b2b-table-body>
+ <div ng-click="resetDefault($index);">
+ <span class="icon-edit"></span>
+ </div>
+ </span>
+ </td>
+ </tr>
+ </tbody>
+
+ </table>
+ <div ng-show = "messageInfo">
+ There is no parameter assoicated with this widget!
+ </div>
+
+ </div>
+
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameters.controller.js b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameters.controller.js
new file mode 100644
index 00000000..460618ea
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameters.controller.js
@@ -0,0 +1,81 @@
+/*-
+ * ================================================================================
+ * 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 WidgetParameterController {
+ constructor($scope, widgetsCatalogService, userProfileService) {
+ let widgetId = $scope.ngDialogData.widgetId;
+ $scope.modflag = false;
+ $scope.isLoadingTable = false;
+ $scope.messageInfo = false;
+
+ widgetsCatalogService.getWidgetCatalogParameters(widgetId).then(res => {
+ if(res.status == 'OK'){
+ $scope.isLoadingTable = true;
+ $scope.widgetParam = res.response;
+ }else if(res.status == 'WARN'){
+ $scope.messageInfo = true;
+ }
+ }).catch(err => {
+ $log.error('WidgetParameterController::init error: ' + err);
+ })
+
+ $scope.setEdit = function(index) {
+ if($scope.modflag === false){
+ $scope.widgetParam[index].showEdit = true;
+ $scope.modflag = true;
+ }
+ }
+
+ $scope.resetDefault = function(index) {
+ var widgetParamObject = {};
+ widgetParamObject.user_value = $scope.widgetParam[parseInt(index)].default_value;
+ widgetParamObject.paramId = $scope.widgetParam[parseInt(index)].param_id;
+ widgetParamObject.widgetId = widgetId;
+ widgetsCatalogService.saveWidgetParameter(widgetParamObject)
+ .then(function(res){
+ if(res.status == 'OK'){
+ $scope.widgetParam[index].user_value = $scope.widgetParam[index].default_value;
+ }
+ });
+ }
+
+ $scope.modify = function(index) {
+
+ var widgetParamObject = {};
+ widgetParamObject.user_value = $scope.widgetParam[parseInt(index)].user_value;
+ widgetParamObject.paramId = $scope.widgetParam[parseInt(index)].param_id;
+ widgetParamObject.widgetId = widgetId;
+
+ widgetsCatalogService.saveWidgetParameter(widgetParamObject)
+ .then(function(res){
+ if(res.status == 'OK'){
+ $scope.modflag = false;
+ $scope.widgetParam[index].showEdit = false;
+ }
+ });
+
+ };
+ }
+ }
+ WidgetParameterController.$inject = ['$scope', 'widgetsCatalogService', 'userProfileService'];
+ angular.module('ecompApp').controller('WidgetParameterController', WidgetParameterController);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameters.controller.less b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameters.controller.less
new file mode 100644
index 00000000..ce63acd4
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget-parameters.controller.less
@@ -0,0 +1,28 @@
+.manage-widget-parameters-title{
+ color: #067ab4;
+ font-family: "Omnes-ECOMP-W02", Arial;;
+ font-size: 24px;
+ margin-left: 30px;
+ width: 1170px;
+ margin-top: 20px;
+ margin-bottom: 10px;
+}
+
+.manage-widget-parameters-table{
+ margin: 30px;
+}
+
+.manage-widget-parameters-table-details{
+ height: 300px;
+ background-color: #ffffff;
+}
+
+.manage-widget-parameters-header{
+ margin-top: 20px;
+ margin-bottom: 20px;
+ height:20px;
+}
+
+.ngdialog-content{
+ width: 40%;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget.controller.js b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget.controller.js
new file mode 100644
index 00000000..fe0bf453
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/dashboard/dashboard-widget.controller.less b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget.controller.less
new file mode 100644
index 00000000..8f326c42
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard-widget.controller.less
@@ -0,0 +1,82 @@
+.searchLiHeader {
+font-weight: bold;
+color: #0574ac;
+font-size: 16px;
+padding-bottom: 10px;
+line-height: 1.5;
+font-family: Omnes-ECOMP-W02,Arial;
+
+}
+
+.searchLiItems{
+cursor: pointer;
+font-weight: normal;
+font-size: 12px;
+color: #444444;
+font-family: Omnes-ECOMP-W02,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-common/client/app/views/dashboard/dashboard.controller.js b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard.controller.js
new file mode 100644
index 00000000..5a763e27
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard.controller.js
@@ -0,0 +1,504 @@
+/*-
+ * ================================================================================
+ * 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(conf, applicationsService, $log,
+ $window, userProfileService, $scope, $cookies, $timeout, $interval,
+ $modal, $state, beReaderService, dashboardService, confirmBoxService,
+ auditLogService,ngDialog, $compile, widgetsCatalogService) {
+
+ this.conf = conf;
+ var _this = this;
+
+ _classCallCheck(this, DashboardCtrl);
+
+ // activate spinner
+ this.isLoading = true;
+ this.isCommError = false;
+ $scope.getUserAppsIsDone = false;
+ this.userProfileService = userProfileService;
+ $scope.demoNum = 1;
+ $scope.event_content_show = false;
+ $scope.widgetData = [];
+ $scope.activateThis = function(ele){
+ $compile(ele.contents())($scope);
+ $scope.$apply();
+ };
+
+ $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();
+ }
+ });
+ };
+
+ $scope.editWidgetParameters = function(widgetId) {
+ let data = {
+ widgetId: widgetId
+ }
+ ngDialog.open({
+ templateUrl : 'app/views/dashboard/dashboard-widget-parameter-manage.html',
+ controller : 'WidgetParameterController',
+ data: data
+ }).closePromise.then(needUpdate => {
+
+ });
+ };
+
+ $scope.sort_options = [
+ {index: 0, value: 'N', title: 'Name'},
+ {index: 1, value: 'L', title: 'Last used'},
+ {index: 2, value: 'F', title: 'Most used'},
+ {index: 3, value: 'M', title: 'Manual'}
+ ];
+
+ $scope.selectedSortTypeChanged = function(userAppSortTypePref) {
+ $scope.appsViewData = [];
+ $scope.appsView = [];
+
+ $scope.sort_type = userAppSortTypePref;
+
+ applicationsService
+ .getAppsOrderBySortPref(userAppSortTypePref)
+ .then(function(res) {
+ _this.apps = res;
+ $scope.applyPresentationDetailsToApps(_this.apps);
+ })
+ applicationsService
+ .saveAppsSortTypePreference($scope.selectedSortType)
+ .then(function(res) {
+ // Nothing to do
+ })
+
+ }
+ $scope.$watch('selectedSortType.value', (newVal, oldVal) => {
+ for(var i=0;i<$scope.sort_options.length;i++){
+ if($scope.sort_options[i].value==newVal){
+ $scope.selectedSortType=angular.copy($scope.sort_options[i]);;
+ }
+ }
+ });
+
+ $scope.restoreSortSelected = function(){
+ confirmBoxService.confirm("Restore the default size and position of all widgets?").then(
+ function(confirmed){
+ var checkConfirm = confirmed;
+ if(checkConfirm === true){
+ applicationsService
+ .delWidgetsSortPref($scope.widgetsViewData).then(function(){
+ $state.reload();
+ });
+ }
+ });
+ /* if(confirm('Restore the default size and position of all widgets?') == true)
+ {
+ applicationsService
+ .delWidgetsSortPref($scope.widgetsViewData).then(function(){
+ $state.reload();
+ })
+ }*/
+
+ }
+
+ $scope.applyPresentationDetailsToApps = function(appsReturned) {
+ var rowNo = 0;
+ for (var i = 0; i < _this.apps.length; i++) {
+ $scope.appsView[i] = {
+ sizeX : 1,
+ sizeY : 1,
+ headerText : '',
+ subHeaderText : '',
+ imageLink : '',
+ order : '',
+ url : '',
+ appid: '',
+ };
+ $scope.appsView[i].headerText = appsReturned[i].name;
+ $scope.appsView[i].subHeaderText = appsReturned[i].notes;
+ let imgLnk = '';
+ if (appsReturned[i].imageUrl)
+ imgLnk = conf.api.appThumbnail.replace(':appId', appsReturned[i].id);
+ //$log.debug('DashboardCtlr::applyPresn: imgLink = ' + imgLnk);
+ $scope.appsView[i].imageLink = imgLnk;
+ $scope.appsView[i].order = appsReturned[i].order;
+ $scope.appsView[i].url = appsReturned[i].url;
+ $scope.appsView[i].restrictedApp = appsReturned[i].restrictedApp;
+ $scope.appsView[i].appid = appsReturned[i].id;
+ }
+ $scope.appsView[_this.apps.length] = {
+ addRemoveApps : true,
+ sizeX : 1,
+ sizeY : 1,
+ headerText : 'Add/Remove Applications',
+ subHeaderText : '',
+ imageLink : 'assets/images/cloud.png',
+ order : '',
+ restrictedApp : false,
+ url : '',
+ };
+ if($scope.appsView.length>6){
+ $(".dashboard-boarder").css({
+ "height" : "400px"
+ });
+ }else{
+ $(".dashboard-boarder").css({
+ "height" : "210px"
+ });
+ }
+
+ if ($scope.appsView != undefined
+ && $scope.appsView != null
+ && $scope.appsView.length > 0) {
+ $scope.appsViewData = $scope.appsView;
+ }
+ }
+
+$scope.widgetsView = [];
+
+ $scope.applyPresentationDetailsToWidgets = function(widgetsReturned){
+ var rowNo = 0;
+ for (var i = 0; i < widgetsReturned.length; i++) {
+ $scope.widgetsView[i] = {
+ sizeX : '',
+ sizeY :'',
+ headerText:'',
+ widgetText:'',
+ widgetIdentifier : '',
+ url : '',
+ widgetid: '',
+ attrb:'',
+ row:'',
+ col:'',
+ };
+ $scope.widgetsView[i].widgetid = widgetsReturned[i].id;
+ $scope.widgetsView[i].headerText = widgetsReturned[i].headerName;
+ $scope.widgetsView[i].widgetText = widgetsReturned[i].name;
+
+ if(widgetsReturned[i].headerName.toLowerCase() === 'news'){
+ $scope.widgetsView[i].widgetIdentifier = 'NEWS';
+ }
+ else
+ if(widgetsReturned[i].headerName.toLowerCase() === 'resources'){
+ $scope.widgetsView[i].widgetIdentifier = 'IMPORTANTRESOURCES';
+ }
+ else
+ if(widgetsReturned[i].headerName.toLowerCase() === 'events'){
+ $scope.widgetsView[i].widgetIdentifier = 'EVENTS';
+ }
+
+ $scope.widgetsView[i].url = widgetsReturned[i].url;
+ $scope.widgetsView[i].attrb = widgetsReturned[i].attrs;
+ if(widgetsReturned[i].width === null){
+ $scope.widgetsView[i].sizeX = 2;
+ }else{
+ $scope.widgetsView[i].sizeX = widgetsReturned[i].width;
+ }
+ if(widgetsReturned[i].height === null){
+ $scope.widgetsView[i].sizeY = 2;
+ }else{
+ $scope.widgetsView[i].sizeY = widgetsReturned[i].height;
+ }
+ $scope.widgetsView[i].row = widgetsReturned[i].x;
+ $scope.widgetsView[i].col = widgetsReturned[i].y;
+ }
+ if ($scope.widgetsView != undefined
+ && $scope.widgetsView != null
+ && $scope.widgetsView.length > 0) {
+ $scope.widgetsViewData = $scope.widgetsView;
+ }
+ }
+
+ applicationsService
+ .getUserAppsSortTypePreference().then(function(res) {
+ var resJson = {};
+ resJson.value = res;
+ if (resJson.value==="N" || resJson.value==="") {
+ resJson.index = 0;
+ resJson.title = 'Name';
+ }else if (resJson.value==="L") {
+ resJson.index = 1;
+ resJson.title = 'Last used';
+ }else if(resJson.value==="F"){
+ resJson.index = 2;
+ resJson.title = 'Most used';
+ }else {
+ resJson.index = 3;
+ resJson.title = 'Manual';
+ }
+ $scope.selectedSortType = resJson;
+ $scope.selectedSortTypeChanged(res);
+
+
+ });
+
+ $scope.widgetsList = [];
+
+ let getUserWidgets = (loginName) => {
+ var conf = this.conf;
+ widgetsCatalogService.getUserWidgets(loginName).then(res => {
+ if(!(res instanceof Array)){
+ this.isCommError = true;
+ return;
+ }
+ for(var i = 0; i < res.length; i++){
+ var widget_id = res[i][0];
+ var widget_name = res[i][1];
+ let url = this.conf.api.widgetCommon + "/" + widget_id + "/framework.js";
+ var header_name = widget_name;
+ if(res[i][7] == 1){
+ header_name = (widget_name.length > 9) ?widget_name.substring(0, 8) + '...' : widget_name;
+ }
+ if(res[i][4] === "S" || res[i][4] === null ){
+ $scope.widgetsList.push({
+ id: widget_id,
+ headerName: header_name,
+ name: widget_name,
+ url: url,
+ attrs: [{attr: 'data-' + res[i][0], value: ''}],
+ x: res[i][3],
+ y: res[i][5],
+ height: res[i][6],
+ width: res[i][7]
+ });
+ }
+ var script = document
+ .createElement('script');
+ script.src = url;
+ script.async = false;
+ var entry = document
+ .getElementsByTagName('script')[0];
+ entry.parentNode
+ .insertBefore(script, entry);
+ }
+ $scope.applyPresentationDetailsToWidgets($scope.widgetsList);
+ }).catch(err => {
+ $log.error('WidgetsHomeCtrl::getUserWidgets error: ' + err);
+ }).finally(()=> {
+
+ });
+ };
+
+ userProfileService.getUserProfile().then(
+ function(profile) {
+ $scope.orgUserId = profile.orgUserId;
+ getUserWidgets($scope.orgUserId);
+ });
+
+ /* Widget Gridster Section */
+ $scope.newsGridsterItem = {
+ sizeX : 1,
+ sizeY : 1,
+ headerText : 'News',
+ subHeaderText : '',
+ imageLink : '',
+ order : '',
+ url : ''
+ };
+
+ $scope.eventsGridsterItem = {
+ sizeX : 1,
+ sizeY : 1,
+ headerText : 'Events',
+ subHeaderText : '',
+ imageLink : '',
+ order : '',
+ url : ''
+ };
+
+ $scope.impoResGridsterItem = {
+ sizeX : 1,
+ sizeY : 1,
+ headerText : 'Resources',
+ subHeaderText : '',
+ imageLink : '',
+ order : '',
+ url : ''
+ };
+
+ this.gridsterAppOpts = {
+ columns : 6,
+ colWidth : 190,
+ rowHeight : 190,
+ margins : [ 20, 20 ],
+ outerMargin : true,
+ pushing : true,
+ floating : true,
+ swapping : true,
+ resizable: {
+ enabled: false,
+ },
+ draggable : {
+ handle:'.icon-content-gridguide',
+ stop: function stop() {
+ $scope.defaultSortBy = function() {
+ var resJson = {};
+ resJson.value = 'M';
+ resJson.index = 3;
+ resJson.title = 'Manual';
+ $scope.selectedSortType = resJson;
+ applicationsService.saveAppsSortTypeManual($scope.appsViewData)
+ applicationsService.saveAppsSortTypePreference($scope.selectedSortType)
+ }
+ $scope.defaultSortBy();
+ }
+ }
+ };
+
+ this.gridsterWidgetOpts = {
+ columns : 6,
+ colWidth : 190,
+ rowHeight : 190,
+ margins : [ 20, 20 ],
+ outerMargin : true,
+ pushing : true,
+ floating : true,
+ swapping : true,
+ resizable: {
+ enabled: true,
+ stop: function stop(event, uiWidget, $element){
+ if($element.sizeX == 1)
+ $element.headerText = ($element.widgetText.length > 9) ? $element.widgetText.substring(0, 8) + '...' : $element.widgetText;
+ if($element.sizeX >= 2)
+ $element.headerText = $element.widgetText;
+
+ applicationsService
+ .saveWidgetsSortManual($scope.widgetsViewData)
+
+ }
+ },
+ draggable : {
+ handle:'.icon-content-gridguide',
+ stop: function stop(){
+ applicationsService
+ .saveWidgetsSortManual($scope.widgetsViewData)
+
+ }
+ }
+ };
+
+ this.goToCatalog = function(item) {
+ $state.go('root.appCatalog');
+ }
+
+ this.goToWidgetCatLog = function(item) {
+ $state.go('root.widgetCatalog');
+ }
+
+ // 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 urlParts = item.url.split('#');
+ var appUrl = null;
+ if (urlParts.length < 2) {
+ // no #
+ let urlLastChar = item.url.charAt(item.url.length - 1);
+ if (item.url.includes("?"))
+ appUrl = (urlLastChar === '&' ? item.url + ccParam : item.url + '&' + ccParam);
+ else
+ appUrl = item.url + '?' + ccParam;
+ } else {
+ // has #
+ let urlLastChar = urlParts[0].charAt(urlParts[0].length - 1);
+ if (item.url.includes("?"))
+ appUrl = (urlLastChar === '&' ? urlParts[0] + ccParam + '#' + urlParts[1] : urlParts[0] + '&' + ccParam + '#' + urlParts[1]);
+ 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,
+ appId: item.appId
+ };
+ $cookies.putObject('addTab', tabContent);
+ }
+
+ };
+
+ this.auditLog = function(app) {
+ console.log(app);
+ auditLogService.storeAudit(app.appid,'app',app.url);
+ };
+
+ if (getParameterByName('noUserError') != null) {
+ if (getParameterByName('noUserError') == "Show") {
+ $("#errorInfo").show();
+ }
+ }
+ };
+
+ DashboardCtrl.$inject = [ 'conf', 'applicationsService', '$log', '$window',
+ 'userProfileService', '$scope', '$cookies', '$timeout', '$interval',
+ '$modal', '$state', 'beReaderService', 'dashboardService', 'confirmBoxService',
+ 'auditLogService', 'ngDialog', '$compile', 'widgetsCatalogService' ];
+ 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-common/client/app/views/dashboard/dashboard.controller.spec.js b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard.controller.spec.js
new file mode 100644
index 00000000..0c05a816
--- /dev/null
+++ b/ecomp-portal-FE-common/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', orgUserId: '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-common/client/app/views/dashboard/dashboard.less b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard.less
new file mode 100644
index 00000000..bf6530b4
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard.less
@@ -0,0 +1,901 @@
+.w-ecomp-dashboard-home {
+ .bg_portalWhite;//white for 1702
+ 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;
+ }
+
+ .dashboard-home-container {
+ position: relative;
+ padding-right: 0;
+ padding-left: 0;
+ padding-bottom: @container-bottom;
+
+ .dashboard-home-title {
+ .blackText24m;
+ margin: auto;
+ .content_justify;
+ }
+
+ .portals-list {
+ min-height: 70vh;
+ //display: flex;
+ justify-content: center;
+ flex-flow: row wrap;
+ width: @table-width;
+ //margin-left: 230px;
+ margin-bottom: 63px;
+ margin:auto;
+ .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 {
+ .blackText24m;
+ margin-bottom: 4px;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ .info-description {
+ .portalDBlue16r;
+ 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{
+ .portalRed;
+ position: absolute;
+ width: 100%;
+ line-height: 1.5em;
+}
+.w-ecomp-main-disclaimer {
+ text-align: center;
+ .dGray14r;
+ //position: absolute;
+ bottom: -75px;
+ line-height: 1.5em;
+ margin: 0 auto;
+ width:1170px;
+ position: relative;
+
+}
+
+@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{
+ // border: 2px solid #ccc!important;
+ // border-radius: 16px!important;
+ 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: Omnes-ECOMP-W02-Bold,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;
+}
+
+
+/*hover*/
+
+
+.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*/
+
+/*We are done :)*/
+
+
+
+.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: "Omnes-ECOMP-W02", Arial;
+}
+.events-link{
+ color: #067ab4 !important;
+}
+
+
+/*widgets*/
+
+.handle-e{
+ width: 5px;
+}
+
+.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 !important;
+ 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: 3px;
+ 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;
+}
+/*news*/
+
+@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;
+ // overflow: auto;
+ border-radius: 4px;
+ padding: 3px;
+ -webkit-user-select: none;
+
+}
+
+
+
+.news ul {
+ float: left;
+ width:100%;
+ // animation: ticker 25s cubic-bezier(1, 0, .5, 0) infinite;
+ -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;
+}
+
+.news ul:hover { animation-play-state: paused }
+.news span:hover+ul { animation-play-state: paused }
+/*resources*/
+.resources {
+
+ width: 100%;
+ 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;
+}
+
+
+/* OTHER COLORS */
+.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 }
+
+
+/*broadcast*/
+
+.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;
+}
+
+.gridster-item{
+ z-index:0 !important;
+}
+.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*/
+
+.information-section{
+ /*margin-top:25px;*/
+}
+.information-section-gridsterContent{
+ /*height:300px;*/
+}
+.information-sections-gridster-header{
+ color: #0574ac;
+ font-family: Omnes-ECOMP-W02-Bold,Arial;
+ font-size: 25px;
+
+}
+/*application empty div*/
+.app-error-block {
+ padding-top: 10px;
+
+}
+/*news empty div*/
+.activity-error-block {
+ padding-top: 60px;
+
+}
+
+.activity-error-msg1{
+ text-align: center;
+ margin-top: 20px;
+ font-family: "Omnes-ECOMP-W02", 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; }
+
+// #newsContainer{
+// overflow:auto;
+// height: 100%;
+// }
+
+/*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;
+}
+
+.dashboardSortHeader{
+ margin-left: 756px;
+}
+
+#dashboardAddWidgetPreference{
+ display: inline-block;
+ font-size: 14px;
+ color: #3e3e3e;
+ width: 69%;
+ text-align: center;
+ padding: 15px 0px 15px 0px;
+ font-family: "Omnes-ECOMP-W02", Arial;
+}
+
+#dashboardAddWidgetPreference:hover{
+ background-color: #0568ae;
+ color:white !important;
+}
+
+#dashboardDefaultPreference{
+ display: inline-block;
+ font-size: 14px;
+ color: #3e3e3e;
+ width: 30%;
+ text-align: center;
+ padding: 15px 0px 15px 0px;
+ font-family: "Omnes-ECOMP-W02", Arial;
+}
+
+#dashboardDefaultPreference:hover{
+ background-color: #0568ae;
+ color:white !important;
+}
+
+.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: "Omnes-ECOMP-W02", 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;
+}
+
+/* apps gridsters */
+ul {
+ list-style: none;
+}
+.gridster-box {
+ height: 100%;
+ border: 1px solid #ccc;
+ background-color: #fff;
+ transition: transform 0.5s ease-out;
+}
+.gridster-box-header {
+ background-color: #fff;
+ padding: 0 0px 0 10px;
+ border-bottom: 1px solid #ccc;
+ position: relative;
+ height: 50px !important;
+}
+.gridster-box-header h3 {
+ margin-top: 15px;
+ display: inline-block;
+ font-family: "Omnes-ECOMP-W02", Arial;
+}
+
+.gridster-box-header i {
+font-size: 22px;
+}
+
+.gridster-box-content {
+ padding: 59px;
+}
+.gridster-box:hover{
+ transform: scale(1.1);
+}
+.gridster-box-header-btns {
+ top: 15px;
+ right: 10px;
+ position: absolute;
+}
+
+/*** widgets ***/
+ul {
+ list-style: none;
+}
+.box {
+ height: 100%;
+ border: 1px solid #ccc;
+ background-color: #fff;
+ font-family: "Omnes-ECOMP-W02", Arial;
+}
+.box-header {
+ height : 50px;
+ background-color: #fff;
+ padding: 0 30px 0 10px;
+ border-bottom: 1px solid #ccc;
+ position: relative;
+}
+.box-header h3 {
+ margin-top: 15px;
+ display: inline-block;
+ font-size: 16px;
+}
+.box-content {
+ position: absolute;
+ width: 100%;
+ top: 50px;
+ left: 0;
+ right: 0;
+ bottom: 29px;
+ border: 1px solid #d3d3d3;
+ box-sizing: border-box;
+ overflow-y: auto;
+ overflow-x: hidden;
+ color: #444444;
+ bottom: 0px;
+}
+.box-header-btns {
+ top: 10px;
+ right: 10px;
+ cursor: pointer;
+ position: absolute;
+}
+
+#widget-boarder{
+ background-color: #eee;
+ border: 1px dashed white;
+}
+.icon-content-gridguide{
+cursor:move;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/dashboard/dashboard.tpl.html b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard.tpl.html
new file mode 100644
index 00000000..5e67526e
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/dashboard/dashboard.tpl.html
@@ -0,0 +1,179 @@
+<!--
+ ================================================================================
+ 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-dashboard-home" id="widgets">
+ <div class="dashboard-home-container" id="page-content">
+ <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
+ class="dashboardSortHeader">Sort by:</span></span>
+ <div style="float: right; margin: 7px 7px 7px 0px;"
+ class="form-field" att-select="sort_options"
+ ng-model="selectedSortType"
+ id="sort-by-{{selectedSortType.value}}"
+ ng-change="selectedSortTypeChanged(selectedSortType.value)"></div>
+ </div>
+ <div class="dashboard-boarder">
+ <div class="gridster-container override_background">
+ <div ng-if="appsViewData.length==0">
+ <div class="app-error-block">
+ <i class="icon-information full-linear-icon-information"
+ style="margin-left: 50%; font-size: 90px; color: black"></i> <br>
+ <div class="activity-error-msg1">
+ You do not have access to any application or function in ECOMP
+ Portal. <br> Please request access via <a
+ href="https://mylogins.cso.att.com/index.cfm"
+ target="_mylogins">MyLogins</a>.
+ </div>
+ </div>
+ </div>
+
+ <div ng-if="appsViewData.length>0" id="page-content"
+ class="content" gridster="dashboard.gridsterAppOpts">
+ <ul>
+ <li gridster-item="item" ng-repeat="item in appsViewData">
+ <div class="gridster-box" ng-if="item.addRemoveApps == null">
+ <div class="gridster-box-header">
+ <i class="icon-content-gridguide"></i>
+ <h3 style="cursor: context-menu">{{item.headerText |
+ elipsis: 14}}</h3>
+ </div>
+ <div class="gridster-box-content"
+ ng-style="{'cursor':'pointer',
+ 'background-image': 'url('+(item.imageLink)+')',
+ 'background-color':'white',
+ 'background-repeat': 'no-repeat',
+ 'background-size': '170px 130px'}"
+ ng-click="dashboard.goToPortal(item);dashboard.auditLog(item)"
+ ng-hide="users.isLoadingTable && !users.getUserAppsIsDone">
+ </div>
+ </div>
+ <div class="gridster-box" ng-if="item.addRemoveApps">
+ <div class="gridster-box-header" style="cursor: pointer;"
+ ng-click="dashboard.goToCatalog()">
+ <i class="icon-content-gridguide"></i>
+ <h3>Select applications...</h3>
+
+ </div>
+ <div class="gridster-box-content"
+ ng-style="{'cursor':'pointer',
+ 'order': item.order,
+ 'text-align': 'center',
+ 'background-color':'white',
+ 'background-repeat': 'no-repeat',
+ 'background-size': '170px 130px',
+ 'padding': '4px',
+ 'opacity': '1'}"
+ ng-click="dashboard.goToCatalog()" class="gridsterContent">
+ <label style="font-size: 12px;">Click
+ here to personalize <br> this applications page
+ </label> <i class="icon-controls-add-maximize"
+ style="font-size: 80px; color: #067ab4"></i>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+
+ </div>
+
+ </div>
+ </div>
+
+ <br> <br>
+
+
+ <div style="font-family: Omnes-ECOMP-W02;"
+ ng-show=dashboard.isCommError>Failed to communicate with the
+ widget microservice.</div>
+
+ <div id="widget-boarder" class="content" gridster="dashboard.gridsterWidgetOpts">
+ <ul>
+ <li gridster-item="widget" ng-repeat="widget in widgetsViewData">
+ <div class="box">
+ <div class="box-header">
+ <i style="cursor:move;" class="icon-content-gridguide"></i>
+ <h3>{{ widget.headerText}}</h3>
+ <div class="box-header-btns pull-right"
+ ng-if="isAdminPortalAdmin == true" class="ng-scope">
+ <a id="widgetHeaderBtns"
+ class="widgetHeaderBtn widgetHeaderBtnPosition singleBtnBorder"
+ ng-class="singleBtnBorder" alt="Settings"
+ ng-mousedown="showImpResOption=!showImpResOption"
+ ng-mouseleave="showImpResOption=false" title="Settings"
+ ddh-accessibility-click="13,32" tabindex="0"><span
+ style="color: #888;"><img
+ ng-src="assets/images/generic.png"
+ style="margin-bottom: 3px; margin-left: 3px;" alt="..."
+ aria-label="Tap or Click to move"
+ src="assets/images/generic.png"></span></a>
+
+ <ul class="optionsMenu"
+ style="display: block"
+ ng-show="showImpResOption"
+ ng-mouseenter="showImpResOption=true"
+ ng-mouseleave="showImpResOption=false">
+ <!-- ngIf: widget.duplicateAllowed -->
+ <li ng-show="widget.widgetIdentifier != ''"
+ ng-click="editWidgetModalPopup(importResData, widget.widgetIdentifier);">
+ <a class="optionsMenuLink" href="javascript:void(0)"
+ ddh-accessibility-click="13,32">Edit</a>
+ </li>
+
+ <li
+ ng-click="editWidgetParameters(widget.widgetid);">
+ <a class="optionsMenuLink" href="javascript:void(0)"
+ ddh-accessibility-click="13,32">Parameters</a>
+ </li>
+
+ <!-- end ngIf: widget.duplicateAllowed -->
+ </ul>
+
+ </div>
+ </div>
+ <div class="box-content">
+ <div dyn-attr="widget.attrb"></div>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+ <div></div>
+ <div class="gridster-container override_background">
+ <div id="dashboardAddWidgetPreference"
+ ng-style="{'cursor':'pointer'}"
+ ng-click="dashboard.goToWidgetCatLog()">
+ <i class="icon-controls-add-maximize"></i> Add Widget
+ </div>
+ &nbsp;
+ <div id="dashboardDefaultPreference" ng-style="{'cursor':'pointer'}"
+ ng-click="restoreSortSelected()">
+ <i class="icon-arrows-replay-restart"></i> Reset Widget Layout
+ </div>
+ </div>
+ </div>
+
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/dashboard/newsticker.controller.js b/ecomp-portal-FE-common/client/app/views/dashboard/newsticker.controller.js
new file mode 100644
index 00000000..0efbaac4
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/dashboard/newsticker.controller.js
@@ -0,0 +1,52 @@
+/*-
+ * ================================================================================
+ * 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({
+
+ // timer period
+ period: 30,
+
+ // offset pixel count
+ offset: 1,
+
+ // mousewheel scrolling
+ mousewheel: true,
+
+ // mousewheel offset pixel count
+ mousewheelRate: 30,
+
+ // dragging tape content
+ dragable: true,
+
+ heightSpy: true
+
+ });
+ };
+ $scope.newstape();
+
+ }
+ }
+ NewsTickerController.$inject = ['$scope'];
+ angular.module('ecompApp').controller('NewsTickerController', NewsTickerController);
+})(); \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less b/ecomp-portal-FE-common/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less
new file mode 100644
index 00000000..0d5e98fc
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less
@@ -0,0 +1,97 @@
+.functionalMenu-details-modal {
+ height: 430px;
+
+ input:not([type="button"]) {
+ height: 13px;
+ }
+
+ .title {
+ //.n18r;
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @blue-active 3px solid;
+
+ }
+.btn-group {
+ display: table-cell;
+ max-width: 250px;
+ min-width: 290px;
+ background-color: #f2f2f2;
+ }
+ .btn-medium{
+ width:168px;
+ }
+
+
+
+ .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{
+ .dGray14r;
+ }
+
+ .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: @funcRed;
+ font-size: 9px;
+ }
+ .valid-message{
+ color: @funcGreen;
+ font-size: 9px;
+ }
+ }
+
+ }
+
+ }
+
+}
diff --git a/ecomp-portal-FE-common/client/app/views/functionalMenu/functionalMenu.less b/ecomp-portal-FE-common/client/app/views/functionalMenu/functionalMenu.less
new file mode 100644
index 00000000..8452590f
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/functionalMenu/functionalMenu.less
@@ -0,0 +1,60 @@
+.functional-menu-main{
+ .bg_portalWhite;//white for 1702
+ 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;
+
+ .functional-menu-container {
+ // .portalWhite16r; // font color and size
+ position: relative;
+ padding-right: 0;
+ padding-left: 0;
+ padding-bottom: @container-bottom;
+ background-color:#ffffff;
+
+ //width: @table-width;
+ .btn-blue{
+ margin-left:0px;
+ }
+
+ .tree {
+ margin:auto;
+ width: @table-width;
+ //margin-left: @table-margin-left;
+ //margin: 0 auto 60px;
+ font-size:16px;
+
+ }
+
+ .functional-admin-button-container {
+ padding-top: 10px;
+ width: @table-width;
+ margin:auto;
+ }
+
+ .error-text {
+ width: @table-width;
+ margin: auto;
+ padding: 20px;
+ left: 20px;
+ font-weight: bold;
+ font-size: 16px;
+ text-align: left;
+ color: @err;
+ background-color: @portalWhite;
+
+ .error-help {
+ color: @portalDGray;
+ font-weight: normal;
+ }
+ }
+
+
+ }
+
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/header/header.controller.js b/ecomp-portal-FE-common/client/app/views/header/header.controller.js
new file mode 100644
index 00000000..961362a5
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/header/header.controller.js
@@ -0,0 +1,449 @@
+/*-
+ * ================================================================================
+ * 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,auditLogService,notificationService) {
+ 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.notificationCount=0;
+ $scope.showNotification = true;
+
+ $scope.hideMenus = false;
+
+ $scope.menuItems = [];
+ $scope.activeClickSubMenu = {
+ x: ''
+ };
+ $scope.activeClickMenu = {
+ x: ''
+ };
+ $scope.megaMenuDataObject =[];
+ $scope.notificationCount= notificationService.notificationCount;
+ this.isLoading = true;
+ this.ECOMP_URL_REGEX = ECOMP_URL_REGEX;
+
+ 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;
+ }
+
+ userProfileService.getFunctionalMenuStaticInfo()
+ .then(res=> {
+ // $log.debug('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: failed to get all required data, trying user profile');
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ // $log.debug('HeaderCtrl:: getting userinfo from session success');
+ this.firstName = profile.firstName;
+ this.lastName = profile.lastName;
+ // $log.debug('HeaderCtrl::getFunctionalMenuStaticInfo: user has the following roles: ' + profile.roles);
+ }).catch(err=> {
+ $log.error('Header Controller:: getUserProfile() failed: ' + err);
+ });
+ } else {
+ // $log.debug('HeaderCtrl: fetched Functional Menu Static Info successfully',res);
+ this.firstName = res.firstName;
+ this.lastName = res.lastName;
+ }
+
+ menusService.GetFunctionalMenuForUser()
+ .then(jsonHeaderMenu=> {
+ $scope.menuItems = unflatten( jsonHeaderMenu );
+ $scope.megaMenuDataObject = $scope.menuItems;
+ }).catch(err=> {
+ $log.error('HeaderCtrl::GetFunctionalMenuForUser: HeaderCtrl json returned: ' + err);
+ });
+
+ }).catch(err=> {
+ $log.error('HeaderCtrl::getFunctionalMenuStaticInfo failed: ' + err);
+ });
+
+ //store audit log
+ $scope.auditLog = function(app,type) {
+ var comment = 'type: '+type+ ',title: '+app.text+",url: "+app.url;
+ auditLogService.storeAudit(app.appid,'functional',comment);
+ };
+
+ $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.error('HeaderCtrl::goToUrl has started',item);
+ let url = item.url;
+ let restrictedApp = item.restrictedApp;
+ if (!url) {
+ $log.warn('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);
+ } else {
+ var tabContent = { id: new Date(), title: item.text, url: item.url,appId:item.appid };
+ $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()');
+ } else {
+ // $log.debug('submenuLevelAction is NOT calling generateFavoriteItems()');
+ }
+ // $log.debug('item hovered: ' + index + '; column = ' + column);
+ // if (column == 2) { // 2 is Design
+ // // This is an admitted hack. See aw3218 for reasons why
+ // $log.debug('submenuLevelAction column == 2');
+ // $scope.favoritesWindow = false;
+ // $scope.showFavorites = false;
+ // $scope.emptyFavorites = false;
+ // }
+ if (index=='Favorites' && $scope.favoriteItemsCount != 0) {
+ // $log.debug('HeaderCtrl::submenuLevelAction: Showing Favorites window');
+ // generateFavoriteItems();
+ $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');
+ // generateFavoriteItems();
+ $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.thirdFourthMenus = true;
+ }
+
+ $scope.isUrlFavorite = function (menuId) {
+ // $log.debug('array objects in menu favorites = ' + $scope.favoriteItemsCount + '; menuId=' + menuId);
+ var jsonMenu = JSON.stringify($scope.favoritesMenuItems);
+ var isMenuFavorite = jsonMenu.indexOf('menuId\":' + menuId);
+ // $log.debug('jsonMenu.indexOf(menuId:' + jsonMenu.indexOf('menuId\":'+menuId));
+ // $log.debug('isMenuFavorite= ' + isMenuFavorite);
+ if (isMenuFavorite==-1) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+
+ let generateFavoriteItems = () => {
+ menusService.getFavoriteItems()
+ .then(favorites=> {
+ // $log.debug('HeaderCtrl.getFavoriteItems:: ' + JSON.stringify(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(() => {
+ // var elementId = '#'+ event.currentTarget.id;
+ 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.warn('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');//, '_self'
+ }
+ else {
+ var tabContent = { id: new Date(), title: headerText, url: url };
+ $cookies.putObject('addTab', tabContent );
+ }
+ };
+
+ }
+ }
+ class LoginSnippetCtrl {
+ constructor($log, $scope, $cookies, $timeout, userProfileService, sessionService) {
+ $scope.firstName="";
+ $scope.lastName="";
+ $scope.displayUserAppRoles=false;
+ $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 individual applications to log out before the portal logout
+ $timeout(function() {
+ window.location = "logout.htm";
+ }, 2000);
+ }
+
+
+ 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: failed in getFunctionalMenuStaticInfo: ' + err);
+ });
+ } catch (err) {
+ $log.error('HeaderCtrl::LoginSnippetCtrl caught exception: ' + err);
+ }
+
+ $scope.getUserApplicationRoles= function(){
+ $scope.userapproles = [];
+ if($scope.displayUserAppRoles)
+ $scope.displayUserAppRoles = false;
+ else
+ $scope.displayUserAppRoles = true;
+
+ userProfileService.getUserAppRoles($scope.loginSnippetUserid)
+ .then(res=>{
+
+ for(var i=0;i<res.length;i++){
+ var userapprole ={
+ App:res[i].appName,
+ Roles:res[i].roleNames,
+ };
+
+ $scope.userapproles.push(userapprole);
+ }
+
+ });
+
+ }
+ }
+ }
+ class NotificationCtrl{
+ constructor($log, $scope, $cookies, $timeout, sessionService,notificationService,$interval,ngDialog) {
+ $scope.notifications=[];
+ var intervalPromise = null;
+ $scope.notificationCount= notificationService.notificationCount;
+
+ $scope.getNotification = function(){
+ notificationService.getNotification()
+ .then(res=> {
+ notificationService.decrementRefreshCount();
+ var count = notificationService.getRefreshCount();
+ if (res==null || res.data==null || res.data.message!='success') {
+ $log.error('NotificationCtrl::updateNotifications: failed to get notifications');
+ if (intervalPromise != null)
+ $interval.cancel(intervalPromise);
+ } else if(count<=0){
+ if (intervalPromise != null)
+ $interval.cancel(intervalPromise);
+ } else {
+ $scope.notifications = [];
+ notificationService.setNotificationCount(res.data.response.length);
+ for(var i=0;i<res.data.response.length;i++){
+ var data = res.data.response[i];
+ var notification ={
+ id:data.notificationId,
+ title:data.msgHeader,
+ message:data.msgDescription,
+ source:data.msgSource,
+ time:data.createdDate,
+ priority:data.priority
+ };
+ $scope.notifications.push(notification);
+ }
+ }
+ }).catch(err=> {
+ $log.error('NotificationCtrl::getNotification: caught exception: ' + err);
+ if (intervalPromise != null)
+ $interval.cancel(intervalPromise);
+ });
+ }
+ $scope.getNotification();
+ function updateNotifications() {
+ $scope.getNotification();
+ }
+ $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(updateNotifications, rate);
+ };
+
+ $scope.stop = function() {
+ $interval.cancel(intervalPromise);
+ };
+
+
+ $scope.showDetailedJsonMessage=function (selectedAdminNotification) {
+ if (selectedAdminNotification.source!=='EP'){
+ var messageObject=JSON.parse(selectedAdminNotification.message);
+ var html="";
+ html+='<p>'+'Message Source'+' : '+selectedAdminNotification.source+'</p>';
+ html+='<p>'+'Message Title'+' : '+selectedAdminNotification.title+'</p>';
+ for(var field in messageObject){
+ if(field=='eventDate'||field=='lastModifiedDate'){
+ html+='<p>'+field+' : '+new Date(+messageObject[field])+'</p>';
+
+ }else{
+ html+='<p>'+field+' : '+messageObject[field]+'</p>';
+
+ }
+ }
+
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/user-notifications-admin/user.notifications.Json.details.modal.page.html',
+ controller: 'userNotificationCtrl',
+ resolve: {
+ message: function () {
+ var message = {
+ title: '',
+ text: html
+
+ };
+ return message;
+ },
+
+ }
+ });
+
+ }
+ };
+
+ notificationService.getNotificationRate().then(res=> {
+ if (res == null || res.response == null) {
+ $log.error('NotificationCtrl: failed to notification update rate or duration, check system.properties file.');
+ } else {
+ var rate = parseInt(res.response.updateRate);
+ var duration = parseInt(res.response.updateDuration);
+ notificationService.setMaxRefreshCount(parseInt(duration/rate)+1);
+ notificationService.setRefreshCount(notificationService.maxCount);
+ if (rate != NaN && duration != NaN) {
+ $scope.updateRate=rate;
+ $scope.start($scope.updateRate);
+ }
+ }
+ }).catch(err=> {
+ $log.error('NotificationCtrl: getNotificationRate() failed: ' + err);
+ });
+
+ $scope.deleteNotification = function(index){
+ if ($scope.notifications[index].id == null || $scope.notifications[index].id == '') {
+ $log.error('NotificationCtrl: failed to delete Notification.');
+ return;
+ }
+ notificationService.setNotificationRead($scope.notifications[index].id);
+ $scope.notifications.splice(index,1);
+ notificationService.setNotificationCount($scope.notifications.length);
+ }
+ }
+ }
+ NotificationCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout', 'sessionService','notificationService','$interval','ngDialog'];
+ LoginSnippetCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService'];
+ HeaderCtrl.$inject = ['$log', '$window', 'userProfileService', 'menusService', '$scope', 'ECOMP_URL_REGEX','$cookies','$state','auditLogService','notificationService'];
+ angular.module('ecompApp').controller('HeaderCtrl', HeaderCtrl);
+ angular.module('ecompApp').controller('loginSnippetCtrl', LoginSnippetCtrl);
+ angular.module('ecompApp').controller('notificationCtrl', NotificationCtrl);
+
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/header/header.controller.spec.js b/ecomp-portal-FE-common/client/app/views/header/header.controller.spec.js
new file mode 100644
index 00000000..3841a2b3
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/header/header.less b/ecomp-portal-FE-common/client/app/views/header/header.less
new file mode 100644
index 00000000..ec57812c
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/header/header.less
@@ -0,0 +1,496 @@
+.header-section {
+ position: relative;
+ z-index: 999;
+}
+
+.logo-image {
+ .portal-logo;
+ display: inline-block;
+ vertical-align: middle;
+ margin-top: -3px;
+ }
+
+.portal-title {
+ font-weight: 400;
+ font-family: "Omnes-ECOMP-W02",Arial !important;
+ font-size: 18px;
+ //.c18b;
+}
+
+.parentmenu-tabs {
+ height: 55px;
+}
+
+.menu-section {
+ float: left;
+}
+
+/* Logout control*/
+.controlCls{
+ font-size: .975rem;
+ color: @portalDGray;
+ display: inline-block;
+ cursor: pointer;
+ height: 37px;
+ line-height: 37px;
+ padding-bottom: 10px;
+ vertical-align: middle;
+ width: 100%;
+}
+
+
+.controlCls:hover{
+ color:@portalLBlue !important;
+}
+
+.login-section {
+ float: right;
+ margin-top:15px;
+ min-width:150px;
+}
+
+.login-snippet-text {
+ display: inline-block;
+ font-size: 12px;
+ font-weight: bold;
+ margin-left: 5px;
+ overflow: hidden;
+ max-height: 31px;
+ max-width: 120px;
+ padding-top: 0;
+ margin-top: 0;
+ font-family: "Omnes-ECOMP-W02",Arial;
+ white-space: nowrap;
+}
+
+
+.newrow {
+ clear: left;
+}
+
+.header-columns{
+ -webkit-column-count:4;
+ -moz-column-rule: 1px outset @portalLGray;
+ -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 @portalLGray;
+
+
+}
+
+.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
+}
+
+
+//.favorites-icon li [class*=icon]:hover {
+.favorites-icon-active {
+ position: relative;
+ margin-top: 5px;
+ margin-left: 5px;
+ top: 3px;
+ color: @funcYellow;
+}
+
+.favorites-icon-inactive {
+ position: relative;
+ margin-top: 5px;
+ margin-left: 5px;
+ top: 3px;
+ color: @portalDGray;
+}
+
+.favorites-window-empty {
+ width: 100%;
+ height: auto;
+ justify-content:center;
+ align-items:center;
+ margin: auto;
+ text-align: center;
+
+ .no-fav-icon{
+ font-weight: 400;
+ font-size: 50px;
+ text-align: center;
+ color: rgb(255, 155, 0);
+ }
+ .largeText {
+ font-weight: 400;
+ font-family: Omnes-ECOMP-W02-Bold,Arial !important;
+ font-size: 18px;
+ text-align: center;
+ color: @portalDGray;
+ }
+
+ .normal {
+ color: @portalDGray;
+ font-size: 14px;
+ text-align: center;
+ }
+
+
+}
+.favorites-window {
+ width: 100%;
+ height: auto;
+ //margin: auto;
+ font-size: 14px !important;
+ display: flex;
+ margin-top: 25px;
+ margin-left: 25px;
+ z-index: 1000;
+
+ .fav-links {
+ margin-right: 25px;
+ }
+
+ .largeText {
+ font-weight: 400;
+ font-family: Omnes-ECOMP-W02-Bold,Arial !important;
+ font-size: 18px;
+ text-align: center;
+ color: @portalDGray;
+ }
+
+ a:link, a:active, a:hover {
+ margin-left: 3px;
+ margin-right: 20px;
+ text-decoration: none;
+ }
+
+ a:hover {
+ color: @portalLBlue;
+ }
+}
+
+
+
+ .notifications-count
+ {
+ .border-radius(50%);
+ background:#db3434;
+ color: @colorWhite;
+ font-size: 10px;
+ padding-top: 2px;
+ 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: Omnes-ECOMP-W02, 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: Omnes-ECOMP-W02, Arial;
+ font-size: 16px;
+ }
+
+
+
+ .notificationBox .icon-circle-action-close {
+ cursor: pointer;
+ font-size: 16px;
+ font-family: "Omnes-ECOMP-W02",Arial
+ }
+
+ .notification-close {
+ padding: 2px 2px 0px 0px;
+ float: right;
+ }
+ #notification-flag{
+ font-size: 20px;
+ color: white;
+ vertical-align: middle;
+ }
+ #notification-flag:hover{
+ color:silver
+ }
+ .notifications-list
+ {
+ list-style: none;
+ margin: 0;
+ overflow: auto;
+ height: 250px;
+ width: 270px;
+ padding: 0;
+
+
+ .item:hover{
+ background-color:#eee;
+ }
+ .item
+ {
+ .transition-transform(@transitionDefault);
+ border-bottom: 1px solid @colorSilver;
+ color: @colorAsbestos;
+ cursor: default;
+ display: block;
+ padding: 10px;
+ position: relative;
+ white-space: nowrap;
+ width: 250px;
+ font-family: "Omnes-ECOMP-W02",Arial;
+ &:before,
+ .details,
+ .button-dismiss
+ {
+ display: inline-block;
+ vertical-align: middle;
+ }
+ .icon{
+ display:inline-block;
+ .important
+ {
+ .border-radius(50%);
+ background: red;
+ content: '';
+ height: 8px;
+ width: 8px;
+ display:block;
+ }
+ .normal
+ {
+ .border-radius(50%);
+ background: @colorPeterRiver;
+ content: '';
+ height: 8px;
+ width: 8px;
+ display:block;
+ }
+ }
+ .details
+ {
+ margin-left: 10px;
+ white-space: normal;
+ width: 200px;
+ font-size:12px;
+
+ .title,
+ .date
+ {
+ display: block;
+ font-weight:bold;
+ }
+ .message-body{
+ display: block;
+ }
+ .date
+ {
+ color: @colorConcrete;
+ font-size: .85em;
+ margin-top: 3px;
+ }
+ }
+
+ .button-dismiss
+ {
+ color: @colorSilver;
+ font-size: 15px;
+
+ &: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%));
+ }
+ }
+ }
+
+#header-user-div{
+ vertical-align: middle;
+ margin-top:20px;
+}
+
+#header-user-icon{
+ display: inline-block;
+ vertical-align: middle;
+ width: 20px;
+ cursor: pointer;
+ font-size:22px;
+}
+.b2b-header-tabs .header__item.b2b-headermenu a.menu__item{
+ font-family:"Omnes-ECOMP-W02", Arial;
+}
+.b2b-header-tabs .header-tertiary li a{
+ display:inline;
+ padding-left: 3px;
+}
+.b2b-header-tabs .header-secondary .header-subitem.active .header-tertiary{
+ padding:20px;
+ font-size:15px;
+}
+
+.b2b-header-tabs .header__item.notification{
+ float:right;
+}
+
+.b2b-header-tabs .header__items{
+ width:90%;
+}
+
+.b2b-header-tabs .header-secondary, .b2b-header-tabs .header-tertiary{
+ width:100%;
+
+}
+
+.third-level-menu{
+column-count: 4;
+ line-height: 12px;
+ max-height: 500px;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ column-gap: 13px;
+ column-rule: 1px outset #d2d2d2;
+ margin-left:20px;
+}
+
+
+
+.third-level-menu a{
+ color:black;
+}
+
+.b2b-header-tabs .third-level-menu li a {
+ color: #333;
+ display: inline-grid;
+ padding: 7px 15px;
+ max-width: 228px;
+ font-family:"Omnes-ECOMP-W02", Arial;
+}
+
+.b2b-header-tabs .third-level-menu li{
+ display: inline-block;
+ width:100%;
+ border-bottom: 1px solid #d2d2d2;
+}
+
+
+.b2b-header-tabs .header-secondary .header-subitem a.menu__item{
+ font-size:16px;
+}
+
+.third-level-title{
+ font-size:15px;
+ font-weight: 700;
+}
+
+.notification-div{
+ width:15px;
+ font-size:23px;
+ cursor: pointer;
+}
+
+.notification-content{
+ line-height: normal;
+ right: 167px;
+ min-height: 122px;
+ height: auto;
+ width: auto;
+}
+
+.header-user-icon{
+ color:white;
+ font-size:20px;
+ display:inline-block;
+}
+#reg-header-snippet .reg-profileDetails .reg-userEmail-value .reg-userEmail-value-spn,
+#reg-header-snippet .reg-profileDetails .reg-userRole-value .reg-userRole-value-spn,
+#reg-header-snippet .reg-profileDetails .reg-userLastLogin-value .reg-userLastLogin-value-spn,
+#reg-header-snippet .reg-profileDetails .reg-userAppRoles-value .reg-userAppRoles-value-spn{
+ font-family: "Omnes-ECOMP-W02",Arial !important;
+}
+
+.reg-Details-table{
+ list-style: none;
+ border-bottom: 1px solid #bbb;
+ padding-bottom: 20px;
+}
+
+.reg-userName-table-cell{
+ font-weight:bold;
+ font-size:15px;
+ line-height:1.6 !important;
+
+}
+
+#header-favorites ul li{
+ width:100%;
+}
+
+.display-userAppRoles-label span{
+ font-family:"Omnes-ECOMP-W02", Arial;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/header/header.tpl.html b/ecomp-portal-FE-common/client/app/views/header/header.tpl.html
new file mode 100644
index 00000000..65a34ed1
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/header/header.tpl.html
@@ -0,0 +1,266 @@
+<!--
+ ================================================================================
+ 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="position:fixed;width: 100%;top: 0px;left: 0;background-color: #222;z-index:9999">
+ <header class="b2b-header-tabs" b2b-header-responsive>
+ <ul class="header__items" role="navigation">
+<!-- Menu Icon and name -->
+ <li class="header__item icon__item" onclick="window.location = 'applicationsHome'">
+ <span id="logo-image" class="icon-primary-att-globe"></span>
+ <span id="portal-title" class="portal-title" >OpenECOMP Portal</span>
+ </li>
+<!-- First Level menu -->
+ <li b2b-header-menu
+ id="megaMenu-{{item.text.split(' ').join('-')}}"
+ class="header__item b2b-headermenu"
+ ng-repeat="item in megaMenuDataObject"
+ ng-mousedown="loadFavorites(item.text)"
+ role="presentation">
+
+ <a href="javascript:void(0);"
+ id="parentmenu-tabs"
+ class="menu__item"
+ role="menuitem">{{item.text}}</a>
+
+ <div class="header-secondary-wrapper" ng-if="item.active_yn=='Y'">
+ <ul class="header-secondary" role="menu">
+<!-- Second Level menu -->
+ <li class="header-subitem"
+ id="subItem-{{subItem.text.split(' ').join('-')}}"
+ b2b-header-submenu
+ ng-repeat="i in item.children | orderBy : 'column'"
+ ng-mousemove="submenuLevelAction(i.text,i.column)"
+ role="presentation">
+ <!-- Favorites -->
+ <div ng-if="i.text=='Favorites'" >
+ <a href="javascript:void(0);" class="menu__item" role="menuitem">{{i.text}}</a>
+ <i id="favorite-star" data-size="large" class="icon-star favorites-icon-active"></i>
+ <div class="header-columns-div" ng-show='favoritesWindow' ng-mouseleave="hideFavoritesWindow()" >
+ <div class="header-tertiary-wrapper" id="header-favorites">
+ <ul class="header-tertiary" role="menu">
+ <li role="presentation">
+ <div
+ ng-repeat="subItem in favoritesMenuItems"
+ ng-show="showFavorites"
+ ng-hide="hideMenus"
+ id="favoritesMenuItems-{{subItem.text.split(' ').join('-')}}">
+ <div class="fav-links">
+ <i id="favorite-selector-favorites-list"
+ class="icon-star favorites-icon-active"
+ data-ng-click="removeAsFavoriteItem($event, subItem.menuId)"
+ ng-mousedown="removeAsFavoriteItem($event, subItem.menuId)">
+ </i>
+ <a id="favorites-list" aria-label="{{subItem.text}}" ng-click="goToUrl(subItem)">{{subItem.text}}</a>
+ </div>
+ </div>
+
+ <div id="favorites-empty" class="favorites-window-empty" ng-show="emptyFavorites">
+ <p id="p-no-favs-icon" class="no-fav-icon">
+ <span class="icon-star" ></span>
+ </p>
+ <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>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ <!-- Support or Help -->
+ <div ng-if="item.text=='Support' || item.text=='Help'" id="second-level-menus-help">
+ <a href="javascript:void(0);" ng-click="goToUrl(i);auditLog(i,'Support')" class="menu__item" role="menuitem">{{i.text| elipsis: 50}}</a>
+ </div>
+ <!-- Others -->
+ <div ng-if="i.text!='Favorites' && (item.text!='Support' && item.text!='Help')" >
+ <a href="javascript:void(0);" class="menu__item" role="menuitem">{{i.text| elipsis: 50}}</a>
+ <div class="header-tertiary-wrapper" >
+ <ul class="third-level-menu" role="menu" id="third-level-menus">
+<!-- Third Level menu -->
+
+ <li b2b-header-tertiarymenu ng-repeat="link in i.children | orderBy : 'column'" role="presentation" >
+ <i id="level3-star-inactive-{{link.menuId}}" ng-cloak
+ class="icon-star favorites-icon-inactive" data-size="large"
+ data-ng-click="setAsFavoriteItem($event, link.menuId)"
+ ng-if="link.url.length > 1 && isUrlFavorite(link.menuId)==false">
+ </i>
+ <i id="level3-star-active-{{link.menuId}}" ng-cloak
+ ng-if="link.url.length > 1 && isUrlFavorite(link.menuId)"
+ class="icon-star favorites-icon-active ng-cloak" data-size="large"
+ data-ng-click="removeAsFavoriteItem($event, link.menuId)">
+ </i>
+
+ <a class="third-level-title"
+ aria-label="{{link.text | elipsis: 50}}"
+ ng-click="goToUrl(link);auditLog(link,'application')">{{link.text| elipsis: 50}}</a>
+<!-- Fourth Level menu -->
+ <div b2b-tertiary-link ng-repeat="title in link.children" >
+ <i id="level4-star-inactive-{{title.menuId}}" ng-cloak
+ class="icon-star favorites-icon-inactive"
+ data-ng-click="setAsFavoriteItem($event, title.menuId)"
+ ng-if="title.url.length > 1 && isUrlFavorite(title.menuId)==false">
+ </i>
+ <i id="level4-star-active-{{title.menuId}}" ng-cloak
+ class="icon-star favorites-icon-active"
+ data-ng-click="removeAsFavoriteItem($event, title.menuId)"
+ ng-if="title.url.length > 1 && isUrlFavorite(title.menuId)">
+ </i>
+ <a href="javascript:void(0);" class="header-tertiaryitem" ng-class="{'disabled': title.disabled}" role="menuitem" ng-click="goToUrl(title);auditLog(title,'functional')">{{title.text | elipsis: 50}}</a>
+ </div>
+ </li>
+
+
+
+
+ </ul>
+ </div>
+ </div>
+
+ </li>
+ </ul>
+ </div>
+ </li>
+<!-- Right side of the Menu - User Icon and Notification flag -->
+ <div class="login-section">
+ <!-- User Icon -->
+ <li class="header__item profile" aria-haspopup="true">
+ <b2b-flyout>
+ <div b2b-flyout-toggler >
+ <div class="icon-people-oneperson" id="header-user-icon" tabindex="0" b2b-accessibility-click="13,32" aria-label="notifications" aria-haspopup="true" aria-expanded="{{flyoutOpened}}" role="button"></div>
+ <div id="login-snippet-text" class="login-snippet-text">{{header.isGuest ? 'Guest' : header.firstName}}</div>
+ </div>
+ <b2b-flyout-content horizontal-placement="center" vertical-placement="below">
+ <div ng-controller="loginSnippetCtrl" >
+ <div id="reg-header-snippet">
+ <div tabindex="0" class="reg-profileDetails" id="reg-profiledetails-id">
+ <ul class="reg-Details-table">
+ <li>
+ <div class="reg-userName-table">
+ <div id="reg-userName-table-row">
+ <div id="reg-userName-table-cell">
+ <h3 >
+ {{firstName}} {{lastName}}&nbsp;</h3>
+ <span>&nbsp;</span>
+ </div>
+ </div>
+ </div>
+ </li>
+ <li><div class="reg-userEmail-label"><span class="reg-userEmail-label-spn" style=font-weight:bold>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>&nbsp;</li>
+ <li><div class="reg-userRole-label"><span class="reg-userRole-label-spn" style=font-weight:bold>
+ 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>
+ <li>&nbsp;</li>
+ <li><div class="reg-userLastLogin-label"><span class="reg-userLastLogin-label-spn" style=font-weight:bold>
+ Last login<span class="visuallyhidden">:</span></span></div></li>
+ <li><div class="reg-userLastLogin-value"><span class="reg-userLastLogin-value-spn">
+ {{lastLogin}}<span class="visuallyhidden"></span></span></div></li>
+ <li>&nbsp;</li>
+ <li>
+ <div class="display-userAppRoles-label">
+ <a href="javascript:void(0);" ng-click="getUserApplicationRoles()" class="icon-controls-add-maximize" ><span>Applications and Roles</span></a>
+ </div>
+
+ <div class="display-userAppRoles-label" ng-show="displayUserAppRoles" style="height:200px; overflow-y:auto;">
+ <div ng-repeat="ua in userapproles track by $index">
+ <div class="reg-userApp-value">
+ <span class="reg-userApp-value-spn" style=font-weight:bold>{{ua.App}}<span class="visuallyhidden">:</span></span>
+ </div>
+ <div ng-repeat="role in ua.Roles track by $index" class="reg-userAppRoles-value" >
+ <span class="reg-userAppRoles-value-spn">{{role}}</span>
+ </div>
+ </div>
+ </div>
+ </li>
+ </ul>
+ <div id="reg-logout-div" style="padding-top: 8px">
+ <button href="javascript:void(0)" id="allLogout" ng-click="allAppsLogout()" class="btn btn-alt btn-small">
+ Log out
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </b2b-flyout-content>
+ </b2b-flyout>
+ </li>
+ <!-- Notification flag -->
+ <li class="header__item notification" aria-haspopup="true" class="notification-li">
+ <b2b-flyout>
+ <div b2b-flyout-toggler class="notification-div">
+ <div class="notifications-count" ng-hide="notificationCount.count==0" ng-bind="notificationCount.count"></div>
+ <div class="icon-content-flag megamenu-notification-overrides" class="b2b-flyout-icon" tabindex="0" b2b-accessibility-click="13,32" aria-label="notifications" aria-haspopup="true" aria-expanded="{{flyoutOpened}}" role="button"></div>
+ </div>
+ <b2b-flyout-content horizontal-placement="center" vertical-placement="below">
+ <div class="notification-content" ng-controller="notificationCtrl" >
+ <div class="ng-scope">
+ <div id="notification" class="notificationBox ">
+ <div align ="right">
+ <a ui-sref="root.notificationHistory" style="font-size: 14px"> View All Recent Notifications </a>
+ </div>
+ <div class="notification-header">
+ <div style="float:left;">
+ <p class="notification-heading">Notifications</p>
+ </div>
+ <div style="clear:both;"></div>
+ </div>
+ <div ng-show="notifications.length==0">
+ <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>
+ <div class="notification-main" ng-show="notifications.length>0">
+ <ul class="notifications-list">
+ <li class="item" data-id="5" ng-repeat="item in notifications">
+ <div class="icon">
+ <span class="important" ng-show="item.priority==2"/>
+ <span class="normal" ng-show="item.priority==1"/>
+ </div>
+ <div class="details">
+ <span class="title" ng-bind="item.title"></span>
+ <span class="message-body" ng-bind="item.message"></span>
+ <!-- <span class="date" ng-bind="item.time" ></span> -->
+ <mydate>{{item.time | date:'MM/dd/yyyy hh:mm:ss a Z'}}</mydate>
+ </div>
+ <button type="button" ng-click="deleteNotification($index)" class="button-default button-dismiss js-dismiss">x</button>
+ </li>
+ </ul>
+ </div>
+ <div class="notification-footer">
+ <div class="notification-links">
+ <div style="clear:both;"></div>
+ </div>
+ </div>
+ </div>
+ </b2b-flyout-content>
+ </b2b-flyout>
+ </li>
+ </div>
+ </ul>
+ </header>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.html b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.html
new file mode 100644
index 00000000..a8b20739
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.html
@@ -0,0 +1,205 @@
+<!--
+ ================================================================================
+ 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="microservice-add-details-model">
+ <div id="microservice-add-details-title" class="title">microservice Details</div>
+
+ <div id="microservices" class="microservice-scrolling-table">
+ <div class="microservice-properties-main"
+ scroll-top="microserviceAddDetails.scrollApi">
+ <form id="microservices-details-form" name="serviceForm" novalidate
+ autocomplete="off">
+ <div class="item required">
+ <div class="item-label">microservice Name</div>
+ <input id="microservice-details-input-name" class="table-search-field"
+ type="text" name="name" ng-pattern="/^[\w -]*$/" maxlength="100"
+ ng-model="microserviceAddDetails.service.name"
+ ng-change="microserviceAddDetails.updateServiceName()"
+ ng-disabled="microserviceAddDetails.isEditMode" required />
+
+ <div class="error-container"
+ ng-show="(microserviceAddDetails.emptyServiceName || serviceForm.name.$dirty)">
+ <div ng-messages="serviceForm.name.$error" class="error-container">
+ <small id="microservices-details-input-name-required"
+ class="err-message" ng-message="required">Microservice
+ Name is required</small> <small
+ id="microservices-details-input-name-pattern"
+ class="err-message" ng-message="pattern">Microservice
+ Name must be letters, numbers, or underscore</small>
+ </div>
+ </div>
+
+ <div class="error-container"
+ ng-show="microserviceAddDetails.isEditMode == false && microserviceAddDetails.dupliateName == true">
+ <small id="microservices-details-input-name-dupliated"
+ class="err-message">Name not available - choose different
+ name </small>
+ </div>
+ </div>
+
+ <div class="item required">
+ <div class="item-label">microservice Description</div>
+ <textarea b2b-reset b2b-reset-textarea id="widgets-details-input-desc"
+ name="desc" ng-model="microserviceAddDetails.service.desc"
+ ng-change="microserviceAddDetails.updateDesc()"></textarea>
+ <div class="error-container"
+ ng-show="(microserviceAddDetails.emptyServiceDesc
+ || (serviceForm.desc.$dirty && microserviceAddDetails.service.desc == ''))">
+ <small id="microservices-details-input-desc-required"
+ class="err-message">Microservice Description is required</small>
+ </div>
+ </div>
+
+
+
+ <div class="item"
+ ng-show="microserviceAddDetails.isEditMode && microserviceAddDetails.availableWidgets.length > 0">
+ <div class="item-label">Client Widgets</div>
+ <div ng-repeat="widget in microserviceAddDetails.availableWidgets">{{widget.name}}</div>
+ </div>
+
+ <div class="item required">
+ <div class="item-label">Application Name</div>
+ <div class="service-select">
+ <div class="table-dropdown">
+ <select id="microservice-details-input-app" name="app" b2b-dropdown placeholder-text="Select Application" ng-model="microserviceAddDetails.service.application.name" ng-change="microserviceAddDetails.updateApp()">
+ <option b2b-dropdown-list option-repeat="d in microserviceAddDetails.availableApps" value="{{d.name}}">{{d.name}}</option>
+ </select>
+ </div>
+ </div>
+
+ <div class="error-container"
+ ng-show="(microserviceAddDetails.emptyServiceApp
+ || (serviceForm.app.$dirty && microserviceAddDetails.service.application == null))">
+ <small id="microservices-details-input-url-required"
+ class="err-message">Please select microservice
+ Application</small>
+ </div>
+ </div>
+
+ <div class="item required">
+ <div class="item-label">microservice Endpoint URL</div>
+ <input id="microservice-details-input-endpoint-url"
+ class="table-search-field" ng-model="microserviceAddDetails.service.url"
+ ng-change="microserviceAddDetails.updateURL()" type="text"
+ name="url" maxlength="200" />
+
+ <div class="error-container"
+ ng-show="(microserviceAddDetails.emptyServiceURL
+ || (serviceForm.url.$dirty && microserviceAddDetails.service.url == ''))">
+ <small id="microservices-details-input-url-required"
+ class="err-message">Microservice Endpoint URL is required</small>
+ </div>
+ </div>
+
+ <div class="item required">
+ <div class="item-label">Security Type</div>
+ <div class="service-select">
+ <div class="table-dropdown">
+ <select id="microservice-details-input-security-type" name="microservice-details-input-security-type" b2b-dropdown placeholder-text="Select Application" ng-model="microserviceAddDetails.service.security.name" ng-change="microserviceAddDetails.updateApp()">
+ <option b2b-dropdown-list option-repeat="d in microserviceAddDetails.availableSecurityTypes" value="{{d.name}}">{{d.name}}</option>
+ </select>
+ </div>
+ </div>
+ </div>
+
+ <div class="item"
+ ng-show="microserviceAddDetails.service.security.id == 1">
+ <div class="item-label">Username</div>
+ <input id="microservice-details-input-username" class="table-search-field"
+ type="text" name="username" maxlength="100"
+ ng-model="microserviceAddDetails.service.username" />
+ </div>
+
+ <div class="item"
+ ng-show="microserviceAddDetails.service.security.id == 1">
+ <div class="item-label">Password</div>
+ <input id="microservice-details-input-password" class="table-search-field"
+ type="password" name="password" maxlength="100"
+ ng-model="microserviceAddDetails.service.password" />
+ </div>
+
+
+ <div class="item" ng-show="microserviceAddDetails.isEditMode">
+ <div class="left-test-item">
+ <div class="item-label">Test Microservice</div>
+ </div>
+ <div class="right-test-item">
+ <div id="microservice-details-test-button" class="test-button"
+ ng-click="microserviceAddDetails.testServiceURL()">Test</div>
+ </div>
+ </div>
+
+ <div class="item" ng-show="microserviceAddDetails.isEditMode">
+ <div class="item-label">JSON output</div>
+ <textarea id="microservice-details-input-json" class="json-field"
+ name="json"></textarea>
+ </div>
+
+ <div class="add-para-item">
+ <div class="item-label add-label-left">Add User Parameter</div>
+ <div class="icon-primary-accordion-plus"
+ ng-click="microserviceAddDetails.addParameter()"></div>
+ </div>
+ <div class="item">
+ <div class="para-label-item-left"
+ ng-show="microserviceAddDetails.service.parameterList.length > 0">
+ Parameter Key</div>
+ <div class="para-label-item-right"
+ ng-show="microserviceAddDetails.service.parameterList.length > 0">
+ Parameter Default Value</div>
+
+ <div id="microservice-details-user-paramters"
+ ng-repeat="parameter in microserviceAddDetails.service.parameterList">
+
+ <div class="para-item-left">
+ <input id="microservice-details-input-user-parameter-key"
+ class="table-search-field" type="text" name="param-key" maxlength="200"
+ ng-model="parameter.para_key" />
+ </div>
+ <div class="para-item-middle">
+ <input id="microservice-details-input-user-parameter-value"
+ class="table-search-field" type="text" name="param-value"
+ maxlength="200" ng-model="parameter.para_value" />
+ </div>
+
+ <div class="icon-primary-accordion-minus para-item-right"
+ ng-click="microserviceAddDetails.removeParamItem(parameter)"></div>
+ </div>
+
+ <div class="microservice-property">
+ <input id="microservices-checkbox-app-is-enabled" type="checkbox"
+ class="checkbox-field"
+ ng-model="microserviceAddDetails.service.active" />
+ <div class="property-label checkbox-label">Active</div>
+ </div>
+
+ </div>
+
+ <div id="microservice-scroll-end"></div>
+ <div class="dialog-control">
+ <button class="btn btn-alt btn-small" id="microservice-details-save-button" ng-click="microserviceAddDetails.saveChanges()">Save</button>
+ <button class="btn btn-alt btn-small" id="microservice-details-close-button" ng-click="microserviceAddDetails.closeThisDialog()">Close</button>
+ </div>
+ </form>
+ </div>
+
+
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.js b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.js
new file mode 100644
index 00000000..f6123349
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.js
@@ -0,0 +1,336 @@
+/*-
+ * ================================================================================
+ * 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 MicroserviceAddDetailsCtrl {
+ constructor($scope, $log, $interval, widgetsCatalogService, applicationsService, adminsService, microserviceService, errorMessageByCode, ECOMP_URL_REGEX, $window,userProfileService, confirmBoxService, $cookies) {
+
+
+ let getAvailableApps = () => {
+ applicationsService.getAppsForSuperAdminAndAccountAdmin().then(apps => {
+ this.availableApps=[];
+ apps.unshift({
+ id: 1,
+ name: "ECOMP Portal"
+ });
+ for(var i = 0; i < apps.length; i++) {
+ this.availableApps.push({
+ id: apps[i].id,
+ name: apps[i].name
+ });
+ if(this.isEditMode == true && this.service.appId == apps[i].id){
+ this.service.application = this.availableApps[i];
+ }
+ }
+ }).catch(err => {
+ $log.error(err);
+ });
+ };
+
+ let getAvailableWidgets = () => {
+ microserviceService.getWidgetListByService(this.service.id).then(widgets =>{
+ this.availableWidgets = [];
+ for(var i = 0; i < widgets.length; i++){
+ this.availableWidgets.push({
+ name: widgets[i]
+ })
+ }
+ }).catch(err => {
+ $log.error(err);
+ });
+ };
+
+
+ let getAvailableSecurityTypes = () => {
+ this.availableSecurityTypes = [];
+ this.availableSecurityTypes.push({
+ id: 0,
+ name: 'No Authentication'
+ });
+ this.availableSecurityTypes.push({
+ id: 1,
+ name: 'Basic Authentication'
+ });
+ this.availableSecurityTypes.push({
+ id: 2,
+ name: 'Cookie based Authentication'
+ });
+ }
+
+ let init = () => {
+ $log.info('MicroserviceAddDetailsCtrl::init');
+ this.service = [];
+ this.service.parameterList = [];
+ this.service.active = true;
+ this.emptyServiceName = false;
+ this.emptyServiceDesc = false;
+ this.emptyServiceURL = false;
+ this.emptyServiceApp = false;
+ this.dupliateName = false;
+ this.serviceList = $scope.ngDialogData.list;
+
+ if ($scope.ngDialogData && $scope.ngDialogData.service) {
+
+ this.isEditMode = true;
+ this.service = _.clone($scope.ngDialogData.service);
+
+ console.log(this.service);
+ if(this.service.active == 'Y')
+ this.service.active = true;
+ else
+ this.service.active = false;
+ } else {
+ this.isEditMode = false;
+ }
+ getAvailableApps();
+ getAvailableSecurityTypes();
+ //getAvailableWidgets();
+
+ /**
+ * 0: Basic Authentication
+ *
+ * TODO: change the structure
+ */
+ if(this.service.securityType == "No Authentication"){
+ this.service.security = this.availableSecurityTypes[0];
+ }else if(this.service.securityType == "Basic Authentication"){
+ this.service.security = this.availableSecurityTypes[1];
+ }else if(this.service.securityType == "Cookie based Authentication"){
+ this.service.security = this.availableSecurityTypes[2];
+ }
+ };
+
+ this.ECOMP_URL_REGEX = ECOMP_URL_REGEX;
+ this.conflictMessages = {};
+ this.scrollApi = {};
+
+ let resetConflict = fieldName => {
+ delete this.conflictMessages[fieldName];
+ if($scope.widgetForm[fieldName]){
+ $scope.widgetForm[fieldName].$setValidity('conflict', true);
+ }
+ };
+
+ this.addParameter = () => {
+ document.getElementById('microservice-scroll-end').scrollIntoView();
+ this.service.parameterList.push({});
+ }
+
+ this.closeThisDialog = () => {
+ $scope.closeThisDialog(true);
+ }
+
+ this.removeParamItem = (parameter) => {
+ microserviceService.getUserParameterById(parameter.id).then((res) => {
+ if(res.length > 0){
+ var message = res.length + " users have their own widget parameters. Are you sure you want to delete?";
+ confirmBoxService.editItem(message).then(isConfirmed => {
+ if(isConfirmed){
+ microserviceService.deleteUserParameterById(parameter.id).then((res) => {
+ for(var i = 0; i < this.service.parameterList.length; i++){
+ if(this.service.parameterList[i].para_key == parameter.para_key
+ && this.service.parameterList[i].para_value == parameter.para_value){
+ this.service.parameterList.splice(i, 1);
+ return;
+ }
+ }
+ });
+ }
+ });
+ }
+ else{
+ for(var i = 0; i < this.service.parameterList.length; i++){
+ if(this.service.parameterList[i].para_key == parameter.para_key
+ && this.service.parameterList[i].para_value == parameter.para_value){
+ this.service.parameterList.splice(i, 1);
+ return;
+ }
+ }
+ }
+ });
+
+
+ }
+
+ this.updateServiceName = () => {
+ this.dupliateName = false;
+ for(var i = 0; i < this.serviceList.length; i++){
+ if(this.serviceList[i].name == this.service.name){
+ this.dupliateName = true;
+ return;
+ }
+ }
+ }
+
+ this.updateDesc = () => {
+ this.emptyServiceDesc = false;
+ }
+
+ this.updateURL = () => {
+ this.emptyServiceURL = false;
+ }
+
+ this.updateApp = () => {
+ this.emptyServiceApp = false;
+ }
+
+ //This is a fix for dropdown selection, due to b2b dropdown only update value field
+ $scope.$watch('microserviceAddDetails.service.application.name', (newVal, oldVal) => {
+ for(var i=0;i<$scope.microserviceAddDetails.availableApps.length;i++){
+ if($scope.microserviceAddDetails.availableApps[i].name==newVal){
+ $scope.microserviceAddDetails.service.application=angular.copy($scope.microserviceAddDetails.availableApps[i]);
+ }
+ }
+ });
+ $scope.$watch('microserviceAddDetails.service.security.name', (newVal, oldVal) => {
+ for(var i=0;i<$scope.microserviceAddDetails.availableSecurityTypes.length;i++){
+ if($scope.microserviceAddDetails.availableSecurityTypes[i].name==newVal){
+ $scope.microserviceAddDetails.service.security=angular.copy($scope.microserviceAddDetails.availableSecurityTypes[i]);
+ }
+ }
+ });
+
+ let emptyCookies = () => {
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ $log.info('AppDetailsModalCtrl::emptyCookies profile: ', profile);
+ $scope.attuid = profile.attuid;
+ $log.info('user has the following attuid: ' + profile.attuid);
+ if ($cookies.getObject($scope.attuid + '_widget') != undefined && $cookies.getObject($scope.attuid + '_widget') != null) {
+ $cookies.remove($scope.attuid + '_widget');
+ }
+ });
+ };
+
+ this.testServiceURL = () =>{
+ //console.log(this.service.id);
+ widgetsCatalogService.getServiceJSON(this.service.id).then(res => {
+ document.getElementById("microservice-details-input-json").innerHTML = (JSON.stringify(res));
+ });
+ }
+
+ this.saveChanges = () => {
+ /* TODO: add form validation */
+
+ var isValid = true;
+ this.updateServiceName();
+
+ if(this.service.name == ''
+ || this.service.name == undefined){
+ this.emptyServiceName = true;
+ isValid = false;
+ }
+
+ if(this.dupliateName == true
+ && this.isEditMode == false){
+ isValid = false;
+ }
+
+
+ if(this.service.desc == ''
+ || this.service.desc == undefined){
+ this.emptyServiceDesc = true;
+ isValid = false;
+ }
+
+ if(this.service.url == ''
+ || this.service.url == undefined){
+ this.emptyServiceURL = true;
+ isValid = false;
+ }
+
+ if(this.service.application == undefined
+ || this.service.application == null){
+ this.emptyServiceApp = true;
+ isValid = false;
+ }
+
+ if(!isValid)
+ return;
+
+ /*
+ * Check the parameter list, delete those parameters that don't
+ * have key
+ */
+ for(var i = 0; i < this.service.parameterList.length; i++){
+ if(this.service.parameterList[i].para_key == undefined
+ || this.service.parameterList[i].para_key == null
+ || this.service.parameterList[i].para_key == ""){
+ this.service.parameterList.splice(i, 1);
+ i--;
+ }
+ }
+
+ var securityType;
+ var username;
+ var password;
+ if(this.service.security == undefined ||
+ this.service.security == null)
+ securityType = "No Authentication";
+ else{
+ securityType = this.service.security.name;
+ username = this.service.username;
+ password = this.service.password;
+ }
+
+ var active = 'N';
+ if(this.service.active == true)
+ active = 'Y';
+
+ var newService = {
+ name: this.service.name,
+ desc: this.service.desc,
+ appId: this.service.application.id,
+ url: this.service.url,
+ securityType: securityType,
+ username: username,
+ password: password,
+ active: active,
+ parameterList: this.service.parameterList
+ };
+
+ if(this.isEditMode){
+ // console.log(this.service.parameterList);
+ var message = "Are you sure you want to change '" + this.service.name + "'?"
+ confirmBoxService.editItem(message).then(isConfirmed => {
+ if(isConfirmed){
+ microserviceService.updateService(this.service.id, newService).then(() => {
+ // TODO: result validation check
+ this.closeThisDialog();
+ });
+ }
+ });
+ }else{
+ microserviceService.createService(newService).then(() => {
+ // TODO: result validation check
+ $scope.closeThisDialog(true);
+ });
+ }
+
+ };
+ init();
+ $scope.$on('$stateChangeStart', e => {
+ e.preventDefault();
+ });
+ }
+ }
+ MicroserviceAddDetailsCtrl.$inject = ['$scope', '$log', '$interval', 'widgetsCatalogService', 'applicationsService', 'adminsService', 'microserviceService', 'errorMessageByCode', 'ECOMP_URL_REGEX', '$window','userProfileService', 'confirmBoxService', '$cookies'];
+ angular.module('ecompApp').controller('MicroserviceAddDetailsCtrl', MicroserviceAddDetailsCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.less b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.less
new file mode 100644
index 00000000..fa607fee
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-add-details/microservice-add-details.less
@@ -0,0 +1,234 @@
+.microservice-add-details-model{
+ height: 550px;
+ overflow-y: auto;
+ .title {
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @blue-active 3px solid;
+ }
+ .microservice-details-title {
+ color: #191919;
+ font-family: "Omnes-ECOMP-W02", Arial;;
+ font-size: 24px;
+ padding-bottom: 15px;
+ padding-top:30px;
+ margin-left: 25px;
+ }
+ .microservice-properties-main {
+ padding-top: 20px;
+ padding-left: 20px;
+ padding-right: 20px;
+ margin-bottom: 50px;
+
+ height: 100%;
+ width:100%;
+
+ .microservice-property{
+ margin-top: 10px;
+ position: relative;
+ .property-label{
+ .dGray14r;
+ }
+ .checkbox-label{
+ display: inline-block;
+ padding-left: 3px;
+ }
+ .checkbox-field{
+ padding: 0;
+ margin: 0;
+ vertical-align: middle;
+ position: relative;
+ top: -1px;
+ }
+ }
+ .add-para-item{
+ position: relative;
+ .add-label-left{
+ line-height: 25px;
+ height: 30px;
+ vertical-align: middle;
+ display:inline-block;
+
+ margin-right: 10px;
+ background: @portalWhite;
+ .dGray14r;
+ }
+ .add-label-right{
+ height: 14px;
+ width: 14px;
+ display:inline-block;
+ }
+ }
+ .item{
+ position: relative;
+ margin-bottom: 15px;
+ width:400px;
+ .service-select{
+ select{
+ cursor: pointer;
+ position: relative;
+ border: 1px solid #5a5a5a;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ line-height: 15px;
+ height: 32px;
+ padding-left: 10px;
+ padding-right: 10px;
+ border-radius: 0px;
+ }
+ display:inline-block;
+ width: 100%;
+ }
+ .error-container{
+ position: absolute;
+ width: 280px;
+ display: block;
+ height: 12px;
+ line-height: 12px;
+
+ .err-message{
+ color: @funcRed;
+ font-size: 9px;
+ }
+ .valid-message{
+ color: @funcGreen;
+ font-size: 9px;
+ }
+ }
+ .auth-item-left{
+ padding-top: 0;
+ line-height: 30px;
+ height: 30px;
+ vertical-align: middle;
+ display:inline-block;
+ width: 15%;
+ //border-radius: 2px;
+ //border: 1px solid @portalLGray;
+ margin-right: 10px;
+ background: @portalWhite;
+ white-space: nowrap;
+ .dGray14r;
+ }
+ .auth-item-right{
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid @portalLGray;
+ background: @portalWhite;
+ vertical-align: middle;
+ }
+ .para-label-item-left{
+ line-height: 30px;
+ height: 30px;
+ vertical-align: middle;
+ display:inline-block;
+ width: 45%;
+ margin-right: 10px;
+ background: @portalWhite;
+ white-space: nowrap;
+ .dGray14r;
+ }
+ .para-label-item-right{
+ line-height: 30px;
+ height: 30px;
+ display:inline-block;
+ width: 45%;
+ background: @portalWhite;
+ vertical-align: middle;
+ white-space: nowrap;
+ .dGray14r;
+ }
+ .para-item-left{
+ line-height: 30px;
+ height: 36px;
+ margin:3px;
+ vertical-align: middle;
+ display:inline-block;
+ width: 43%;
+ border-radius: 2px;
+ border: 1px solid @portalLGray;
+ margin-right: 10px;
+ background: @portalWhite;
+ white-space: nowrap;
+ .dGray14r;
+ }
+ .para-item-middle{
+ line-height: 30px;
+ height: 36px;
+ margin:3px;
+ display:inline-block;
+ width: 43%;
+ border-radius: 2px;
+ border: 1px solid @portalLGray;
+ background: @portalWhite;
+ vertical-align: middle;
+ white-space: nowrap;
+ .dGray14r;
+ }
+ .json-field{
+ position: relative;
+ .custom-input-field;
+ height:80px;
+ }
+ .left-test-item{
+ display: inline-block;
+ width: 85%;
+ background: @portalWhite;
+ }
+
+ .right-test-item{
+ position: relative;
+ display: inline-block;
+ width: 15%;
+ float: right;
+ }
+
+ .test-button {
+ .btn-blue;
+ }
+ .para-item-right{
+ margin-top: 10px;
+ height: 14px;
+ width: 14px;
+ float: right;
+ display:inline-block;
+ }
+ .input-field{
+ .custom-input-field;
+ width: 100%;
+ }
+ .textarea-field{
+ position: relative;
+ .custom-input-field;
+ height:50px;
+ }
+ .submit-button {
+ margin-top: 25px;
+ position: relative;
+ float: right;
+ width: 20%;
+ .btn-blue;
+ }
+
+
+
+ .add-para-button{
+ height: 14px;
+ width: 14px;
+ float: right;
+ }
+ .item-label{
+ .dGray14r;
+ }
+ }
+ }
+}
+.microservice-scrolling-table{
+ width: 548px;
+ margin-left: 16px;
+ height: 430px;
+ overflow: auto;
+}
+#microservice-scroll-end{
+ height: 20px;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.controller.js b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.controller.js
new file mode 100644
index 00000000..6e711b75
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.controller.js
@@ -0,0 +1,87 @@
+'use strict';
+(function () {
+ class MicroserviceOnboardingCtrl {
+ constructor($log, applicationsService, microserviceService, ngDialog, confirmBoxService,
+ userProfileService, $cookies, $scope) {
+
+
+ let getOnboardingServices = () => {
+ microserviceService.getServiceList().then(res => {
+ $scope.serviceList = res;
+ }).catch(err => {
+ $log.error('MicroserviceOnboardingCtrl::getOnboardingServices caught error', err);
+ });
+ };
+
+
+
+ let init = () => {
+ $scope.serviceList = [];
+ getOnboardingServices();
+ this.serviceTableHeaders = [
+ {name: 'Microservice Name', value: 'name', isSortable: false},
+ {name: 'Service Endpoint URL', value: 'url', isSortable: false},
+ {name: 'Security Type', value: 'securityType', isSortable: false}
+ ];
+ };
+
+ this.openAddNewMicroserviceModal = (selectedService) => {
+ let data = null;
+ if(selectedService){
+ if(!selectedService.id){
+ $log.error('MicroserviceOnboardingCtrl:openAddNewMicroserviceModal:service id not found');
+ return;
+ }
+ data = {
+ service:selectedService,
+ list: $scope.serviceList
+ }
+ }else{
+ data = {
+ list: $scope.serviceList
+ }
+ }
+
+ ngDialog.open({
+ templateUrl: 'app/views/microservice-onboarding/microservice-add-details/microservice-add-details.html',
+ controller: 'MicroserviceAddDetailsCtrl',
+ controllerAs: 'microserviceAddDetails',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if(needUpdate.value === true){
+ getOnboardingServices();
+ }
+ });
+ };
+
+ this.deleteService = service => {
+ confirmBoxService.deleteItem(service.name).then(isConfirmed => {
+ if(isConfirmed){
+ if(!service || !service.id){
+ $log.error('MicroserviceOnboardingCtrl::deleteService: No service or ID... cannot delete');
+ return;
+ }
+ microserviceService.deleteService(service.id).then((res) => {
+ if(res.status == "WARN"){
+ confirmBoxService.showInformation("Failed: widgets " + res.response + " are assoicated with this microservice!");
+ }else{
+ $scope.serviceList.splice($scope.serviceList.indexOf(service), 1);
+ }
+ }).catch(err => {
+ $log.error('MicroserviceOnboardingCtrl::deleteService error:',err);
+ });
+ }
+ }).catch(err => {
+ $log.error('MicroserviceOnboardingCtrl::deleteService error:',err);
+ });
+
+
+ };
+
+ init();
+ }
+ }
+ MicroserviceOnboardingCtrl.$inject = ['$log', 'applicationsService', 'microserviceService', 'ngDialog', 'confirmBoxService',
+ 'userProfileService','$cookies', '$scope'];
+ angular.module('ecompApp').controller('MicroserviceOnboardingCtrl', MicroserviceOnboardingCtrl);
+})(); \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.less b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.less
new file mode 100644
index 00000000..7565b432
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.less
@@ -0,0 +1,26 @@
+.microservice-onboarding{
+ //.bg_portalWhite;//white for 1702
+ .bg_portalGray; // 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;
+
+ .microservices-table {
+ width: @table-width;
+ margin: 0 auto;
+
+ .add-button{
+ width: 180px;
+ }
+ }
+
+ .delete-microservice{
+ .ico_trash_default;
+ }
+
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.tpl.html b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.tpl.html
new file mode 100644
index 00000000..e3b3128a
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/microservice-onboarding/microservice-onboarding.tpl.html
@@ -0,0 +1,75 @@
+<!--
+ ================================================================================
+ 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="microservice-onboarding" id="page-content">
+ <div id="microservice-onboarding-title"
+ class="w-ecomp-main-view-title">Microservice Onboarding</div>
+ <div class="microservices-table">
+ <div class="table-control">
+ <div class="table-control-buttons">
+ <button class="btn btn-alt btn-small" id="microservice-onboarding-button-add" ng-click="microserviceOnboarding.openAddNewMicroserviceModal()">
+ <i class="icon-people-userbookmark" aria-hidden="true"></i>&nbsp;Add Microservice
+ </button>
+ </div>
+ <div>
+ <div class="c-ecomp-b2b-abs-table default">
+ <table b2b-table table-data="serviceList"
+ search-string="microserviceOnboarding.searchString"
+ view-per-page="microserviceOnboarding.viewPerPageIgnored"
+ current-page="microserviceOnboarding.currentPageIgnored"
+ total-page="microserviceOnboarding.totalPageIgnored">
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="microservice-catalog-th-header-name"
+ ng-repeat="header in microserviceOnboarding.serviceTableHeaders"
+ b2b-table-header key="{{header.value}}"
+ sortable="{{header.isSortable}}">{{header.name}}</th>
+ <th id="microservices-catalog-th-header-delete"
+ b2b-table-header sortable="false">Delete</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" class="table-body"
+ row-repeat="rowData in serviceList">
+
+ <tr>
+ <td b2b-table-body
+ ng-repeat="header in microserviceOnboarding.serviceTableHeaders"
+ ng-click="microserviceOnboarding.openAddNewMicroserviceModal(rowData)">
+ <div
+ id="microservices-catalog-microservice-name-{{rowData.id}}"
+ ng-bind="rowData[header.value]"></div>
+ </td>
+
+ <td b2b-table-body>
+ <div id="microservice-onboarding-div-delete-{{$index}}"
+ class="icon-misc-trash"
+ ng-click="microserviceOnboarding.deleteService(rowData)"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.controller.js b/ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.controller.js
new file mode 100644
index 00000000..609dba38
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.controller.js
@@ -0,0 +1,83 @@
+/*-
+ * ================================================================================
+ * 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 notificationHistoryCtrl {
+ constructor( $scope, $log,notificationService, confirmBoxService, $modal, ngDialog, $state) {
+
+ var priorityItems={"1":"Normal","2":"Important"};
+ $scope.priorityItems=priorityItems;
+ $scope.externalNotification="External System";
+ $scope.isLoadingTable = false;
+ $scope.searchString = '';
+ $scope.notificationHistory = [];
+ let getNotificationHistory = () => {
+ $scope.isLoadingTable = true;
+ notificationService.getNotificationHistory().then(res => {
+ $scope.notificationHistory = res.data;
+ $scope.isLoadingTable = false;
+ }).catch(err => {
+ $log.error('notificationHistoryCtlr:notifSvc.getNotifHist failed: ', err);
+ $scope.isLoadingTable = false;
+ });
+ }
+
+ getNotificationHistory();
+
+ $scope.showDetailedJsonMessage=function (selectedAdminNotification) {
+ var messageObject=JSON.parse(selectedAdminNotification.msgDescription);
+ var html="";
+ html+='<p>'+'Message Source'+' : '+selectedAdminNotification.msgSource+'</p>';
+ html+='<p>'+'Message Title'+' : '+selectedAdminNotification.msgHeader+'</p>';
+ for(var field in messageObject){
+ if(field=='eventDate'||field=='lastModifiedDate'){
+ html+='<p>'+field+' : '+new Date(+messageObject[field])+'</p>';
+
+ }else{
+ html+='<p>'+field+' : '+messageObject[field]+'</p>';
+
+ }
+ }
+
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/user-notifications-admin/user.notifications.Json.details.modal.page.html',
+ controller: 'userNotificationCtrl',
+ resolve: {
+ message: function () {
+ var message = {
+ title: '',
+ text: html
+
+ };
+ return message;
+ },
+
+ }
+ });
+
+
+ };
+ }
+ }
+ notificationHistoryCtrl.$inject = ['$scope', '$log', 'notificationService', 'confirmBoxService', '$modal', 'ngDialog', '$state'];
+ angular.module('ecompApp').controller('notificationHistoryCtrl', notificationHistoryCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.less b/ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.less
new file mode 100644
index 00000000..9e1de6fd
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.less
@@ -0,0 +1,41 @@
+.w-ecomp-user-notification-history {
+ .bg_portalWhite;//white for 1702
+ //.bg_portalGray; // 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;
+#input-search::-webkit-input-placeholder,
+{
+font-style: italic;
+ color: #999999;
+
+}
+ .tab-bottom {
+ bottom: 0;
+ }
+
+ .tablesorter-default .tablesorter-header .tablesorter-header-inner {
+ background-position: center right;
+ background-repeat: no-repeat;
+ cursor: pointer;
+ white-space: normal;
+ display: inline-block;
+ vertical-align: baseline;
+ zoom: 1;
+ padding: 12px 50px;
+ }
+
+ .notifHistTable {
+ width: @table-width;
+ //margin-left: @table-margin-left;
+ //margin: @table-margin;
+ margin:auto;
+ }
+
+
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.tpl.html b/ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.tpl.html
new file mode 100644
index 00000000..5cd56e97
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/notification-history/notificationhistory.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-user-notification-history"
+ ng-style="{bottom: tabBottom}">
+ <div class="user-notification-history" id="page-content">
+ <div id='app-title' class="w-ecomp-main-view-title">
+ <h1 class="heading-page" >Recent Notifications</h1>
+ </div>
+ <div class="notifHistTable">
+
+ <div class="table-control">
+ <div class="simulateCatGridHeaderDetails">This table shows
+ notifications published in the last 30 days.</div>
+ <div align="right">
+ <input class="table-search" type="text" id="input-search"
+ placeholder="Search in entire table" ng-model="searchString" />
+ </div>
+ </div>
+
+ <span class="ecomp-spinner" ng-show="isLoadingTable"></span>
+
+ <div id="table-main" class="b2b-table-div" ng-hide="isLoadingTable">
+ <table b2b-table id="notification-history-table"
+ table-data="notificationHistory"
+ search-string="searchString"
+ view-per-page="viewPerPagerIgnored"
+ current-page="ignoredCurrentPage"
+ total-page="totalPageIgnored">
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="th-notif-0" b2b-table-header key="msgSource"
+ sortable="true" style=" width: 10px;">Message Source</th>
+ <th id="th-notif-1" b2b-table-header key="msgHeader"
+ sortable="true">Message</th>
+ <th id="th-notif-2" b2b-table-header key="startTime"
+ sortable="true">Start Date (Local Time)</th>
+ <th id="th-notif-3" b2b-table-header key="endTime"
+ sortable="true">End Date (Local Time)</th>
+ <th id="th-notif-4" b2b-table-header key="priority"
+ sortable="true">Priority</th>
+ <th id="th-notif-5" b2b-table-header key="loginId"
+ sortable="true">Created By</th>
+ <th id="th-notif-6" b2b-table-header key="createdDate"
+ sortable="true">Created Time</th>
+ </tr>
+ </thead>
+
+ <tbody b2b-table-row
+ type="body"
+ class="table-body"
+ track-by="$index"
+ row-repeat="rowData in notificationHistory">
+ <tr id="row-{{$index}}">
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-msgSource">{{rowData.msgSource}}</div>
+ </td>
+ <td class="td-first" b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}" ng-click="rowData.msgSource=='EP'||showDetailedJsonMessage(rowData)">
+ <div id="{{$index}}-title" style="font-weight: bold;" ng-bind="rowData.msgHeader"></div>
+ <div id="{{$index}}-message" ng-if="rowData.msgSource==='EP'" style="width:500px" ng-bind="rowData.msgDescription"></div>
+ <div id="{{$index}}-message" ng-if="rowData.msgSource!=='EP'" ng-bind="rowData.msgDescription| elipsis: 27"></div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-startTime" ng-bind="rowData.startTime | date:'medium'"></div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-endTime" ng-bind="rowData.endTime | date:'medium'"></div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-priority" ng-bind="priorityItems[rowData.priority]"></div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-loginId" ng-bind="!rowData.loginId ? externalNotification : rowData.loginId"></div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-createdDate" ng-bind="rowData.createdDate | date:'medium'"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js b/ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js
new file mode 100644
index 00000000..54575d39
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js
@@ -0,0 +1,95 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+/**
+ * Created by nnaffar on 12/8/15.
+ */
+'use strict';
+(function () {
+ class NewPortalAdminModalCtrl {
+ constructor($log, portalAdminsService, $scope, confirmBoxService) {
+
+ let init = () => {
+ this.isSaving = false;
+ /* istanbul ignore if */
+ 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;
+ }
+ //this.searchUsersInProgress = false;
+ //this.showNewAdminAppDropdown = false;
+ $log.info('NewPortalAdminModalCtrl:: initiated');
+ };
+
+ this.addNewPortalAdmin = () => {
+ 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 => {
+ if(err.status === 409) { //Conflict
+ confirmBoxService.showInformation('This user already exists as a portal admin!').then(function (isConfirmed) {
+ $scope.closeThisDialog(true);
+ });
+ } else {
+ confirmBoxService.showInformation('There was a unknown problem adding the portal admin. ' + 'Please try again later. Error Status: '+ err.status).then(function (isConfirmed) {
+ $scope.closeThisDialog(true);
+ });
+ }
+ });
+ }
+ }).catch(err => {
+ confirmBoxService.showInformation('There was a unknown problem adding the portal admin. ' + 'Please try again later. Error Status: '+ err.status).then(function (isConfirmed) {
+ $scope.closeThisDialog(true);
+ });
+ $log.error('portalAdminsService.addPortalAdmin error status: '+ err.status);
+ });
+ };
+
+ /**
+ * this function set the selected user
+ * @param user: selected user object
+ */
+ this.setSelectedUser = (user) => {
+ $log.debug('NewPortalAdminModalCtrl::setSelectedUser: selected user: ', user);
+ this.selectedUser = user;
+ };
+
+ init();
+
+ $scope.$on('$stateChangeStart', e => {
+ //Disable navigation when modal is opened
+ //**Nabil - note: this will cause the history back state to be replaced with current state
+ e.preventDefault();
+ });
+ }
+ }
+ NewPortalAdminModalCtrl.$inject = ['$log', 'portalAdminsService', '$scope', 'confirmBoxService'];
+ angular.module('ecompApp').controller('NewPortalAdminModalCtrl', NewPortalAdminModalCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js b/ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js
new file mode 100644
index 00000000..3841a2b3
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html b/ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html
new file mode 100644
index 00000000..d328db82
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.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="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">
+
+ <button id="pa-search-users-button-save" class="btn btn-alt btn-small"
+ ng-click="newPortalAdmin.selectedUser && newPortalAdmin.addNewPortalAdmin()"
+ ng-class="{disabled: !newPortalAdmin.selectedUser}">Save
+ </button>
+ <button id="pa-search-users-button-cancel" class="btn btn-alt btn-small" ng-click="closeThisDialog()">Cancel</button>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less b/ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less
new file mode 100644
index 00000000..f8e1960c
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less
@@ -0,0 +1,80 @@
+.new-portal-admin-modal {
+ height: 430px;
+
+ .search-users {
+ }
+
+ .admin-app-roles {
+ .title {
+ //.n18r;
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @blue-active 3px solid;
+
+ }
+
+ .app-roles-main {
+ margin-top: 16px;
+ .app-roles-main-title {
+ .dGray14r;
+ margin-bottom: 8px;
+ .left {
+ display: inline-block;
+ }
+ .right {
+ display: inline-block;
+ color: @portalDBlue;
+ float: right;
+ cursor: pointer;
+ }
+ }
+
+ .select-input{
+ width: 460px;
+ }
+
+ .new-administrated-app {
+ height: 30px;
+ line-height: 30px;
+
+ border: 1px solid @portalGray;
+ margin-bottom: 8px;
+ border-radius: 2px;
+ padding-left: 6px;
+ padding-top: 0;
+ width: 100%;
+ .dGray14r;
+ }
+
+ .admin-roles-list {
+ height: 240px;
+ overflow-y: auto;
+ }
+
+ .administrated-application {
+ width: 460px;
+ height: 30px;
+ border: 1px solid @portalGray;
+ margin-bottom: 8px;
+ border-radius: 2px;
+ padding: 6px;
+ .dGray14r;
+ 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-common/client/app/views/portal-admin/portal-admin-controller.js b/ecomp-portal-FE-common/client/app/views/portal-admin/portal-admin-controller.js
new file mode 100644
index 00000000..0051e708
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/portal-admin/portal-admin-controller.js
@@ -0,0 +1,110 @@
+/*-
+ * ================================================================================
+ * 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);
+ confirmBoxService.showInformation('There was a problem retrieving the portal admins. ' +
+ 'Please try again later. Error: ' + err.status).then(isConfirmed => {});
+
+ }).finally(() => {
+ this.isLoadingTable = false;
+ });
+ };
+
+ let init = () => {
+ $log.info('portalAdminsService.getPortalAdmins::initializing...');
+ this.isLoadingTable = false;
+
+ /*Table general configuration params*/
+ this.searchString= '';
+ /*Table data*/
+ this.portalAdminsTableHeaders = ['First Name', 'Last Name', 'User ID', 'Delete'];
+ this.portalAdminsTableData = [];
+ updateTableData();
+ };
+
+ init();
+
+ this.removePortalAdmin = pAdmin => {
+ $log.debug('PortalAdminsCtrl::removePortalAdmin: 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,pAdmin.loginId).then(() => {
+ $log.info("PortalAdminsCtrl::removePortalAdmin removed admin");
+ init();
+ }).catch(err => {
+ $log.error('PortalAdminsCtrl::removePortalAdmin.deleteItem error: '+ err);
+ confirmBoxService.showInformation('There was a problem deleting this portal admins. ' +
+ 'Please try again later. Error: ' + err.status).then(isConfirmed => {});
+ });
+ }
+ }).catch(err => {
+ $log.error('PortalAdminsCtrl::removePortalAdmin.deleteItem error: '+ err);
+ });
+ };
+
+ this.openAddNewPortalAdminModal = (user) => {
+ let data = null;
+ if(user){
+ data = {
+ dialogState: 2,
+ selectedUser:{
+ orgUserId: 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();
+ }
+ });
+ };
+ }
+ }
+ PortalAdminsCtrl.$inject = ['$log', 'portalAdminsService', 'ngDialog', 'confirmBoxService'];
+ angular.module('ecompApp').controller('PortalAdminsCtrl', PortalAdminsCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/portal-admin/portal-admin.tpl.html b/ecomp-portal-FE-common/client/app/views/portal-admin/portal-admin.tpl.html
new file mode 100644
index 00000000..43f830cc
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/portal-admin/portal-admin.tpl.html
@@ -0,0 +1,64 @@
+<!--
+ ================================================================================
+ 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="page-content" >
+ <div id="title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page" >Portal Admins</h1>
+ </div>
+ <div class="portal-admins-table">
+ <div class="table-control">
+ <div class="table-control-fields">
+ <input id="input-table-search" placeholder="Search in entire table" class="table-search-field" type="text" data-ng-model="portalAdmin.searchString">
+ </div>
+ <div class="table-control-buttons">
+ <button class="btn btn-alt btn-small" ng-click="portalAdmin.openAddNewPortalAdminModal()"><i class="icon-people-userbookmark" aria-hidden="true"></i>&nbsp;Add Portal Admin</button>
+ </div>
+ </div>
+
+ <span class="ecomp-spinner" ng-show="portalAdmin.isLoadingTable"></span>
+
+ <div b2b-table table-data="portalAdmin.portalAdminsTableData" ng-hide="portalAdmin.isLoadingTable" search-string="portalAdmin.searchString" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr >
+ <th b2b-table-header key="firstName" sortable="true" id="col1">First Name</th>
+ <th b2b-table-header key="lastName" sortable="true" id="col2">Last Name</th>
+ <th b2b-table-header key="loginId" sortable="true" id="col3">User ID</th>
+ <th b2b-table-header key="" sortable="falses" id="col4">Delete</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" row-repeat="rowData in portalAdmin.portalAdminsTableData">
+ <tr>
+ <td b2b-table-body id="rowheader_t1_{{$index}}" headers="col1" ng-bind="rowData.firstName"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col2" ng-bind="rowData.lastName"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col3" ng-bind="rowData.loginId"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col4">
+ <span class="icon-misc-trash" ng-click="portalAdmin.removePortalAdmin(rowData)"></span>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/portal-admin/portal-admins.less b/ecomp-portal-FE-common/client/app/views/portal-admin/portal-admins.less
new file mode 100644
index 00000000..0471aee9
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/portal-admin/portal-admins.less
@@ -0,0 +1,42 @@
+.portal-admins-page-main {
+ .bg_portalWhite;//white for 1702
+ 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;
+
+#input-table-search::-webkit-input-placeholder,
+{
+font-style: italic;
+ color: #D7D7D7;
+
+}
+ .portal-admins-table {
+ width: @table-width;
+ //margin-left: @table-margin-left;
+ margin:auto;
+ .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-common/client/app/views/role/popup_modal_rolefunction.html b/ecomp-portal-FE-common/client/app/views/role/popup_modal_rolefunction.html
new file mode 100644
index 00000000..3970960d
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/popup_modal_rolefunction.html
@@ -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.
+ ================================================================================
+ -->
+
+<div class="new-admin-modal" style="height: 350px;">
+ <form name="adminForm" novalidate>
+ <div class="title" ng-bind="label" style="color: #5a5a5a;font-size: 18px;border-bottom: #067ab4 3px solid;margin-left:0px"></div>
+
+ <div id="name-property-title" class="property required">
+ <div id="name-property-label" class="property-label" style="margin-bottom: 5px; color: #5a5a5a; font-size: 14px; font-weight: bold; padding-top: 20px;">
+ <sup><b style="color: Red;">*</b></sup>Name:</div>
+ <input id="name-property-input" type="text" class="input-field" style=" display: inline-block; width: 396px; height: 40px;"ng-model="editRoleFunction.name" maxlength="30" />
+ <div id="name-property-required" ng-show="!editRoleFunction.name||editRoleFunction.name.length==0" style="color: #cf2a2a; font-size: 10px;"><small style="position: absolute;">Name is Required</small>
+ </div>
+ </div>
+ <br />
+ <div id="code-property-title" class="property required">
+ <div id="code-property-label" class="property-label" style=" margin-bottom: 5px; color: #5a5a5a; font-size: 14px; font-weight: bold; padding-top: 20px;">
+ <sup><b style="color: Red;">*</b></sup>Code:
+ </div>
+ <input id="code-property-input" type="text" class="input-field" style=" display: inline-block; width: 396px; height: 40px;"
+ ng-model="editRoleFunction.code" ng-disabled="disableCd"
+ maxlength="30" />
+ <div id="code-property-required" ng-show="!editRoleFunction.code||editRoleFunction.code.length==0" style="color: #cf2a2a; font-size: 10px;"><small style="position: absolute;">Code is Required</small>
+ </div>
+ </div>
+
+
+
+ <div class="dialog-control">
+
+ <button id="button-app-save" class="save-button" size="small" ng-disabled="(!editRoleFunction.name||editRoleFunction.name.length==0)||(!editRoleFunction.code||editRoleFunction.code.length==0 )" ng-click="!saveRoleFunction(editRoleFunction)";herf="javascript:void(0)">Save</button>
+
+ <div id="button-app-cancel" class="cancel-button" ng-click="closeThisDialog()" role="button" tabindex="0" herf="javascript:void(0)">Cancel</div>
+ </div>
+ </form>
+</div>
+
+
diff --git a/ecomp-portal-FE-common/client/app/views/role/role-controller.js b/ecomp-portal-FE-common/client/app/views/role/role-controller.js
new file mode 100644
index 00000000..c5c7c97c
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/role-controller.js
@@ -0,0 +1,219 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+
+app.controller('roleController', function ($scope, $http, confirmBoxService, ngDialog, RoleService, conf, $stateParams){
+ //$scope.role=${role};
+
+ $( "#dialogRoleFunction" ).hide();
+ $( "#dialogChildRole" ).hide();
+
+ //$scope.ociavailableRoleFunctions=${availableRoleFunctions};
+ $scope.fetchRoles = function() {
+
+ RoleService.getRole($stateParams.roleId).then(function(data){
+
+ var j = data;
+ $scope.data = JSON.parse(j.data);
+
+ $scope.role =JSON.parse($scope.data.role);
+
+ $scope.ociavailableRoleFunctions =JSON.parse($scope.data.availableRoleFunctions);
+ $scope.availableRoleFunctions=[];
+
+ if($scope.ociavailableRoleFunctions)
+ $.each($scope.ociavailableRoleFunctions, function(i, a){
+ var availableRoleFunction = a;
+ availableRoleFunction.selected = false;
+ $.each($scope.role.roleFunctions, function(j, b){
+ if(a.code === b.code) {
+ availableRoleFunction.selected = true;
+ }
+ });
+ $scope.availableRoleFunctions.push(availableRoleFunction);
+ });
+
+
+ $scope.ociavailableRoles=JSON.parse($scope.data.availableRoles);
+ $scope.availableRoles=[];
+
+ if($scope.ociavailableRoles)
+ $.each($scope.ociavailableRoles, function(i, a){
+ var availableRole = a;
+ availableRole.selected = false;
+ if($scope.role.childRoles){
+ $.each($scope.role.childRoles, function(j, b){
+ if(a.id === b.id) {
+ availableRole.selected = true;
+ }
+ });
+ };
+ $scope.availableRoles.push(availableRole);
+ });
+
+
+ },function(error){
+ console.log("RoleService.getRole failed", error);
+ //reloadPageOnce();
+ });
+ }
+
+ $scope.fetchRoles();
+
+ $scope.saveRole = function() {
+ var exists = false,x;
+ for(x in $scope.availableRoles){
+ if($scope.availableRoles[x].name==$scope.role.name){
+ exists = true;
+ //$modalInstance.close({availableRoleFunctions:message.availableRoleFunctions});
+ }
+ }
+ if (exists) {
+ confirmBoxService.showInformation( "Role already exists.");
+ }
+ else {
+ var uuu = conf.api.saveRole + "?role_id="+$stateParams.roleId;
+ var postData = {
+ role: $scope.role,
+ childRoles: $scope.role.childRoles,
+ roleFunctions : $scope.role.roleFunctions
+ };
+ $http.post(uuu, JSON.stringify(postData)).then(function(res) {
+ // console.log('roleController::saveRole: ' + JSON.stringify(res));
+ if (res && res.data && res.data.role)
+ confirmBoxService.showInformation("Update Successful.");
+ else
+ confirmBoxService.showInformation('Failed to create role: ' + res.data.error)
+ },
+ function(res){
+ console.log('post failed', res.data);
+ confirmBoxService.showInformation("Error while saving.");
+ }
+ );
+ }
+ };
+
+ $scope.addNewRoleFunctionModalPopup = function() {
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/role/role_functions_popup.html',
+ controller: 'rolepopupController',
+
+ resolve: {
+ roleId: function () {
+ return $stateParams.roleId;
+ },
+ role: function () {
+ return $scope.role;
+ },
+ availableRoles: function () {
+ return $scope.ociavailableRoles;
+ },
+ availableRoleFunctions: function () {
+ return $scope.ociavailableRoleFunctions;
+ },
+ }
+ });
+ modalInstance.closePromise.then(response => {
+ if($stateParams.roleId === '0'){
+ return $scope.role;
+ }else{
+ $scope.fetchRoles();
+ }
+ // $scope.role=response.role;
+ });
+ };
+
+ $scope.addNewChildRoleModalPopup = function() {
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/role/role_childrole_popup.html',
+ controller: 'rolepopupController',
+
+ resolve: {
+ roleId: function () {
+ return $stateParams.roleId;
+ },
+ role: function () {
+ return $scope.role;
+ },
+ availableRoles: function () {
+ return $scope.ociavailableRoles;
+ },
+ availableRoleFunctions: function () {
+ return $scope.ociavailableRoleFunctions;
+ },
+ }
+ }).closePromise.then(function(response){
+ if($stateParams.roleId === '0'){
+ return $scope.role;
+ }else{
+ $scope.fetchRoles();
+ }
+ //$scope.role=response.role;
+ });
+ };
+
+ $scope.removeRoleFunction = function(roleFunction) {
+ confirmBoxService.confirm("You are about to remove the role function "+roleFunction.name+" from the role for "+$scope.role.name+". Do you want to continue?").then(
+ function(confirmed){
+ var uuu = conf.api.toggleRoleRoleFunction + "?role_id=" + $stateParams.roleId;
+ var postData={roleFunction:roleFunction};
+ if(confirmed) {
+ $http.post(uuu, postData).then(
+ function(response) {
+ $scope.role= response.data.role;
+ $.each($scope.availableRoleFunctions, function(k, c){
+ if(c.code === roleFunction.code) {
+ c.selected = false;
+ }
+ });
+ },
+ function(response) {
+ confirmBoxService.showInformation("Error while saving.");
+ }
+ );
+ }
+
+ });
+
+ };
+
+ $scope.removeChildRole = function(childRole) {
+ confirmBoxService.confirm("You are about to remove the child role "+childRole.name+" from the role for "+$scope.role.name+". Do you want to continue?").then(
+ function(confirmed){
+ var uuu = conf.api.toggleRoleChildRole + "?role_id=" + $stateParams.roleId;
+ var postData={childRole:childRole};
+ if(confirmed) {
+ $http.post(uuu,postData).then( function(response) {
+ $scope.role=response.data.role;
+ $.each($scope.availableRoles, function(k, c){
+ if(c.id === childRole.id) {
+ c.selected = false;
+ }
+ });
+ },
+
+ function(data) {
+ confirmBoxService.showInformation("Error while saving.");
+ });
+ }
+ });
+
+ };
+
+});
diff --git a/ecomp-portal-FE-common/client/app/views/role/role-function-list-controller.js b/ecomp-portal-FE-common/client/app/views/role/role-function-list-controller.js
new file mode 100644
index 00000000..c7a1bfd8
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/role-function-list-controller.js
@@ -0,0 +1,160 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+app.controller('roleFunctionListController', function ($scope,RoleService,$http,$state,conf,confirmBoxService, ngDialog){
+ $( "#dialog" ).hide();
+
+ RoleService.getRoleFunctionList().then(function(data){
+
+ var j = data;
+ $scope.data = JSON.parse(j.data);
+ $scope.availableRoleFunctions =JSON.parse($scope.data.availableRoleFunctions);
+
+ //$scope.resetMenu();
+
+ },function(error){
+ console.log("failed");
+ //reloadPageOnce();
+ });
+
+ $scope.editRoleFunction = null;
+ var dialog = null;
+ $scope.editRoleFunctionPopup = function(availableRoleFunction) {
+ $scope.editRoleFunction = availableRoleFunction;
+ $( "#dialog" ).dialog({
+ modal: true
+ });
+ };
+
+ $scope.editRoleFunctionModalPopup = function(availableRoleFunction) {
+ $scope.editRoleFunction = availableRoleFunction;
+ $scope.availableRoleFunctionsTemp=$scope.availableRoleFunctions;
+ //$scope.availableRoleFunctions={};
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/role/popup_modal_rolefunction.html',
+ controller: 'rolefunctionpopupController',
+ resolve: {
+ message: function () {
+ var message = {
+ availableRoleFunction: $scope.editRoleFunction,
+ availableRoleFunctions: $scope.availableRoleFunctionsTemp
+ };
+ return message;
+ },
+ isEditing: function () {
+ return true;
+ }
+ }
+ });
+ modalInstance.closePromise.then(response =>{
+ if(response.value!=null){
+ if(response.value.result){
+ $scope.availableRoleFunctions=response.value.availableRoleFunctions;
+ }
+ }
+ /*else
+ $scope.availableRoleFunctions=$scope.availableRoleFunctionsTemp;
+ */
+ });
+ };
+
+ $scope.addNewRoleFunctionModalPopup = function(availableRoleFunction) {
+
+ $scope.editRoleFunction = null;
+ $scope.availableRoleFunctionsTemp=$scope.availableRoleFunctions;
+ //$scope.availableRoleFunctions={};
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/role/popup_modal_rolefunction.html',
+ controller: 'rolefunctionpopupController',
+ resolve: {
+ message: function () {
+ var message = {
+ availableRoleFunction: $scope.editRoleFunction,
+ availableRoleFunctions: $scope.availableRoleFunctionsTemp
+ };
+ return message;
+ },
+ isEditing: function () {
+ return false;
+ }
+ }
+ });
+ modalInstance.closePromise.then(response => {
+ if(response.value!=null){
+ if(response.value.result){
+ $scope.availableRoleFunctions=response.value.availableRoleFunctions;
+ }
+ }
+ /* if(response.availableRoleFunctions != undefined)
+ $scope.availableRoleFunctions=response.availableRoleFunctions;
+ else
+ $scope.availableRoleFunctions=$scope.availableRoleFunctionsTemp;
+ */
+ });
+ };
+
+ $scope.addNewRoleFunctionPopup = function() {
+ $scope.editRoleFunction = null;
+ $( "#dialog" ).dialog({
+ modal: true
+ });
+ };
+
+ $scope.saveRoleFunction = function(availableRoleFunction) {
+ var uuu = conf.api.saveRoleFuncion;
+ var postData={availableRoleFunction: availableRoleFunction};
+ $http.post(uuu,postData).then(function(response) {
+ var data = response.data;
+ $scope.availableRoleFunctions=data.availableRoleFunctions;
+ $scope.editRoleFunction = null;
+ },
+ function() {
+
+ confirmBoxService.showInformation("Error while saving");
+ }
+ );
+ };
+
+
+ $scope.removeRole = function(availableRoleFunction) {
+ confirmBoxService.confirm("You are about to delete the role function "+availableRoleFunction.name+". Do you want to continue?").then(
+ function(confirmed){
+ if(confirmed){
+ $scope.availableRoleFunctionsTemp=$scope.availableRoleFunctions;
+ //$scope.availableRoleFunctions={};
+ var uuu = conf.api.removeRoleFunction;
+ var postData={availableRoleFunction: availableRoleFunction};
+ $http.post(uuu,postData).then(function(response) {
+ var data = response.data;
+ if(data.availableRoleFunctions == undefined)
+ confirmBoxService.showInformation("Error while deleting: "+ data);
+ else
+ $scope.availableRoleFunctions=data.availableRoleFunctions;
+ },
+ function() {
+ $scope.availableRoleFunctions=$scope.availableRoleFunctionsTemp;
+ confirmBoxService.showInformation("Error while deleting: "+ data.responseText);
+ }
+ );
+ }
+ });
+
+ };
+
+});
diff --git a/ecomp-portal-FE-common/client/app/views/role/role-list-controller.js b/ecomp-portal-FE-common/client/app/views/role/role-list-controller.js
new file mode 100644
index 00000000..5ef37165
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/role-list-controller.js
@@ -0,0 +1,147 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+app.controller('roleListController', function ($scope,RoleService,confirmBoxService,conf,$state,$http){
+ $scope.showSpinner = true;
+
+ RoleService.getRoles().then(function(data){
+ $scope.showSpinner = true;
+ var j = data;
+ $scope.data = JSON.parse(j.data);
+ $scope.availableRoles =JSON.parse($scope.data.availableRoles);
+ $scope.showSpinner = false;
+ //$scope.resetMenu();
+
+ },function(error){
+ console.log("failed");
+ //reloadPageOnce();
+ });
+
+
+ $scope.goToUrl = function(roleIdVal) {
+ $state.go("root.role", {"roleId":roleIdVal});
+ }
+ //console.log($scope.availableRoles);
+ $scope.toggleRole = function(selected,availableRole) {
+ //alert('toggleRole: '+selected);
+ var toggleType = null;
+ if(selected) {
+ toggleType = "activate";
+ } else {
+ toggleType = "inactivate";
+ }
+
+ confirmBoxService.confirm("You are about to "+toggleType+" the role "+availableRole.name+". Do you want to continue?").then(
+ function(confirmed){
+
+ if(confirmed) {
+ var uuu = conf.api.toggleRole;
+
+ var postData={role:availableRole};
+ $http.post(uuu, postData).then(function(response) {
+ var data = response.data;
+ if (typeof data === 'object') {
+ console.log(data);
+ $scope.availableRoles=data.availableRoles;
+ console.log($scope.availableRoles);
+ } else {
+ //
+ }
+
+ }, function(response) {
+ console.log(response.data);
+ availableRole.active=!availableRole.active;
+ confirmBoxService.showInformation("Error while saving.");
+ });
+
+ /*
+ $.ajax({
+ type : 'POST',
+ url : uuu,
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify(postData),
+ success : function(data){
+ console.log(data);
+ $scope.$apply(function(){$scope.availableRoles=data.availableRoles;});
+ console.log($scope.availableRoles);
+ },
+ error : function(data){
+ console.log(data);
+ availableRole.active=!availableRole.active;
+ confirmBoxService.showInformation("Error while saving.");
+ }
+ });
+ */
+ }
+ else {
+ availableRole.active=!availableRole.active;
+ }
+
+ });
+ //,
+ //function(){
+ // availableRole.active=!availableRole.active;
+ //})
+
+
+ };
+
+ $scope.removeRole = function(role) {
+
+ confirmBoxService.confirm("You are about to delete the role "+role.name+". Do you want to continue?").then(
+ function(confirmed){
+ var uuu = conf.api.removeRole;
+ var postData={role:role};
+ /* $.ajax({
+ type : 'POST',
+ url : uuu,
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify(postData),
+ success : function(data){
+ $scope.$apply(function(){$scope.availableRoles=data.availableRoles;});
+ },
+ error : function(data){
+ console.log(data);
+ confirmBoxService.showInformation("Error while deleting: "+ data.responseText);
+ }
+ }); */
+
+
+ $http.post(uuu, postData).then(function(response) {
+ var data = response.data;
+ if (typeof data === 'object') {
+ $scope.availableRoles=data.availableRoles;
+ } else {
+ //
+ }
+
+ }, function(response) {
+ console.log(response.data);
+ confirmBoxService.showInformation("Error while deleting: "+ data.responseText);
+ });
+
+ });
+
+
+ };
+
+
+});
diff --git a/ecomp-portal-FE-common/client/app/views/role/role.html b/ecomp-portal-FE-common/client/app/views/role/role.html
new file mode 100644
index 00000000..42793eee
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/role.html
@@ -0,0 +1,96 @@
+<!--
+ ================================================================================
+ 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-admins-page-main">
+ <div class="admins-home-container" id="page-content">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h3 class="heading-page" >Role</h3>
+ </div>
+ <div class="admins-table" >
+ <div>
+ <br>
+ <p class="p-info">Please edit the role details below:&nbsp;</p><br>
+
+ <div id="role-text" >
+ <label id="role-text-name-label" >*Name:</label>
+ <input id="input-name" class="input-field" maxlength="30" type="text" data-ng-model="role.name">
+ <br>
+ <label id="role-text-priority-label">Priority:</label>
+ <input id="input-priority" class="input-field" maxlength="30" type="text" data-ng-model="role.priority">
+ </div>
+ <br>
+ <div align="left" >
+ <button id="button-role-save" type="submit" ng-click="saveRole();" class="btn btn-alt btn-small">Save</button>
+ </div>
+
+ <br>
+ <div id="page-title" class="pageTitle">
+ <label>Role Functions</label>
+ <a id="add-new-role" ng-click="addNewRoleFunctionModalPopup();" ng-style="{'cursor':'pointer'}" class="icon-primary-accordion-plus" size="small"></a>
+ </div>
+
+ <div b2b-table table-data="role.roleFunctions" ng-hide="users.isLoadingTable" search-string="users.searchString" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr >
+ <th id="table-header-name" b2b-table-header key="name" sortable="true" >Name</th>
+ <th id="table-header-remove" b2b-table-header sortable="false" >Remove</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" row-repeat="roleFunction in role.roleFunctions">
+ <tr >
+ <td id="role-function-{{roleFunction.name}}" b2b-table-body id="rowheader_t1_{{$index}}" headers="col1" ng-bind="roleFunction.name"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col4">
+ <div ng-click="removeRoleFunction(roleFunction);" ><a href="javascript:void(0)" class="icon-misc-trash"></a></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <a id="manage-role" href="roleFunctions">Manage Role Functions</a><br><br>
+
+ <div id="page-title-child" class="pageTitle">
+ <label>Child Roles</label>
+ <a id="add-child-role" ng-click="addNewChildRoleModalPopup();" ng-style="{'cursor':'pointer'}" class="icon-primary-accordion-plus" size="small"></a>
+ </div>
+
+ <div b2b-table table-data="role.childRoles" ng-hide="users.isLoadingTable" search-string="users.searchString" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr >
+ <th id="table-header-name" b2b-table-header key="firstName" sortable="true" >Name</th>
+ <th id="table-header-remove" b2b-table-header key="lastName" sortable="true" >Remove</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" row-repeat="role in role.childRoles">
+ <tr >
+ <td id="role-function-{{role.name}}" b2b-table-body id="rowheader_t1_{{$index}}" headers="col1" ng-bind="role.name"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col4">
+ <div ng-click="removeChildRole(role);" ><a href="javascript:void(0)" class="icon-misc-trash"></a></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/role/role.less b/ecomp-portal-FE-common/client/app/views/role/role.less
new file mode 100644
index 00000000..42311e64
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/role.less
@@ -0,0 +1,50 @@
+.p-info {
+ font-size: 18px;
+}
+
+.input-field {
+ width: 250px !important;
+}
+
+.scrolling-table {
+ width: 548px;
+ margin-left: 16px;
+ height:575px;
+ overflow:auto;
+}
+
+.scrolling-table .scroll-viewport {
+ height: 200px !important;
+ width: 99.5% !important;
+ background-color: white;
+}
+
+.scrolling-table .scroll-overview {
+ margin-top: -14px !important;
+}
+
+.scrolling-table .scroll-viewport:hover {
+ background-color: white;
+}
+
+.scrolling-table #portal-scroll-table-content {
+ height: 200px;
+ position: absolute !important;
+ width: 548px;
+ padding-left: 0px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ padding-right: 5px;
+}
+
+.vertical .scroll-thumb {
+ width: 13px !important;
+}
+
+.vertical .scroll-bar {
+ width: 15px !important;
+}
+
+.scroll-bar {
+ border-radius: 0px !important;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/role/role_childrole_popup.html b/ecomp-portal-FE-common/client/app/views/role/role_childrole_popup.html
new file mode 100644
index 00000000..278ef2cd
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/role_childrole_popup.html
@@ -0,0 +1,51 @@
+<!--
+ ================================================================================
+ 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.
+ ================================================================================
+ -->
+<link rel="stylesheet" href="role.css">
+<div class="application-details-modal">
+ <div id="title" class="title">Select Role Functions</div>
+<div class="scrolling-table">
+
+ <div b2b-table table-data="availableRoles" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr >
+ <th b2b-table-header key="firstName" sortable="true" id="col1"></th>
+ <th b2b-table-header key="name" sortable="true" id="col2">Name</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" row-repeat="rowData in availableRoles">
+ <tr>
+ <td b2b-table-body id="rowheader_t1_{{$index}}" headers="col1">
+ <label class="btn-switch-label" tabindex="0" role="option">
+ <input type="checkbox" b2b-switches ng-model="rowData.selected" ng-click="toggleChildRole(rowData.selected,rowData);">
+ </label>
+ </td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col2" ng-bind="rowData.name"></td>
+
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
+ <div class="dialog-control">
+ <button id="button-app-cancel" class="btn btn-alt btn-small" ng-click="closeThisDialog()">Close</button>
+ </div>
+</div>
+
diff --git a/ecomp-portal-FE-common/client/app/views/role/role_function_list.html b/ecomp-portal-FE-common/client/app/views/role/role_function_list.html
new file mode 100644
index 00000000..c015ee56
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/role_function_list.html
@@ -0,0 +1,107 @@
+<!--
+ ================================================================================
+ 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>
+.c-ecomp-abs-table .tablesorter-default tbody tr td div {
+ line-height: 20px;
+}
+
+.c-ecomp-abs-table{
+ width: 100%;
+ height: 600px;
+ overflow-x: auto;
+ overflow-y: auto;
+ background-color: white;
+}
+</style>
+<div class="w-ecomp-admins-page-main">
+ <div class="admins-home-container" id="page-content">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h3 class="heading-page" >Role Functions</h3>
+ </div>
+
+
+ <br>
+
+ <div class="admins-table">
+
+ <!-- <a ng-click="addNewRoleFunctionModalPopup();" class="icon-add" size="small" ></a> -->
+ <div id="create-new-admin" align="left" style="marin-bottom: 50px;">
+ <button id="button-admin-create" type="submit" ng-click="addNewRoleFunctionModalPopup();"
+ class="btn btn-alt btn-small">Create</button>
+ </div>
+
+ <div style="text-align: justify;text-align-last:auto;">
+ <!-- Click on the edit icon to update a role function, the plus icon to add additional role functions, or the delete icon to remove them. -->
+ </div>
+
+ <div id="rolesTable" class="c-ecomp-abs-table default" title="Role Functions">
+ <table b2b-table table-data="availableRoleFunctions" current-page="1">
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="table-header-name" b2b-table-header width="70%" key="name" sortable="true">Name</th>
+ <th id="table-header-code" b2b-table-header width="10%" key="code" sortable="true">Code</th>
+ <th id="table-header-edit" b2b-table-header width="10%" sortable="false">Edit?</th>
+ <th id="table-header-delete" b2b-table-header width="10%" sortable="false">Delete?</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" row-repeat="availableRoleFunction in availableRoleFunctions" style="max-height: 980px;" ><!-- background colors will alternate not properly with multiple tbody-->
+ <tr>
+ <td id="table-body-role-{{availableRoleFunction['name'].split(' ').join('-')}}" b2b-table-body width="70%" ng-bind="availableRoleFunction['name']"></td>
+ <td id="table-body-role-{{availableRoleFunction['code']}}" b2b-table-body width="10%" ng-bind="availableRoleFunction['code']"></td>
+ <td id="table-body" b2b-table-body width="10%">
+ <!-- <a ng-click="editRoleFunctionPopup(availableRoleFunction);" >
+ <img src="static/fusion/images/editicon.gif">
+ </a> -->
+ <div ng-click="editRoleFunctionModalPopup(availableRoleFunction);" style="font-size:20px;"><a id="role-{{availableRoleFunction['name'].split(' ').join('-')}}-edit" href="javascript:void(0)" class="icon-edit"></a></div>
+ </td>
+ <td b2b-table-body width="10%">
+ <!-- <a ng-click="removeRole(availableRoleFunction);" ><img src="static/fusion/images/deleteicon.gif"></a> -->
+ <div ng-click="removeRole(availableRoleFunction);" style="font-size:20px;"><a id="role-{{availableRoleFunction['name'].split(' ').join('-')}}-trash" href="javascript:void(0)" class="icon-misc-trash"></a></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+
+
+ </div>
+
+
+ <div id="dialog" title="Add Role Function">
+
+ <div id="fn-ebz-container-name" class="fn-ebz-container" >
+ <label id="fn-ebz-label-name" class="fn-ebz-text-label"><sup><b>*</b></sup>Name:</label><br>
+ <input id="fn-ebz-input-{{editRoleFunction.name}}" type="text" class="fn-ebz-text" ng-model="editRoleFunction.name"
+ maxlength="30" />
+ </div>
+ <br/>
+ <div id="fn-ebz-container-code" class="fn-ebz-container" >
+ <label id="fn-ebz-label-code" class="fn-ebz-text-label"><sup><b>*</b></sup>Code:</label><br>
+ <input id="fn-ebz-input-{{editRoleFunction.code}}" type="text" class="fn-ebz-text" ng-model="editRoleFunction.code" ng-disabled="editRoleFunction.code!=null"
+ maxlength="30" />
+ </div>
+ <br/>
+ <button id="button-admin-save" type="submit" ng-click="saveRoleFunction(editRoleFunction);" class="btn btn-alt btn-small">Save</button>
+
+ </div>
+
+</div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/role/role_functions_popup.html b/ecomp-portal-FE-common/client/app/views/role/role_functions_popup.html
new file mode 100644
index 00000000..7d80d81e
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/role_functions_popup.html
@@ -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.
+ ================================================================================
+ -->
+<link rel="stylesheet" href="role.css">
+<div class="application-details-modal">
+ <div id="title" class="title">Select Role Functions</div>
+<div class="scrolling-table">
+ <div b2b-table table-data="availableRoleFunctions" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr >
+ <th b2b-table-header key="firstName" sortable="true" id="col1"></th>
+ <th b2b-table-header key="name" sortable="true" id="col2">Name</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" row-repeat="rowData in availableRoleFunctions">
+ <tr>
+ <td b2b-table-body id="rowheader_t1_{{$index}}" headers="col1">
+ <label class="btn-switch-label" tabindex="0" role="option">
+ <input type="checkbox" b2b-switches ng-model="rowData.selected" ng-click="toggleRoleFunction(rowData.selected,rowData);">
+ </label>
+ </td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col2" ng-bind="rowData.name"></td>
+
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
+ <div class="dialog-control">
+ <button id="button-app-cancel" class="btn btn-alt btn-small" ng-click="closeThisDialog()">Close</button>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/role/role_list.html b/ecomp-portal-FE-common/client/app/views/role/role_list.html
new file mode 100644
index 00000000..e8d56cab
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/role_list.html
@@ -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.
+ ================================================================================
+ -->
+<div class="w-ecomp-admins-page-main">
+ <div class="admins-home-container" id="page-content">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page" >Roles</h1>
+ </div>
+ <div class="admins-table">
+ <div id="button-create-role" align="left" style="marin-bottom: 50px;" >
+ <button ng-click="goToUrl(0)" class = "btn btn-alt btn-small">Create</button>
+ </div>
+ <div b2b-table table-data="availableRoles" search-string="searchString" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr>
+ <th b2b-table-header key="name" sortable="true" default-sort="a" id="col1">Name</th>
+ <th b2b-table-header key="priority" sortable="true" id="col2">Priority</th>
+ <th b2b-table-header id="col3" sortable="false">Active</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" row-repeat="rowData in availableRoles">
+ <tr>
+ <td b2b-table-body id="rowheader_t1_{{$index}}" headers="col1" ng-bind="rowData['name']"></td>
+ <td b2b-table-body ng-bind="rowData['priority']"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col3" >
+ <div >
+ <label class="btn-switch-label" tabindex="0" role="option">
+ <input type="checkbox" b2b-switches ng-model="rowData.active" ng-click="toggleRole(rowData.active,rowData);">
+ </label>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/role/rolefunctionpopupController.js b/ecomp-portal-FE-common/client/app/views/role/rolefunctionpopupController.js
new file mode 100644
index 00000000..6275c760
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/rolefunctionpopupController.js
@@ -0,0 +1,95 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+app.controller('rolefunctionpopupController', function ($scope, confirmBoxService, message, $http,RoleService, conf, isEditing){
+ if(message.availableRoleFunction==null) {
+ $scope.label='Add Role Function';
+ var tempText = "";
+ }
+ else{
+ $scope.label='Edit Role Function'
+ $scope.disableCd=true;
+ var tempText = new String(message.availableRoleFunction.name);
+ $scope.editRoleFunction = angular.copy(message.availableRoleFunction);
+ }
+
+ $scope.tempText = tempText;
+ $scope.isEditing = isEditing;
+
+ $scope.saveRoleFunction = function(availableRoleFunction) {
+ var uuu = conf.api.saveRoleFunction;
+ var postData={availableRoleFunction: availableRoleFunction};
+
+ if(availableRoleFunction==null){
+ confirmBoxService.showInformation("Please enter valid role function details.");
+ }
+ var exists = false,x;
+ for(x in message.availableRoleFunctions){
+ console.log(message.availableRoleFunctions[x].name);
+ if(message.availableRoleFunctions[x].name==availableRoleFunction.name){
+ confirmBoxService.showInformation("Role Function already exists.");
+ exists = true;
+ availableRoleFunction.name = $scope.tempText;
+ break;
+ }
+ if(!isEditing){
+ if (message.availableRoleFunctions[x].code == availableRoleFunction.code) {
+ confirmBoxService.showInformation("Code already exists. Please create a role function with a different code to proceed.");
+ exists = true;
+ availableRoleFunction.name = $scope.tempText;
+ break;
+ }
+ }
+ }
+
+ if(!exists && availableRoleFunction.name.trim() != '' && availableRoleFunction.code.trim() != ''){
+ $http.post(uuu, JSON.stringify(postData)).then(function(res){
+ console.log("data");
+// console.log(res.data);
+// $scope.availableRoleFunctionsTemp = res.data.availableRoleFunctions;
+ RoleService.getRoleFunctionList().then(function(data){
+
+ var j = data;
+ $scope.data = JSON.parse(j.data);
+ $scope.availableRoleFunctions =JSON.parse($scope.data.availableRoleFunctions);
+
+ //$scope.resetMenu();
+ $scope.closeThisDialog({result: true, availableRoleFunctions: $scope.availableRoleFunctions });
+ },function(error){
+ console.log("failed");
+ //reloadPageOnce();
+ $scope.closeThisDialog(true);
+ });
+
+
+ });
+
+
+
+
+ }
+ };
+
+
+
+ $scope.close = function() {
+ this.closeThisDialog(true);
+ };
+}
+);
diff --git a/ecomp-portal-FE-common/client/app/views/role/rolepopupmodelController.js b/ecomp-portal-FE-common/client/app/views/role/rolepopupmodelController.js
new file mode 100644
index 00000000..7feb7f2f
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/role/rolepopupmodelController.js
@@ -0,0 +1,251 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+app.controller('rolepopupController', function ($scope, role, roleId, confirmBoxService, $http, $state, availableRoles, conf, availableRoleFunctions,ngDialog){
+
+ $scope.role = role;
+ console.log($scope.role);
+ if($scope.role.childRoles==null){
+ $scope.role.childRoles=[];
+ }
+
+ $scope.ociavailableRoles=availableRoles;
+ console.log($scope.ociavailableRoles);
+
+ $scope.availableRoles=[];
+ if($scope.ociavailableRoles)
+ $.each($scope.ociavailableRoles, function(i, a){
+ var availableRole = a;
+ availableRole.selected = false;
+ if($scope.role.childRoles){
+ $.each($scope.role.childRoles, function(j, b){
+ if(a.id === b.id) {
+ availableRole.selected = true;
+ }
+ });
+ };
+ $scope.availableRoles.push(availableRole);
+ });
+
+ $scope.ociavailableRoleFunctions = availableRoleFunctions;
+ console.log($scope.ociavailableRoleFunctions);
+ $scope.availableRoleFunctions = [];
+ if($scope.ociavailableRoleFunctions)
+ $.each($scope.ociavailableRoleFunctions, function(i, a){
+ var availableRoleFunction = a;
+ availableRoleFunction.selected = false;
+ $.each($scope.role.roleFunctions, function(j, b){
+ if(a.code === b.code) {
+ availableRoleFunction.selected = true;
+ }
+ });
+ $scope.availableRoleFunctions.push(availableRoleFunction);
+ });
+ //$scope.resetMenu();
+
+ $scope.toggleRoleFunction = function(selected,availableRoleFunction) {
+ //alert('toggleRole: '+selected);
+
+ if(!selected) {
+ //remove role function
+ if(role.id==null){
+ var index = $scope.role.roleFunctions.indexOf(availableRoleFunction);
+ if(index>=0)
+ $scope.role.roleFunctions.splice(index, 1);
+ return;
+ }
+ var uuu = conf.api.toggleRoleRoleFunction + "?role_id=" + roleId;
+ confirmBoxService.confirm("You are about to remove the role function "+availableRoleFunction.name+" from the role for "+$scope.role.name+". Do you want to continue?").then(
+ function(confirmed){
+ if(confirmed) {
+ var postData={roleFunction:availableRoleFunction};
+ $http.post(uuu, postData).then(
+ function(response) {
+ $scope.role= response.data.role;
+ },
+ function(response) {
+ confirmBoxService.showInformation("Error while saving.");
+ }
+ );
+
+
+
+
+ }
+ else {
+ availableRoleFunction.selected=!availableRoleFunction.selected;
+ }
+ /*
+ $.ajax({
+ type : 'POST',
+ url : uuu,
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify(postData),
+ success : function(data){
+ $scope.$apply(function(){$scope.role=data.role;});
+ },
+ error : function(data){
+ modalService.showFailure("Fail","Error while saving.");
+ }
+ });
+ */
+ });
+
+ } else {
+ //add role function
+ if(role.id==null){
+ $scope.role.roleFunctions.push(availableRoleFunction);
+ return;
+ }
+ var uuu = conf.api.addRoleRoleFunction + "?role_id=" + roleId;
+
+ confirmBoxService.confirm("You are about to add the role function "+availableRoleFunction.name+" to the role for "+$scope.role.name+". Do you want to continue?").then(
+ function(confirmed){
+ if(confirmed) {
+ var postData={roleFunction:availableRoleFunction};
+ $http.post(uuu,postData).then( function(response) {
+ $scope.role=response.data.role;
+ },
+
+ function(data) {
+ confirmBoxService.showInformation("Error while saving.");
+ });
+ } else {
+ availableRoleFunction.selected=!availableRoleFunction.selected;
+ }
+ /*
+ $.ajax({
+ type : 'POST',
+ url : uuu,
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify(postData),
+ success : function(data){
+ $scope.$apply(function(){$scope.role=data.role;});
+ },
+ error : function(data){
+ modalService.showFailure("Fail","Error while saving.");
+ }
+ });
+
+
+ },
+ function(){
+ availableRoleFunction.selected=!availableRoleFunction.selected;
+ })*/
+ });
+
+
+ }
+ };
+
+ $scope.toggleChildRole = function(selected,availableRole) {
+ //alert('toggleRole: '+selected);
+
+ if(!selected) {
+ //remove role
+ if(role.id==null){
+ var index = $scope.role.childRoles.indexOf(availableRole);
+ if(index>=0)
+ $scope.role.childRoles.splice(index, 1);
+ return;
+ }
+ var uuu = conf.api.toggleRoleChildRole +"?role_id=" + roleId;
+
+ confirmBoxService.confirm("You are about to remove the child role "+availableRole.name+" from the role for "+$scope.role.name+". Do you want to continue?").then(
+ function(confirmed){
+ if(confirmed) {
+
+ var postData={childRole:availableRole};
+ $http.post(uuu,postData).then(function(response) {
+ $scope.role=response.data.role;
+ },
+ function(data) {
+ confirmBoxService.showInformation("Error while saving.");
+ });
+ } else {
+ availableRole.selected=false;
+ }
+ });
+ /* $.ajax({
+ type : 'POST',
+ url : uuu,
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify(postData),
+ success : function(data){
+ console.log('role',data.role);
+ $scope.$apply(function(){$scope.role=data.role;});
+ },
+ error : function(data){
+ modalService.showFailure("Fail","Error while saving.");
+ }
+ });
+ */
+
+
+ } else {
+ //add role
+ if(role.id==null){
+ $scope.role.childRoles.push(availableRole);
+ return;
+ }
+ var uuu = conf.api.addRoleChildRole +"?role_id=" + roleId;
+
+ confirmBoxService.confirm("You are about to add the child role "+availableRole.name+" to the role for "+$scope.role.name+". Do you want to continue?").then(
+ function(confirmed){
+ if(confirmed) {
+ var postData={childRole:availableRole};
+ $http.post(uuu,postData).then(function(response) {
+ $scope.role=response.data.role;
+ },
+ function(data) {
+ confirmBoxService.showInformation("Error while saving.");
+ });
+ } else {
+ availableRole.selected=false;
+ }
+ /* $.ajax({
+ type : 'POST',
+ url : uuu,
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify(postData),
+ success : function(data){
+ $scope.$apply(function(){$scope.role=data.role;});
+ },
+ error : function(data){
+ modalService.showFailure("Fail","Error while saving.");
+ }
+ });*/
+
+ });
+
+
+ }
+ };
+
+ $scope.close = function() {
+ console.log('role', $scope.role);
+ //$modalInstance.close({role:$scope.role});
+ this.closeThisDialog(true);
+ };
+
+});
diff --git a/ecomp-portal-FE-common/client/app/views/search/search.less b/ecomp-portal-FE-common/client/app/views/search/search.less
new file mode 100644
index 00000000..11c71f9a
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/search/search.less
@@ -0,0 +1,66 @@
+.searchLiHeader {
+font-weight: bold;
+color: #0574ac;
+font-size: 16px;
+padding-bottom: 10px;
+line-height: 1.5;
+font-family: Omnes-ECOMP-W02,Arial;
+
+}
+
+.searchLiItems{
+cursor: pointer;
+font-weight: normal;
+font-size: 12px;
+color: #444444;
+font-family: Omnes-ECOMP-W02,Arial;
+}
+
+.searchUl {
+list-style: none;
+border-bottom: 1px solid #bbb;
+padding-bottom: 20px;
+}
+
+#contentVertical {
+ height: 300px;
+ width: 250px;
+ overflow:auto;
+ }
+
+#contentVertical .scroll-viewport {
+ height: 300px;
+ width: 250px;
+ }
+#mainSearchText::-ms-clear{
+ display:none;
+}
+.search-input input{
+ height: 30px;
+ width:265px;
+ border-radius: 10px;
+ border: 2px solid rgb(250, 250, 250);
+ font-size: 12px !important;
+ font-family: Omnes-ECOMP-W02,Arial;
+}
+
+.search-div{
+ left: -270px;
+ position: relative;
+}
+.icon-search-span i{
+ line-height: 10px !important;
+}
+
+.search-res-dialog{
+ position: fixed;
+ background: white;
+ box-shadow: rgba(0, 0, 0, 0.247059) 0px 5px 6px 0px;
+ padding: 20px;
+ top: 90px;
+ right: 10px;
+}
+
+.search-popup-content{
+ line-height: normal; right: 167px; min-height: 122px; height: auto; width: auto;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/sidebar/sidebar.controller.js b/ecomp-portal-FE-common/client/app/views/sidebar/sidebar.controller.js
new file mode 100644
index 00000000..6d0f2d1e
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/sidebar/sidebar.controller.js
@@ -0,0 +1,63 @@
+/*-
+ * ================================================================================
+ * 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(applicationsService,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);
+
+ //note: this model should be retrieved from BE via sidebar specific service
+ userProfileService.getUserProfile()
+ .then(profile=> {
+
+ if (profile.roles.indexOf('System Administrator') > -1) {
+ $rootScope.isAdminPortalAdmin = true;
+ } else {
+ this.$log.debug('SidebarCtrl::getUserProfile: user is not superAdmin nor admin');
+ }
+ });
+ // $log.debug('SidebarCtrl::getUserProfile: profile.roles.indexOf(superAdmin) = ' + profile.roles.indexOf('superAdmin'));
+ // $log.debug('SidebarCtrl::getUserProfile: profile.roles.indexOf(admin) = ' + profile.roles.indexOf('admin'));
+ applicationsService
+ .getLeftMenuItems()
+ .then(res=>
+ {
+ //console.log("Menu items is "+JSON.stringify(res));
+ this.sidebarModel = res;
+ }).catch(err => {
+ //confirmBoxService.showInformation('There was a problem creating the menu. ' +
+ // 'Please try again later. Error Status: '+ err.status).then(isConfirmed => {});
+ $log.error('SidebarCtrl::getUserProfile: User Profile error occurred: ' + err);
+ });
+
+ }
+ }
+ SidebarCtrl.$inject = ['applicationsService','userProfileService', '$log', '$rootScope'];
+ angular.module('ecompApp').controller('SidebarCtrl', SidebarCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/sidebar/sidebar.less b/ecomp-portal-FE-common/client/app/views/sidebar/sidebar.less
new file mode 100644
index 00000000..f86b5afd
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/sidebar/sidebar.tpl.html b/ecomp-portal-FE-common/client/app/views/sidebar/sidebar.tpl.html
new file mode 100644
index 00000000..95492ed1
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js
new file mode 100644
index 00000000..8413382e
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.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 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;
+ });
+
+ }
+
+ $scope.$watch('newContactUs.app.value', (newVal, oldVal) => {
+ for(var i=0;i<$scope.contactUsAllAppList.length;i++){
+ if($scope.contactUsAllAppList[i].value==newVal){
+ $scope.newContactUs.app=angular.copy($scope.contactUsAllAppList[i]);;
+ }
+ }
+ });
+ /*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-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less
new file mode 100644
index 00000000..905f9faa
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less
@@ -0,0 +1,152 @@
+.searchLiHeader {
+font-weight: bold;
+color: #0574ac;
+font-size: 16px;
+padding-bottom: 10px;
+line-height: 1.5;
+font-family: Omnes-ECOMP-W02,Arial;
+
+}
+
+.searchLiItems{
+cursor: pointer;
+font-weight: normal;
+font-size: 12px;
+color: #444444;
+font-family: Omnes-ECOMP-W02,Arial;
+}
+
+.searchUl {
+list-style: none;
+border-bottom: 1px solid #bbb;
+padding-bottom: 20px;
+}
+
+.manage-contactUs-home-title{
+ color: #067ab4;
+ font-family: "Omnes-ECOMP-W02", Arial;;
+ font-size: 24px;
+ margin-left: 78px;
+ width: 1170px;
+ }
+
+.manage-contactUs-home-manageWidgets-title{
+ color: #067ab4;
+ font-family: "Omnes-ECOMP-W02", 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:32px;
+}
+.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-portal-abs-table .contactUs{
+
+ height:300px;
+
+}
+
+.c-ecomp-portal-abs-table
+{
+background-color: transparent;
+}
+.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;
+}
+
+#divider-line-bottom , #divider-line
+{
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ padding-bottom: 11px;
+
+}
diff --git a/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html
new file mode 100644
index 00000000..c619a18a
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html
@@ -0,0 +1,180 @@
+<!--
+ ================================================================================
+ 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-portal-abs-table" style="height: 300px">
+ <table b2b-table id="table-main" table-data="contactUsList"
+ current-page="ignoredCurrentPage">
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="th-users-0" b2b-table-header key="ecomp_function"
+ default-sort="a">App Name</th>
+ <th id="th-users-1" b2b-table-header key="app_name"
+ sortable="true">Contact Name</th>
+ <th id="th-users-2" b2b-table-header key="app_name"
+ sortable="true">Contact Email</th>
+ <th id="th-users-3" b2b-table-header key="role_name"
+ sortable="true">Contact URL</th>
+ <th id="th-users-4" b2b-table-header key="role_name"
+ sortable="true">Description</th>
+ <th id="th-users-5" b2b-table-header key="role_name"
+ sortable="true">Edit</th>
+ <th id="th-users-6" b2b-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 b2b-table-row type="body" class="table-body"
+ track-by="$index" row-repeat="rowData in contactUsList">
+ <tr id="tr-rowData" ng-click="">
+ <td b2b-table-body>
+ <div id="users-page-td-appName" ng-bind="rowData.appName"></div>
+ </td>
+ <td b2b-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 b2b-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 b2b-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 b2b-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 b2b-table-body>
+ <div class="delete-contact-us" ng-hide="rowData.showEdit"
+ ng-click="rowData.showEdit=true">
+ <span class="icon-edit"></span>
+ </div> <span ng-show="rowData.showEdit"> <a
+ btn-type="primary"
+ ng-click="editContactUsFun(rowData); rowData.showEdit=false"
+ class="btn btn-alt btn-small" size="small">Save</a>
+ </span>
+ </td>
+ <td b2b-table-body>
+ <div class="delete-contact-us"
+ ng-click="delContactUsFun(rowData)">
+ <span class="icon-misc-trash"></span>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div id="divider-line"></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 id="add-contact-us-field-appname"
+ class="add-contact-us-field">
+ <div id="mots-property-label" class="property-label">App
+ Name</div>
+ <select id="dropdown1" name="dropdown1" b2b-dropdown
+ placeholder-text="Select an App"
+ ng-model="newContactUs.app.value">
+ <option b2b-dropdown-list
+ option-repeat="d in contactUsAllAppList" value="{{d.value}}">{{d.title}}</option>
+ </select>
+ </div>
+ <br>
+ <div id="add-contact-us-field-contactname"
+ class="add-contact-us-field">
+ <div id="property-label-name" class="property-label">Contact
+ Name</div>
+ <input id="property-input-name" class="input-text-area"
+ type="text" ng-model="newContactUs.name" />
+ </div>
+ <div id="add-contact-us-field-email" class="add-contact-us-field">
+ <div id="property-label-email" class="property-label">Contact
+ Email</div>
+ <input id="property-input-email" class="input-text-area"
+ type="text" ng-model="newContactUs.email" />
+ </div>
+ <div id="add-contact-us-field-url" class="add-contact-us-field">
+ <div id="property-label-url" class="property-label">Contact
+ URL</div>
+ <input id="property-input-url" class="input-text-area"
+ type="text" ng-model="newContactUs.url" />
+ </div>
+ <div id="add-contact-us-field-desc"
+ class="add-contact-us-field-des">
+ <div id="property-label-desc" class="property-label">Description</div>
+ <textarea id="property-input-desc"
+ 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"
+ class="btn btn-alt btn-small" ng-click="newContactUsFun()">Add
+ New</a>
+ </div>
+ <div id="divider-line-bottom"></div>
+
+ <div style="height: 50px;">
+ <a style="float: right; margin-right: -230px !important;"
+ class="btn btn-alt btn-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>
diff --git a/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.controller.js b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.controller.js
new file mode 100644
index 00000000..0d39a653
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.controller.js
@@ -0,0 +1,171 @@
+/*-
+ * ================================================================================
+ * 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,$anchorScroll,$location) {
+
+ 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;
+ }
+ }
+ console.log(offSetHeight);
+ 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;
+ $location.hash('appId'+index);
+ $anchorScroll();
+ /* $('#contentId').animate({
+ scrollTop: targetDiv.offsetTop-offSetHeight
+ }, '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','$anchorScroll','$location'];
+ angular.module('ecompApp').controller('ContactUsCtrl', ContactUsCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.controller.spec.js b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.controller.spec.js
new file mode 100644
index 00000000..3841a2b3
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/support/contact-us/contact-us.less b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.less
new file mode 100644
index 00000000..6fd0a420
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.less
@@ -0,0 +1,93 @@
+.w-ecomp-contactUs-home {
+ //.bg_portalWhite;//white for 1702
+ .bg_portalGray; // 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;
+ padding-left: @padding-left-side;
+ background-color: white;
+
+ .w-ecomp-main-view-title{
+ font-family: Omnes-ECOMP-W02,Arial;
+ }
+
+ #title{
+ font-family: Omnes-ECOMP-W02-Light,Arial;
+ }
+
+ #edit-button-contact-us{
+ float: right;
+ }
+
+ .contactUs-home-container {
+ .content_justify;
+ position: relative;
+ //padding-top: 15px;
+ padding-right: 0;
+ padding-left: 0;
+ padding-bottom: 32px;
+ width: 100%;
+
+
+ .contactUs-general-div {
+ margin: auto !important;
+ 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: Omnes-ECOMP-W02-Medium,Arial;
+ margin:10px;
+ }
+
+.contactUs-panel-header {
+ font-weight: normal;
+ color: #666666;
+ font-size: 18px;
+ font-family: Omnes-ECOMP-W02-Medium,Arial;
+ border-top: 1px solid #ccc;
+ outline : none;
+ height: 35px;
+
+ }
+
+ .contactUs-panel-header:hover{
+ color: #428bca;
+ background: #eae8e8;
+ }
+
+.contactUs-panel-labels {
+ font-weight: normal;
+ color: #666666;
+ font-size: 15px;
+ font-family: Omnes-ECOMP-W02-Medium,Arial;
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
+
+}
diff --git a/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.tpl.html b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.tpl.html
new file mode 100644
index 00000000..9e96b441
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/support/contact-us/contact-us.tpl.html
@@ -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.
+ ================================================================================
+ -->
+<div class="w-ecomp-contactUs-home" style=" overflow:auto;''">
+ <div class="contactUs-home-container" id="page-content">
+ <div class="admins-page-main">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page">Contact Us</h1>
+ <button id="edit-button-contact-us" ng-if="isAdminPortalAdmin == true" class="btn btn-alt btn-small" ng-click="contact.editContactUsModalPopup()"><i class="icon-people-userbookmark" aria-hidden="true"></i>&nbsp;Edit Contact Us</button>
+
+ <div ng-include src="'app/views/support/contact-us/contact-us.aux.html'"></div>
+
+ <br/>
+ <!-- Function Categories -->
+ <div id="appFunctionInfo" style="font-size: 20px;" class="w-ecomp-main-view-title"> Application Functions
+ <div b2b-table table-data="contact.functionalTableData" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="th-functionalItem-0" b2b-table-header sortable="false">Category</th>
+ <th id="th-functionalItem-1" b2b-table-header sortable="false">ECOMP Functions</th>
+ <th id="th-functionalItem-2" b2b-table-header sortable="false">ECOMP Application</th>
+ <!-- <th id="th-functionalItem-3" b2b-table-header >Contact</th> -->
+ </tr>
+ </thead>
+ <!-- Use track-by="UNIQUE KEY HERE" or leave out if no unique keys in data -->
+ <tbody b2b-table-row type="body"
+ type="body"
+ class="table-body"
+ track-by="$index"
+ row-repeat="rowData in contact.functionalTableData">
+ <tr id="tr-rowData" ng-click="">
+ <td b2b-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 b2b-table-body>
+ <div id="functional-item-td-functions" ng-bind="rowData.functions"></div>
+ </td>
+ <td b2b-table-body>
+ <a ng-click="contact.goToSection(rowData.app_Id, $index);">{{rowData.app_Name}}</a>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <br/>
+ <!-- Display applicationInfo panels -->
+ <div id="allAppInfo">
+ <div style="font-size: 20px;" class="w-ecomp-main-view-title"> All Applications
+ </div>
+ <div class="contactUs-general-div">
+ <div class="contact-us-table">
+ <div style="height:auto;" class="c-ecomp-portal-abs-table default">
+ <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 id="appId{{$index}}" ng-bind="rowData.app_name" style="position:relative; top:20px"></span>
+
+ <span style="float: right; margin-right: 30px; margin-top:20px;">
+ <img ng-hide="rowData.showFlag" src="assets/images/chevron_down.png"
+ alt="Expand app contact section" title="Expand app contact section" />
+ <img ng-show="rowData.showFlag" src="assets/images/chevron_up.png"
+ alt="App contact section" title="Collapse app contact section" />
+ </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 ECOMP 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>
+
+ </div>
+</div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/support/get-access/get-access.controller.js b/ecomp-portal-FE-common/client/app/views/support/get-access/get-access.controller.js
new file mode 100644
index 00000000..74b5ba4e
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/support/get-access/get-access.controller.js
@@ -0,0 +1,123 @@
+/*-
+ * ================================================================================
+ * 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, $scope, $stateParams, getAccessService, userProfileService, ExternalRequestAccessService, applicationsService, ngDialog) {
+ // $log.debug('GetAccessCtrl: appService param is: ' + applicationsService.goGetAccessAppName);
+ var resultAccessValue = null;
+
+ $scope.openAppRoleModal = (itemData) => {
+ if(resultAccessValue){
+ let data = null;
+ data = {
+ dialogState: 2,
+ selectedUser:{
+ attuid: $scope.attuid,
+ firstName: $scope.firstName,
+ lastName: $scope.lastName,
+ headerText: itemData.app_name
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/catalog/add-catalog-dialogs/new-catalog.modal.html',
+ controller: 'NewCatalogModalCtrl',
+ controllerAs: 'userInfo',
+ data: data
+ });
+ }
+ }
+
+ userProfileService.getUserProfile().then(
+ function(profile) {
+ $scope.attuid = profile.orgUserId;
+ $scope.firstName = profile.firstName;
+ $scope.lastName = profile.lastName;
+ });
+
+ this.updateAppsList = () => {
+ ExternalRequestAccessService.getExternalRequestAccessServiceInfo().then(
+ function(property) {
+ resultAccessValue = property.accessValue;
+ }).catch(err => {
+ $log.error('GetAccessCtrl: failed getExternalRequestAccessServiceInfo: ' + JSON.Stringify(err));
+ });
+ 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 checkEcompFuncAvail = 'Ecomp Function Not Available' ;
+ var reqStatus = 'Pending';
+ dataArr.ecompFunction = (dataArr.ecompFunction === null) ? checkEcompFuncAvail : dataArr.ecompFunction;
+ dataArr.reqType = (dataArr.reqType === 'P') ? reqStatus : dataArr.reqType;
+ var dataTemp={
+ ecomp_function: dataArr.ecompFunction,
+ app_name:dataArr.appName,
+ role_name:dataArr.roleName,
+ current_role:dataArr.roleActive,
+ request_type:dataArr.reqType
+ }
+ tableData.push(dataTemp);
+ }
+ this.appTable=tableData;
+ if(tableData!=null){
+ var len = tableData.length;
+ this.totalPage = Math.ceil(len/this.viewPerPage);
+ }
+ 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;
+ });
+ };
+
+ this.updateTable = (num) => {
+ this.startIndex=this.viewPerPage*(num-1);
+ this.currentPage = num;
+ };
+ let init = () => {
+ // $log.info('GetAccessCtrl:: initializing...');
+ this.searchString = '';
+ this.getAccessTableHeaders = ['ECOMP Function', 'Application Name', 'Role Name', 'Current Role', 'Request Status'];
+ this.appTable=[];
+ this.updateAppsList();
+ this.viewPerPage=20;
+ this.startIndex=0;
+ this.currentPage = 1;
+ this.totalPage=0;
+ };
+ init();
+ }
+ }
+ GetAccessCtrl.$inject = ['$log', '$scope', '$stateParams', 'getAccessService', 'userProfileService', 'ExternalRequestAccessService','applicationsService', 'ngDialog'];
+ angular.module('ecompApp').controller('GetAccessCtrl', GetAccessCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/support/get-access/get-access.controller.spec.js b/ecomp-portal-FE-common/client/app/views/support/get-access/get-access.controller.spec.js
new file mode 100644
index 00000000..3841a2b3
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/support/get-access/get-access.less b/ecomp-portal-FE-common/client/app/views/support/get-access/get-access.less
new file mode 100644
index 00000000..7ac0d180
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/support/get-access/get-access.less
@@ -0,0 +1,55 @@
+.w-ecomp-get-access-home {
+ //.bg_portalWhite;//white for 1702
+ .bg_portalGray; // 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;
+ background-color: white;
+
+ .w-ecomp-main-view-title{
+ font-family: Omnes-ECOMP-W02-Light,Arial;
+ }
+
+ .get-access-home-container {
+ position: relative;
+ padding-right: 0;
+ padding-left: 0;
+ padding-bottom: @container-bottom;
+
+ .get-access-general-div {
+ width: @table-width;
+ //margin-left: @table-margin-left;
+ margin:auto;
+ }
+
+ .get-access-table {
+ margin-top: 15px;
+ width: @table-width;
+ //margin-left: @table-margin-left;
+ margin:0 auto;
+ .table-control {
+ .table-dropdown-filter {
+ width: @table-dropdown-filter-width;
+ display: @table-dropdown-filter-display;
+ }
+ }
+
+ .table-body {
+ cursor: pointer;
+ }
+ }
+ }
+}
+
+table th, table td {
+ padding: 15px 14px 10px;
+}
+
+#access-page-currentRole{
+ text-align: center;
+}
diff --git a/ecomp-portal-FE-common/client/app/views/support/get-access/get-access.tpl.html b/ecomp-portal-FE-common/client/app/views/support/get-access/get-access.tpl.html
new file mode 100644
index 00000000..7f6770ea
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/support/get-access/get-access.tpl.html
@@ -0,0 +1,102 @@
+<!--
+ ================================================================================
+ 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" id="page-content">
+ <div class="admins-page-main">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page">Get Access</h1>
+ </div>
+ <div ng-include
+ src="'app/views/support/get-access/get-accessinfo.html'"></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 b2b-table table-data="access.appTable"
+ ng-hide="access.isLoadingTable"
+ search-string="access.searchString"
+ class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="th-access-0" b2b-table-header key="ecomp_function"
+ sortable="false" >{{access.getAccessTableHeaders[0]}}</th>
+ <th id="th-access-1" b2b-table-header key="app_name"
+ sortable="false">{{access.getAccessTableHeaders[1]}}</th>
+ <th id="th-access-2" b2b-table-header key="role_name"
+ sortable="false">{{access.getAccessTableHeaders[2]}}</th>
+ <th id="th-access-3" b2b-table-header key="current_role"
+ sortable="false">{{access.getAccessTableHeaders[3]}}</th>
+ <th id="th-access-4" b2b-table-header key="request_access"
+ sortable="false">{{access.getAccessTableHeaders[4]}}</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body"
+ row-repeat="rowData in access.appTable | limitTo:access.viewPerPage:access.startIndex | orderBy:'ecomp_function'" track-by="$index">
+ <tr id="tr-rowData" ng-click="openAppRoleModal(rowData)">
+ <td b2b-table-body>
+ <div id="access-page-function"
+ ng-if="rowData.ecomp_function !== 'Ecomp Function Not Available'"
+ ng-show="$index == 0 || access.appTable[$index-1].ecomp_function != rowData.ecomp_function"
+ ng-bind="rowData.ecomp_function"></div>
+ <div id="access-page-function"
+ ng-if="rowData.ecomp_function === 'Ecomp Function Not Available'"
+ ng-bind="rowData.ecomp_function"></div>
+ </td>
+ <td b2b-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 b2b-table-body>
+ <div id="access-page-roleName" ng-bind="rowData.role_name"></div>
+ </td>
+ <td b2b-table-body>
+ <div id="access-page-currentRole"
+ ng-if="rowData.current_role === 'Y'">
+ <i class="icon-included-checkmark"></i>
+ </div>
+ </td>
+ <td b2b-table-body>
+ <div id="access-page-RequestAccess"
+ ng-if="rowData.request_type !== null"
+ ng-bind="rowData.request_type"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div b2b-pagination="" input-id="goto-page-1" total-pages="access.totalPage" current-page="access.currentPage" click-handler="access.updateTable" role="navigation" aria-label="Customer Data Pages"></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-common/client/app/views/tabs/tabframe.html b/ecomp-portal-FE-common/client/app/views/tabs/tabframe.html
new file mode 100644
index 00000000..0155592a
--- /dev/null
+++ b/ecomp-portal-FE-common/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> \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/tabs/tabs.controller.js b/ecomp-portal-FE-common/client/app/views/tabs/tabs.controller.js
new file mode 100644
index 00000000..5a5b7e49
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/tabs/tabs.controller.js
@@ -0,0 +1,314 @@
+/*-
+ * ================================================================================
+ * 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, userbarUpdateService, $scope,$cookies,$rootScope,confirmBoxService,auditLogService) {
+ // Tab counter
+ var counter = 1;
+ var tabLimit = 6;
+ this.conf = conf;
+ var cookieDomain = this.conf.cookieDomain;
+ // Array to store the tabs
+ $scope.tabs = [];
+ $scope.notificationShow=true;
+ $rootScope.showFooter = "";
+ $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("to");
+ } 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;
+ }
+ }
+ }
+ // Add tab to the end of the array
+ var addTab = function (title, content) {
+ if($scope.tabs.length===tabLimit){
+ //alert
+ confirmBoxService.showInformation('You have reached your maximum limit of tabs allowed.').then(isConfirmed => {});
+ } else {
+ // console.log(window.performance.memory);
+ // var usedperc = (window.performance.memory.usedJSHeapSize/window.performance.memory.jsHeapSizeLimit)*100;
+ // console.log('Current memory usage: '+usedperc+'%');
+ 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.');
+ //confirmBoxService.showInformation('The application URL you are trying to open is not HTTPS. We recommend to use secured HTTPS URL while on-boarding the application.').then(isConfirmed => {});
+ }
+
+ $scope.tabs.push({ title: title, content: content });
+ counter++;
+ //$scope.tabs[$scope.tabs.length - 1].disabled = false;
+ $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 : '/'});
+ }
+ };
+
+ //with APP ID
+ var addTab = function (title, content, appId) {
+ if($scope.tabs.length===tabLimit){
+ //alert
+ 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, appId:appId });
+ 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 : '/'});
+ }
+ };
+
+ // adjust title - trim the title and append ...
+ 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);
+
+
+ };
+
+ //store audit log
+ $scope.auditLog = function(app) {
+ $log.debug('auditLog::auditLog: auditLog.ping() = ' + app);
+ var comment = '';
+ if(app.content==null || app.content=='')
+ comment= app.title;
+ else
+ comment = app.content;
+ auditLogService.storeAudit(app.appId, 'tab', comment);
+ };
+
+ // Remove tab by index
+ var removeTab = function (event, index) {
+ event.preventDefault();
+ event.stopPropagation();
+ $scope.tabs.splice(index, 1);
+ $cookies.putObject('cookieTabs', $scope.tabs,{domain: cookieDomain, path : '/'});
+ };
+
+ //adjust height of the tab due to the search popup being hidden
+ $scope.adjustTabStyle = function(title){
+ if(title=='Home'){
+ $(".w-ecomp-tabs").css('height',"50px");
+ }else{
+ $(".w-ecomp-tabs").css('height',"100%");
+ }
+ }
+
+ // select tab
+ var selectTab = function (title) {
+ $scope.adjustTabStyle(title);
+ if(title==='Home') {
+ $rootScope.ContentModel.IsVisible=true;
+ $rootScope.showFooter = true;
+ $rootScope.tabBottom = 75;
+ userbarUpdateService.setRefreshCount(userbarUpdateService.maxCount);
+ }
+ else {
+ $rootScope.ContentModel.IsVisible=false;
+ $rootScope.showFooter = false;
+ $rootScope.tabBottom = 0;
+ }
+ };
+
+ // Initialize the scope functions
+ $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));
+ // For demonstration add 5 tabs
+
+ 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,1) ;
+ }else{
+ addTab( cookieTabs[t].title, cookieTabs[t].content,cookieTabs[t].appId) ;
+ }
+
+ }
+ } else {
+ for (var i = 0; i < 1; i++) {
+ var content="";
+ var title="";
+ var appId=""
+ if(i===0){
+ title="Home";
+ $rootScope.ContentModel.IsVisible=true;
+ addTab(title, content,1);
+ }else{
+ addTab(title, content,appId);
+ }
+ }
+ }
+
+
+
+ //$scope.tabs[$scope.tabs.length - 1].active = false;
+ //$scope.tabs[0].disabled = false;
+ $scope.selectedIndex = 0;
+ });
+
+ $scope.$watchCollection(function() { return $cookies.getObject('addTab'); }, function(newValue) {
+ // $log.log('Cookie string: ' + $cookies.get('test'))
+ 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.tabs[x].disabled = false;
+ $scope.selectedIndex = x;
+ // added dummy variable to get iframe reloded if tab is already opened.
+ if(tabContent.url.indexOf('?')===-1){
+ $scope.tabs[x].content=tabContent.url+'?dummyVar='+(new Date()).getTime();
+ }
+ else{
+ $scope.tabs[x].content=tabContent.url+'&dummyVar='+(new Date()).getTime();
+ }
+ }
+ }
+ if(!tabExists){
+ addTab( tabContent.title, tabContent.url,tabContent.appId) ;
+ }
+ $cookies.remove('addTab');
+ }
+ });
+ }
+ }
+ TabsCtrl.$inject = ['applicationsService', '$log', '$window', 'conf', 'userProfileService', 'userbarUpdateService', '$scope','$cookies','$rootScope','confirmBoxService','auditLogService'];
+ 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) {
+
+ //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').directive('tabHighlight', [function () {
+ return {
+ restrict: 'A',
+ link: function (scope, element) {
+ // Here is the major jQuery usage where we add the event
+ // listeners mousemove and mouseout on the tabs to initalize
+ // the moving highlight for the inactive tabs
+ var x, y, initial_background = '#c3d5e6';
+
+ element
+ .removeAttr('style')
+ .mousemove(function (e) {
+ // Add highlight effect on inactive tabs
+ if(!element.hasClass('md-active'))
+ {
+ x = e.pageX - this.offsetLeft;
+ y = e.pageY - this.offsetTop;
+
+ // Set the background when mouse moves over inactive tabs
+ 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 () {
+ // Return the inital background color of the tab
+ 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) {
+ // alert(ref.id);
+ if (self != top) {
+ var e = document.body;
+ e.parentNode.removeChild(e);
+ window.location = "unKnownError";
+ }
+}
diff --git a/ecomp-portal-FE-common/client/app/views/tabs/tabs.controller.spec.js b/ecomp-portal-FE-common/client/app/views/tabs/tabs.controller.spec.js
new file mode 100644
index 00000000..b9fd0acc
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/tabs/tabs.controller.spec.js
@@ -0,0 +1,81 @@
+/*-
+ * ================================================================================
+ * 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'));
+
+ //destroy $http default cache before starting to prevent the error 'default cache already exists'
+ 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');
+ });
+
+
+}); \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/tabs/tabs.less b/ecomp-portal-FE-common/client/app/views/tabs/tabs.less
new file mode 100644
index 00000000..9f90d610
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/tabs/tabs.less
@@ -0,0 +1,660 @@
+.w-ecomp-tabs {
+ //.bg_portalWhite;//white for 1702
+ .bg_portalGray; // gray for 1610
+
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: @page-main-bottom;
+ overflow-y: hidden;
+ top: @header-height;
+ padding-left: 0;
+ height:50px;
+ z-index:101;
+ .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;
+ .dBlue24r; // AT&T Dark Blue
+ 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: @portalWhite;
+ }
+
+ .app-gridster-footer {
+ background-color: @portalWhite;
+ }
+
+ .portals-list-item {
+ background-color: @portalWhite;
+ 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: @portalWhite;
+ height: 120px;
+ top: 180px;
+ position: relative;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ padding: 16px;
+
+ .info-title {
+ //.a24r;
+ .dBlue24r; // AT&T Dark Blue
+ margin-bottom: 4px;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ .info-description {
+ .portalDBlue16r;
+ 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-disclaimer {
+ text-align: center;
+ .dGray14r;
+ bottom: -37px;
+ width: 100%;
+ line-height: 1.5em;
+
+}
+//.build-number {
+// .o12i;
+//}
+
+@keyframes fadein {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+
+
+.tab-container {
+ background: @portalLGray;
+ 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 @funcBkgGray;
+}
+.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 @portalGray;
+}
+.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: @portalBlack;
+ 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: @portalDGray;
+ 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: @portalLPurple;
+ color: @portalWhite;
+ -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%, @portalWhite), color-stop(100%, @funcBkgGray));
+ background-image: -moz-linear-gradient(@portalWhite, @funcBkgGray 30px);
+ background-image: -webkit-linear-gradient(@portalWhite, @funcBkgGray 30px);
+ background-image: linear-gradient(@portalWhite, @funcBkgGray 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: @portalLBlue;
+ 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: @portalLGray;
+ 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;
+ // position: absolute;
+ //top: -999px;
+
+ &: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: 126px;
+ top:-70px;
+ @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
+ top:-70px;
+ }
+}
+
+#tab-cross-sign-icon{
+ margin: 48px 0px 0px 0px;
+ width: 16.1px;
+ border-bottom-width:0px;
+ padding: 6px 6px 6px 3px;
+}
diff --git a/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.Json.details.controller.js b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.Json.details.controller.js
new file mode 100644
index 00000000..dd11c60d
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.Json.details.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 userNotificationCtrl {
+ constructor($scope, message, ngDialog) {
+ $scope.messageData=message.text;
+ //alert("message.text" + $scope.messageData);
+ }
+ }
+ userNotificationCtrl.$inject = ['$scope', 'message', 'ngDialog'];
+ angular.module('ecompApp').controller('userNotificationCtrl', userNotificationCtrl);
+})();
+
+
+
+
diff --git a/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.Json.details.modal.page.html b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.Json.details.modal.page.html
new file mode 100644
index 00000000..2c22751b
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.Json.details.modal.page.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-user-json-notification-details-admin"
+ ng-style="{bottom: tabBottom}">
+ <div class="w-ecomp-main-container" >
+ <div class="user-notification" >
+ <div id="'widgets-details-title" class="w-ecomp-main-json-view-title"> Message Notification Details </div>
+
+
+<div class="notifications-properties-main">
+
+ <div ng-bind-html="messageData"></div>
+ </div>
+
+ <div class="dialog-control-close">
+
+ <button id="div-cancel-button" class="btn btn-alt btn-small" ng-click="closeThisDialog()">Close</button>
+ </div>
+
+ </div>
+
+</div>
+</div>
+
+<script type="application/javascript">
+ $(document).ready(function(){
+ $(".ngdialog-content").css("width","40%")
+ $(".ngdialog-content").css("height","450px")
+
+ });
+</script>
diff --git a/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.controller.js b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.controller.js
new file mode 100644
index 00000000..51c7bb7c
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.controller.js
@@ -0,0 +1,196 @@
+/*-
+ * ================================================================================
+ * 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 userNotificationsCtrl {
+ constructor($scope, $log, notificationService, confirmBoxService, $modal, ngDialog, $state) {
+
+ var priorityItems={"1":"Normal","2":"Important"};
+ $scope.priorityItems=priorityItems;
+ $scope.searchString='';
+ $scope.externalNotification="External System";
+ $scope.itemExpired={"background-color":"silver "};
+ $scope.showInput = true;
+ $scope.totalPages1 = 0;
+ $scope.viewPerPage1 = 15;
+ $scope.currentPage1 = 1;
+ $scope.showLoader = false;
+ $scope.firstPlay = true;
+ // Start with empty list to silence error in console
+ $scope.tableData = [];
+ $scope.tableAdminNotifItems = [];
+ let getAdminNotifications = () => {
+ $scope.isLoadingTable = true;
+ notificationService.getAdminNotification().then(res => {
+ $scope.adminNotifications = res.data;
+ $scope.isLoadingTable = false;
+ $scope.tableData = res.data;
+ var totalItems = $scope.tableData.length;
+ $scope.totalPages1 = Math.ceil(totalItems / $scope.viewPerPage1);
+ $scope.showLoader = false;
+ $scope.currentPage1=1;
+ var endIndex = 1 * $scope.viewPerPage1;
+ var startIndex = endIndex - $scope.viewPerPage1;
+ $scope.tableAdminNotifItems = $scope.tableData.slice(startIndex, endIndex);
+ }).catch(err => {
+ $log.error('userNotificationsCtrl:getAdminNotifications:: error ', err);
+ $scope.isLoadingTable = false;
+ });
+ }
+
+ getAdminNotifications();
+
+ $scope.customPageHandler = function(num) {
+ $scope.currentPage1=num;
+ var endIndex = num * $scope.viewPerPage1;
+ var startIndex = endIndex - $scope.viewPerPage1;
+ $scope.tableAdminNotifItems = $scope.tableData.slice(startIndex, endIndex);
+ };
+
+
+
+
+
+ $scope.removeUserNotification = function (selectedAdminNotification) {
+ selectedAdminNotification.activeYn = 'N';
+ confirmBoxService.deleteItem(selectedAdminNotification.msgHeader)
+ .then(isConfirmed => {
+ if (isConfirmed) {
+ notificationService.updateAdminNotification(selectedAdminNotification)
+ .then(() => {
+ getAdminNotifications();
+ }).catch(err => {
+ switch (err.status) {
+ case '409': // Conflict
+ // handleConflictErrors(err);
+ break;
+ case '500': // Internal Server
+ // Error
+ confirmBoxService.showInformation('There was a problem updating the notification. ' +
+ 'Please try again later. Error: ' + err.status).then(isConfirmed => { });
+ break;
+ case '403': // Forbidden...
+ // possible
+ // webjunction error
+ // to try again
+ confirmBoxService.showInformation('There was a problem updating the notification. ' +
+ 'Please try again. If the problem persists, then try again later. Error: ' + err.status).then(isConfirmed => { });
+ break;
+ default:
+ confirmBoxService.showInformation('There was a problem updating the notification. ' +
+ 'Please try again. If the problem persists, then try again later. Error: ' + err.status).then(isConfirmed => { });
+ }
+ $log.error('UserNotifCtlr::updateAdminNOtif failed: ' + JSON.stringify(err));
+ }).finally(() => {
+ var objOffsetVersion = objAgent.indexOf("MSIE");
+ if (objOffsetVersion != -1) {
+ $window.location.reload();
+ }
+ });
+ }
+ }).catch(err => {
+ $log.error('UserNotifCtlr::deleteItem error: ' + err);
+ });
+ }
+
+
+
+ $scope.showDetailedJsonMessage=function (selectedAdminNotification) {
+ notificationService.getMessageRecipients(selectedAdminNotification.notificationId).then(res =>{
+ $scope.messageRecipients = res;
+ var messageObject=JSON.parse(selectedAdminNotification.msgDescription);
+ var html="";
+ html+='<p>'+'Message Source'+' : '+selectedAdminNotification.msgSource+'</p>';
+ html+='<p>'+'Message Title'+' : '+selectedAdminNotification.msgHeader+'</p>';
+ html+='<p>'+'Message Recipient'+' : '+$scope.messageRecipients+'</p>';
+
+ for(var field in messageObject){
+ if(field=='eventDate'||field=='lastModifiedDate'){
+ html+='<p>'+field+' : '+new Date(+messageObject[field])+'</p>';
+
+ }else{
+ html+='<p>'+field+' : '+messageObject[field]+'</p>';
+
+ }
+ }
+
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/user-notifications-admin/user.notifications.Json.details.modal.page.html',
+ controller: 'userNotificationCtrl',
+ resolve: {
+ message: function () {
+ var message = {
+ title: '',
+ text: html
+
+ };
+ return message;
+ },
+
+ }
+ });
+
+ }).catch(err => {
+ $log.error('userNotificationsCtrl:getMessageRecipients:: error ', err);
+ $scope.isLoadingTable = false;
+ });
+
+ };
+
+
+ $scope.editUserNotificationModal = function (selectedAdminNotification) {
+
+ // retrieve roleIds here
+ selectedAdminNotification.roleIds = null;
+ notificationService.getNotificationRoles(selectedAdminNotification.notificationId)
+ .then(res => {
+ selectedAdminNotification.roleIds = res.data;
+
+ $scope.openUserNotificationModal(selectedAdminNotification);
+ }).catch(err => {
+ $log.error('UserNotifCtlr:getNotificationRoles:: error ', err);
+
+ });
+ }
+
+ $scope.openUserNotificationModal = function (selectedAdminNotification) {
+ let data = null;
+ if (selectedAdminNotification) {
+ data = {
+ notif: selectedAdminNotification
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/user-notifications-admin/user.notifications.modal.page.html',
+ controller: 'userNotificationsModalCtrl',
+ controllerAs: 'userNotifModal',
+ data: data
+ }).closePromise.then(function (needUpdate) {
+ getAdminNotifications();
+ });
+ }
+
+ }
+ }
+ userNotificationsCtrl.$inject = ['$scope', '$log', 'notificationService', 'confirmBoxService', '$modal', 'ngDialog', '$state'];
+ angular.module('ecompApp').controller('userNotificationsCtrl', userNotificationsCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.json.details.modal.page.less b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.json.details.modal.page.less
new file mode 100644
index 00000000..ebf48075
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.json.details.modal.page.less
@@ -0,0 +1,48 @@
+
+.w-ecomp-user-json-notification-details-admin {
+ .bg_portalWhite;//white for 1702
+ //.bg_portalGray; // gray for 1610
+ position: relative;
+ top: 20px;
+ left: -200px;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ padding-left: @padding-left-side;
+ height: 345px;
+ width: 96%;
+ margin-left: 220px;
+ .w-ecomp-main-json-view-title {
+ //.n18r;
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @portalDBlue 3px solid;
+ width:100%;
+ }
+
+ .notifications-properties-main{
+ padding-top: 16px;
+ font-size: 14px;
+ overflow-y: scroll;
+ max-height: 285px;
+
+ }
+input:not([type="button"]) {
+ height: 22px; }
+ .widget-properties-main {
+ padding: 16px;
+ height: 460px;
+ overflow-y: auto;
+
+
+
+
+ }
+
+.dialog-control-close {
+ position: absolute;
+ bottom: -60px;
+ right: 16px;
+}
+
+
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.less b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.less
new file mode 100644
index 00000000..ee5f9e4d
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.less
@@ -0,0 +1,128 @@
+.w-ecomp-user-notification-admin{
+ .bg_portalWhite;//white for 1702
+ //.bg_portalGray; // 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;
+
+ .tab-bottom {
+ bottom: 0;
+ }
+
+ .tablesorter-default .tablesorter-header .tablesorter-header-inner {
+ //background-image: url(images/upanddown.png);
+ background-position: center right;
+ background-repeat: no-repeat;
+ cursor: pointer;
+ white-space: normal;
+ display: inline-block;
+ vertical-align: baseline;
+ zoom: 1;
+ padding: -1px 50px;
+}
+
+
+p {
+ display: inline;
+}
+.userNotifTable {
+ width: @table-width;
+ //margin-left: @table-margin-left;
+ //margin: @table-margin;
+ margin:auto;
+ .delete-app{
+ .ico_trash_default;
+ }
+ }
+ .user-notification-container {
+ .content_justify;
+ position: relative;
+ padding: 15px 0 32px 0;
+ width: 100%;
+
+ .user-notification-list {
+ min-height: 70vh;
+ //display: flex;
+ justify-content: center;
+ flex-flow: row wrap;
+ width: @table-width;
+ margin: auto;
+ margin-bottom: 63px;
+
+ .app-gridster-header {
+ background-color: @portalWhite;
+
+ }
+
+ .app-gridster-footer {
+ background-color: @portalWhite;
+ }
+
+ .user-notification-list-item {
+ background-color: @portalWhite;
+ 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;
+
+ .notification-item-info {
+ background-color: @portalWhite;
+ height: 120px;
+ top: 180px;
+ position: relative;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ padding: 50px;
+
+ .info-title {
+ //.a24r;
+ .dBlue24r; // AT&T Dark Blue
+ margin-bottom: 4px;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ .info-description {
+ .portalDBlue16r; // omnes 16 regular
+ 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;
+ }
+ }
+ }
+ }
+ }
+
+ .go-button {
+ .btn-green;
+ width: 96px;
+ position: absolute;
+ border-radius: 0px;
+ }
+ .user-notification-add-button {
+ width: 160px;
+ margin-bottom: 11px;
+ }
+
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.controller.js b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.controller.js
new file mode 100644
index 00000000..ebd2f93d
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.controller.js
@@ -0,0 +1,747 @@
+/*-
+ * ================================================================================
+ * 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 userNotificationsModalCtrl {
+ constructor($scope, $log, functionalMenuService, confirmBoxService, notificationService, $modal, ngDialog, $state, $filter) {
+
+ let newNotifModel = {
+ 'isOnlineUsersOnly': null,
+ 'isForAllRolesOptions': null,
+ 'selectedPriority': null,
+ 'isActive': null,
+ 'startTime': null,
+ 'endTime': null,
+ 'msgHeader': null,
+ 'msgDescription': null,
+ 'roleIds': null,
+ 'roleObj': {notificationRoleIds:null}
+ };
+
+ $scope.notificationId = null;
+ $scope.selectedCat = null;
+ $scope.selectedEcompFunc = null;
+ this.YN_index_mapping = {
+ "Y": 0,
+ "N": 1
+ }
+
+ $scope.onlineAllUsersOptions = [
+ { "index": 0, "value": "Y", "title": "Online Users Only" },
+ { "index": 1, "value": "N", "title": "Online & Offline Users" }
+
+ ];
+
+ $scope.isForAllRolesOptions = [
+ { "index": 0, "value": "Y", "title": "Yes" },
+ { "index": 1, "value": "N", "title": "No" }
+ ];
+
+ $scope.priorityOptions = [
+ { "index": 0, "value": 1, "title": "Normal" },
+ { "index": 1, "value": 2, "title": "Important" }
+ ];
+
+ $scope.isActiveOptions = [
+ { "index": 0, "value": "Y", "title": "Yes" },
+ { "index": 1, "value": "N", "title": "No" }
+ ];
+ $scope.isActive = $scope.isActiveOptions[0];
+ $scope.selectPriority = $scope.priorityOptions[0];
+ $scope.isOnlineUsersOnly = $scope.onlineAllUsersOptions[1];
+ $scope.isForAllRoles=$scope.isForAllRolesOptions[0].value;
+ $scope.isFunctionalMenu ="Y";
+
+ $scope.selectedPriority=$scope.priorityOptions[0].value;
+
+ // $scope.notificationRoleIds = [];
+ $scope.msgHeader = '';
+ $scope.msgDescription = '';
+ $scope.treeTitle="Functional Menu";
+ $scope.notifObj= {isCategoriesFunctionalMenu:true};
+
+ let init = () => {
+ // $log.info('userNotificationsModalCtrl::init');
+ this.isSaving = false;
+ var today = new Date();
+ $scope.minDate = today.toISOString().substring(0, 10);
+ var threeMonthsFromNow = new Date();
+ threeMonthsFromNow.setMonth(threeMonthsFromNow.getMonth() + 3);
+ $scope.maxDate = threeMonthsFromNow.toISOString().substring(0, 10);
+ if ($scope.ngDialogData && $scope.ngDialogData.notif) {
+ // $log.debug('userNotificationsModalCtrl:init:: Edit
+ // notification mode for', $scope.ngDialogData.notif);
+ $scope.isEditMode = true;
+ $scope.editModeObj = {isEditMode: true};
+ this.notif = _.clone($scope.ngDialogData.notif);
+ $scope.modalPgTitle = 'View Notification'
+ $scope.isOnlineUsersOnly = $scope.onlineAllUsersOptions[this.YN_index_mapping[this.notif.isForOnlineUsers]];
+ $scope.isForAllRoles = $scope.isForAllRolesOptions[this.YN_index_mapping[this.notif.isForAllRoles]].value;
+ $scope.isActive = $scope.isActiveOptions[this.YN_index_mapping[this.notif.activeYn]];
+ $scope.selectedPriority = $scope.priorityOptions[this.notif.priority - 1].value;
+ $scope.startTime = new Date(this.notif.startTime);
+ $scope.endTime = new Date(this.notif.endTime);
+ $scope.msgHeader = this.notif.msgHeader;
+ $scope.msgDescription = this.notif.msgDescription;
+ $scope.notificationId = this.notif.notificationId;
+ $scope.notificationRoleIds = this.notif.roleIds;
+ $scope.roleObj = {notificationRoleIds:this.notif.roleIds};
+ } else {
+ // $log.debug('AppDetailsModalCtrl:init:: New app mode');
+ $scope.isEditMode = false;
+ $scope.editModeObj = {isEditMode: false};
+ $scope.modalPgTitle = 'Add a New Notification'
+ this.notif = _.clone(newNotifModel);
+ $scope.roleObj = {notificationRoleIds:null};
+ }
+ };
+ this.conflictMessages = {};
+ this.scrollApi = {};
+ let handleConflictErrors = err => {
+ if(!err.data){
+ return;
+ }
+ if(!err.data.length){ // support objects
+ err.data = [err.data]
+ }
+ _.forEach(err.data, item => {
+ _.forEach(item.fields, field => {
+ // set conflict message
+ this.conflictMessages[field.name] = errorMessageByCode[item.errorCode];
+ // set field as invalid
+ $scope.appForm[field.name].$setValidity('conflict', false);
+ // set watch once to clear error after user correction
+ watchOnce[field.name]();
+ });
+ });
+ this.scrollApi.scrollTop();
+ };
+
+ let resetConflict = fieldName => {
+ delete this.conflictMessages[fieldName];
+ if($scope.appForm[fieldName]){
+ $scope.appForm[fieldName].$setValidity('conflict', true);
+ }
+ };
+ $scope.addUserNotificationValidation = function () {
+ // // pre-processing
+ if (!($scope.isEditMode)) {
+ var validation=false;
+
+ if($scope.startTime && $scope.endTime && $scope.msgHeader != '' && $scope.msgDescription != '' && ($scope.startTime<$scope.endTime)){
+ validation=true;
+ if( $scope.isForAllRoles=='N'){
+ validation = $scope.checkBoxObj.isAnyRoleSelected;
+ }
+ }
+ else{
+ validation=false;
+ }
+
+
+ return !validation;
+ }
+ }
+
+ /* format the value for viewing a notification */
+ $scope.formatStartDate = function () {
+ if ($scope.startTime) {
+ $scope.startTime = $filter('date')($scope.startTime, 'medium');
+ }
+ }
+
+ /* format the value for viewing a notification */
+ $scope.formatEndDate = function () {
+ if($scope.endTime){
+ $scope.endTime = $filter('date')($scope.endTime, 'medium');
+ }
+ }
+
+ $scope.addUserNotification = function () {
+ $scope.notificationRoleIds = [];
+ // pre-processing
+ for (var key in $scope.checkboxIdDict) {
+ if ($scope.checkboxIdDict[key].is_box_checked && ($scope.checkboxIdDict[key].role_id != null)) {
+ var role_ids = $scope.checkboxIdDict[key].role_id;
+ for (var i in role_ids) {
+ if (!($scope.notificationRoleIds.indexOf(role_ids[i]) > -1)) {
+ $scope.notificationRoleIds.push(role_ids[i]);
+ }
+ }
+ }
+ }
+
+ $scope.notificationRoleIds.sort();
+ if (($scope.isOnlineUsersOnly) && ($scope.isForAllRoles) && ($scope.selectedPriority) && ($scope.isActive)
+ && ($scope.startTime) && ($scope.endTime) && ($scope.msgHeader != '') && ($scope.msgDescription != '')) {
+ this.newUserNotification =
+ {
+ 'notificationId':$scope.notificationId,
+ 'isForOnlineUsers': $scope.isOnlineUsersOnly.value,
+ 'isForAllRoles': $scope.isForAllRoles,
+ 'priority': $scope.selectedPriority,
+ 'activeYn': $scope.isActive.value,
+ 'startTime': $scope.startTime,
+ 'endTime': $scope.endTime,
+ 'msgHeader': $scope.msgHeader,
+ 'msgDescription': $scope.msgDescription,
+ 'roleIds': $scope.notificationRoleIds,
+ 'createdDate': new Date()
+ };
+
+ // POST ajax call here;
+ if ($scope.isEditMode) {
+ notificationService.updateAdminNotification(this.newUserNotification)
+ .then(() => {
+ //$log.debug('NotificationService:updateAdminNotification:: Admin notification update succeeded!');
+ $scope.closeThisDialog(true);
+ // emptyCookies();
+ }).catch(err => {
+ $log.error('notificationService.updateAdminNotfication failed: ' + JSON.stringify(err));
+ switch (err.status) {
+ case '409': // Conflict
+ // handleConflictErrors(err);
+ break;
+ case '500': // Internal Server Error
+ confirmBoxService.showInformation('There was a problem updating the notification. ' +
+ 'Please try again later. Error: ' + err.status).then(isConfirmed => { });
+ break;
+ case '403': // Forbidden... possible
+ // webjunction error to
+ // try again
+ confirmBoxService.showInformation('There was a problem updating the notification. ' +
+ 'Please try again. If the problem persists, then try again later. Error: ' + err.status).then(isConfirmed => { });
+ break;
+ default:
+ confirmBoxService.showInformation('There was a problem updating the notification. ' +
+ 'Please try again. If the problem persists, then try again later. Error: ' + err.status).then(isConfirmed => { });
+ }
+ }).finally(() => {
+ // for bug in IE 11
+ var objOffsetVersion = objAgent.indexOf("MSIE");
+ if (objOffsetVersion != -1) {
+ $log.debug('AppDetailsModalCtrl:updateOnboardingApp:: Browser is IE, forcing Refresh');
+ $window.location.reload();
+ }
+ // for bug in IE 11
+ });
+
+ } else {
+ notificationService.addAdminNotification(this.newUserNotification)
+ .then((res) => {
+ $log.debug('notificationService:addAdminNotification:: Admin notification creation succeeded!,',res);
+ if(res.status=='ERROR'){
+ confirmBoxService.showInformation('There was a problem adding the notification. ' +
+ ' Error: ' + res.response).then(isConfirmed => { });
+
+
+ }
+ else{
+ $scope.closeThisDialog(true);
+ }
+
+ // emptyCookies();
+ }).catch(err => {
+ switch (err.status) {
+ case '409': // Conflict
+ // handleConflictErrors(err);
+ break;
+ case '500': // Internal Server Error
+ confirmBoxService.showInformation('There was a problem adding the notification. ' +
+ 'Please try again later. Error: ' + err.status).then(isConfirmed => { });
+ break;
+ default:
+ confirmBoxService.showInformation('There was a problem adding the notification. ' +
+ 'Please try again. If the problem persists, then try again later. Error: ' +
+ err.status).then(isConfirmed => { });
+ }
+ $log.error('notificationService:addAdminNotification error:: ' + JSON.stringify(err));
+ })
+ }
+
+
+ } else {
+ $log.warn('please fill in all required fields');
+ confirmBoxService.showInformation('Please fill in all required fields').then(isConfirmed => { });
+ }
+ }
+ // Populate the category list for category dropdown list
+ let getFunctionalMenu = () => {
+ this.isLoadingTable = true;
+ $scope.notifObj= {isCategoriesFunctionalMenu:true};
+ functionalMenuService.getFunctionalMenuRole().then(role_res => {
+ var menu_role_dict = {};
+ for (var i in role_res) {
+ // if first time appear in menu_role_dict
+ if (!(role_res[i].menuId in menu_role_dict)) {
+ menu_role_dict[role_res[i].menuId] = [role_res[i].roleId];
+ } else {
+ menu_role_dict[role_res[i].menuId].push(role_res[i].roleId);
+ }
+ }
+ functionalMenuService.getManagedFunctionalMenuForNotificationTree().then(res => {
+ let actualData = [];
+ var exclude_list = ['Favorites']
+ // Adding children and label attribute to all objects in
+ $scope.checkboxIdDict = {};
+ $scope.checkBoxObj = {isAnyRoleSelected:false};
+ for (let i = 0; i < res.length; i++) {
+ res[i].children = [];
+ res[i].label = res[i].text;
+ res[i].id = res[i].text;
+ // res[i].is_box_checked = false;
+ res[i].can_check = true;
+ res[i].roleId = menu_role_dict[res[i].menuId];
+ $scope.checkboxIdDict[res[i].id] = { 'is_box_checked': false, 'role_id': res[i].roleId };
+ }
+
+ // Adding actual child items to children array in res
+ // objects
+ $scope.parentChildDict ={};
+ $scope.parentChildRoleIdDict ={};
+ for (let i = 0; i < res.length; i++) {
+ let parentId = res[i].menuId;
+ $scope.parentChildDict[parentId] = [];
+ $scope.parentChildRoleIdDict[parentId]=[];
+ for (let j = 0; j < res.length; j++) {
+ let childId = res[j].parentMenuId;
+ if (parentId === childId) {
+ res[i].children.push(res[j]);
+ $scope.parentChildDict[parentId].push(res[j].menuId);
+ //if res[j].roleId is defined
+ if (res[j].roleId) {
+ for (let k in res[j].roleId) {
+ $scope.parentChildRoleIdDict[parentId].push(res[j].roleId[k]);
+ }
+
+ }
+ }
+ }
+ }
+
+ //check if grand children exist
+ for (var key in $scope.parentChildDict){
+ var children = $scope.parentChildDict[key];
+ var isGrandParent = false;
+ if (children.length>0) {
+ for (var i in children) {
+ if ($scope.parentChildDict[children[i]].length>0){
+ isGrandParent = true;
+ break;
+ }
+ }
+ }
+ if (isGrandParent) {
+ for (var i in children) {
+ // if the child has children
+ if ($scope.parentChildDict[children[i]].length>0) {
+ for (var j in $scope.parentChildRoleIdDict[children[i]]) {
+
+ if ($scope.parentChildRoleIdDict[key].indexOf($scope.parentChildRoleIdDict[children[i]][j]) === -1) {
+ $scope.parentChildRoleIdDict[key].push($scope.parentChildRoleIdDict[children[i]][j]);
+ }
+ }
+ } else {
+
+ }
+ }
+ }
+
+ };
+
+
+ var ListMenuIdToRemove = [];
+ //$scope.parentObj = {ListMenuIdToRemove : []};
+ //get the list of menuId that needs to be removed
+ for (let i = 0; i < res.length; i++) {
+ if ((res[i].children.length==0)&&(!res[i].roleId)) {
+ var menuIdToRemove = res[i].menuId;
+ if (ListMenuIdToRemove.indexOf(menuIdToRemove) === -1){
+ ListMenuIdToRemove.push(menuIdToRemove);
+ }
+ }
+ }
+
+ // a scope variable that marks whether each functional menu item should be displayed.
+ $scope.toShowItemDict = {};
+ for (let i = 0; i < res.length; i++) {
+ if (res[i].roleId==null) {
+ if (res[i].children.length==0) {
+ $scope.toShowItemDict[res[i].menuId]=false;
+ } else if(res[i].children.length>0){
+ if($scope.parentChildDict[res[i].menuId].length === _.intersection($scope.parentChildDict[res[i].menuId], ListMenuIdToRemove).length){
+ $scope.toShowItemDict[res[i].menuId]=false;
+ } else {
+ $scope.toShowItemDict[res[i].menuId]=true;
+ }
+ }
+ } else {
+ $scope.toShowItemDict[res[i].menuId]=true;
+ }
+ }
+
+ // Sort the top-level menu items in order based on the
+ // column
+ res.sort(function (a, b) {
+ return a.column - b.column;
+ });
+
+ // Sort all the children in order based on the column
+ for (let i = 0; i < res.length; i++) {
+ res[i].children.sort(function (a, b) {
+ return a.column - b.column;
+ });
+ }
+
+ // Forming actual parent items
+ for (let i = 0; i < res.length; i++) {
+ let parentId = res[i].parentMenuId;
+ if (parentId === null) {
+ actualData.push(res[i]);
+ }
+ }
+
+ // $scope.treedata = actualData;
+ var treedata = actualData[0].children;
+ $scope.treedata = [];
+ for (var i in treedata) {
+ if (!(treedata[i].label.indexOf(exclude_list) > -1)) {
+ $scope.treedata.push(treedata[i])
+ }
+ }
+
+ }).catch(err => {
+ $log.error('FunctionalMenuCtrl:getFunctionalMenu:: error ', err);
+ }).finally(() => {
+ this.isLoadingTable = false;
+ })
+
+ }).catch(err => {
+ $log.error('FunctionalMenuCtrl:getFunctionalMenu:: error ', err);
+ })
+ ;
+ }
+
+
+ let getAppRoleIds = () => {
+ $scope.notifObj= {isCategoriesFunctionalMenu:false};
+ notificationService.getAppRoleIds().then(res => {
+
+ res = res.data;
+ let actualData = [];
+ // var exclude_list = ['Favorites']
+ var app_id_name_list = {};
+ $scope.checkboxIdDict = {};
+ $scope.checkBoxObj = {isAnyRoleSelected:false};
+
+ for (let i = 0; i < res.length; i++) {
+ if (!(res[i].appId in app_id_name_list)) {
+ app_id_name_list[res[i].appId] = res[i].appName;
+ }
+
+ res[i].children = [];
+ res[i].label = res[i].roleName;
+ res[i].id = res[i].roleId;
+ res[i].menuId = res[i].roleId;
+ res[i].parentMenuId = res[i].appId;
+ res[i].can_check = true;
+ res[i].roleId = [res[i].roleId];
+ $scope.checkboxIdDict[res[i].id] = { 'is_box_checked': false, 'role_id': res[i].roleId};
+ }
+
+ for (var app_id in app_id_name_list) {
+ var new_res = {};
+ new_res.children = [];
+ new_res.label = app_id_name_list[app_id];
+ new_res.id = app_id;
+ new_res.menuId = app_id;
+ new_res.parentMenuId = null;
+ new_res.appId = null;
+ new_res.can_check = true;
+ new_res.roleId = null;
+ $scope.checkboxIdDict[new_res.id]= { 'is_box_checked': false, 'role_id': new_res.roleId };
+ res.push(new_res);
+ }
+ $scope.parentChildRoleIdDict ={};
+ //Adding actual child items to children array in res objects
+ for (let i = 0; i < res.length; i++) {
+ let parentId = res[i].menuId;
+ $scope.parentChildRoleIdDict[parentId]=[];
+ for (let j = 0; j < res.length; j++) {
+ let childId = res[j].parentMenuId;
+ if (parentId == childId) {
+ res[i].children.push(res[j]);
+ if (res[j].roleId) {
+ for (let k in res[j].roleId) {
+ $scope.parentChildRoleIdDict[parentId].push(res[j].roleId[k]);
+ }
+
+ }
+ }
+ }
+ }
+ //Forming actual parent items
+ 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;
+ })
+ }
+ $scope.getFunctionalMenu= function() {
+ $scope.treeTitle="Functional Menu";
+ getFunctionalMenu();
+ }
+ $scope.getAppRoleIds = function() {
+ $scope.treeTitle="Applications/Roles";
+ getAppRoleIds();
+ }
+
+ init();
+ getFunctionalMenu();
+
+ }
+
+ }
+
+ userNotificationsModalCtrl.$inject = ['$scope', '$log', 'functionalMenuService', 'confirmBoxService', 'notificationService', '$modal', 'ngDialog', '$state', '$filter'];
+ angular.module('ecompApp').controller('userNotificationsModalCtrl', userNotificationsModalCtrl);
+
+ angular.module('ecompApp').directive('attDatepickerCustom', ['$log', function($log) {
+ return {
+ restrict: 'A',
+ require: 'ngModel',
+ scope: {},
+
+ controller: ['$scope', '$element', '$attrs', '$compile', 'datepickerConfig', 'datepickerService', function($scope, $element, $attrs, $compile, datepickerConfig, datepickerService) {
+ var dateFormatString = angular.isDefined($attrs.dateFormat) ? $scope.$parent.$eval($attrs.dateFormat) : datepickerConfig.dateFormat;
+ var selectedDateMessage = '<div class="sr-focus hidden-spoken" tabindex="-1">the date you selected is {{$parent.current | date : \'' + dateFormatString + '\'}}</div>';
+ $element.removeAttr('att-datepicker-custom');
+ $element.removeAttr('ng-model');
+ $element.attr('ng-value', '$parent.current | date:"EEEE, MMMM d, y"');
+ $element.attr('aria-describedby', 'datepicker');
+
+ $element.attr('maxlength', 10);
+
+ var wrapperElement = angular.element('<div></div>');
+ wrapperElement.attr('datepicker-popup', '');
+ wrapperElement.attr('current', 'current');
+
+ datepickerService.setAttributes($attrs, wrapperElement);
+ datepickerService.bindScope($attrs, $scope);
+
+ wrapperElement.html('');
+ wrapperElement.append($element.prop('outerHTML'));
+ if (navigator.userAgent.match(/MSIE 8/) === null) {
+ wrapperElement.append(selectedDateMessage);
+ }
+ var elm = wrapperElement.prop('outerHTML');
+ elm = $compile(elm)($scope);
+ $element.replaceWith(elm);
+ }],
+ link: function(scope, elem, attr, ctrl) {
+ if (!ctrl) {
+ // do nothing if no ng-model
+ $log.error("ng-model is required.");
+ return;
+ }
+
+ scope.$watch('current', function(value) {
+ ctrl.$setViewValue(value);
+ });
+ ctrl.$render = function() {
+ scope.current = ctrl.$viewValue;
+ };
+
+ }
+ };
+ }]);
+
+ angular.module('ecompApp').directive('jqTreeUserNotif', ['functionalMenuService', '$log', 'confirmBoxService', '$compile', function (functionalMenuService, $log, confirmBoxService, $compile) {
+ return {
+ scope: true,
+ templateUrl: 'jq-tree-tmpl-user-notif.html',
+ link: function (scope, el, attrs) {
+
+ var $jqTree = el.find('#jqTreeUserNotif').tree({
+ data: scope.treedata,
+ autoOpen: scope.editModeObj.isEditMode,
+ dragAndDrop: false,
+ onCreateLi: function (node, $li) {
+ node.is_checked = false;
+ if (node.roleId&&scope.roleObj.notificationRoleIds) {
+ node.is_checked = (node.roleId.length === _.intersection(node.roleId, scope.roleObj.notificationRoleIds).length);
+ }
+ if (typeof node.id =="string"){
+ $li.attr('id', node.id.replace(/\s+/g, '_'));
+ }
+ var isChecked = '';
+ if (node.is_checked) {
+ isChecked = 'checked="checked"';
+ }
+ if (node.can_check) {
+ var toShow = true;
+ if (scope.notifObj.isCategoriesFunctionalMenu) {
+ toShow = scope.toShowItemDict[node.menuId];
+ }
+ var isDisabled = "";
+ if (scope.editModeObj.isEditMode) {
+ isDisabled = " disabled"
+
+ //if node is a parent/grandparent node
+ if (node.children.length>0){
+ //whether to show node first
+ if (_.intersection(scope.parentChildRoleIdDict[node.menuId], scope.roleObj.notificationRoleIds).length) {
+ toShow=true;
+ if (scope.parentChildRoleIdDict[node.menuId].length==_.intersection(scope.parentChildRoleIdDict[node.menuId], scope.roleObj.notificationRoleIds).length) {
+ isChecked = 'checked="checked"';
+ }
+ } else {
+ toShow=false;
+ }
+ }
+ //if node is a child node
+ else {
+ if (node.is_checked) {
+ toShow=true;
+ } else {
+ toShow=false;
+ }
+ }
+
+ }
+
+
+
+ var template = '<input ng-click="thisCheckboxClicked($event)" type="checkbox" class="edit js-node-check" data-node-menu-id="' + node.menuId + '" data-node-id="' + node.id + '" ' + isChecked + ' ng-show="' + toShow + '"' + isDisabled+ '/>'
+
+ var templateEl = angular.element(template);
+ var $jqCheckbox = $compile(templateEl)(scope);
+ if (toShow){
+ $li.find('.jqtree-element').prepend($jqCheckbox);
+ } else {
+ $li.find('.jqtree-element').remove();
+ }
+ }
+ }
+ });
+
+ scope.thisCheckboxClicked = function (e) {
+
+ var nodeId = e.target.attributes[4].value;
+
+
+
+ var sBrowser, sUsrAg = window.navigator.userAgent;
+ //if (sUsrAg.indexOf("Firefox") > -1) {
+
+ if (sUsrAg.indexOf("Trident") > -1) {
+ nodeId = e.target.attributes[5].value;
+ }
+
+// if (sUsrAg.indexOf("MSIE") > 1) {
+// alert("hELLO tHIS IS IE10");
+// nodeId = e.target.attributes[3].value;
+// alert('nodeId 26 of IE 45 : '+nodeId);
+// }
+//
+ var version = navigator.userAgent.match(/Firefox\/(.*)$/);
+
+ if(version && version.length > 1){
+ if(parseInt(version[1]) >= 50){
+ nodeId = e.target.attributes[3].value;
+ } else if(parseInt(version[1]) >= 45){
+
+ nodeId = e.target.attributes[2].value;
+ }
+ }
+ var thisNode = el.find('#jqTreeUserNotif').tree('getNodeById', nodeId);
+ var isChecked = e.target.checked;
+ scope.checkboxIdDict[nodeId]['is_box_checked'] = isChecked;
+
+ thisNode = angular.element(thisNode);
+ if (thisNode[0].hasOwnProperty('children') && thisNode[0].children.length > 0) {
+ var jsNodeCheckList = angular.element(e.target).parent().next().find('.js-node-check')
+ // check/uncheck children items
+ jsNodeCheckList.prop('checked', isChecked);
+
+ for (var i in jsNodeCheckList) {
+ var singlediv = jsNodeCheckList[i];
+ if (typeof singlediv == 'object' & (!singlediv.length)) {
+
+ var tempNodeId = angular.element(singlediv)[0].attributes[4].value;
+
+
+
+ if (sUsrAg.indexOf("Trident") > -1) {
+
+ var tempNodeId = angular.element(singlediv)[0].attributes[5].value;
+
+
+ }
+
+// if (sUsrAg.indexOf("MSIE") > 0) {
+// var tempNodeId = angular.element(singlediv)[0].attributes[3].value;
+// alert('tempNodeId 2 FF 45 : '+tempNodeId);
+// }
+ if(version && version.length > 1){
+ if(parseInt(version[1]) >= 50){
+ tempNodeId = angular.element(singlediv)[0].attributes[3].value;
+ }
+ else if(parseInt(version[1]) >= 45){
+ tempNodeId = angular.element(singlediv)[0].attributes[2].value;
+ }
+ }
+ scope.checkboxIdDict[tempNodeId]['is_box_checked'] = isChecked;
+ }
+ }
+ }
+
+ scope.checkBoxObj.isAnyRoleSelected = false;
+ for (var key in scope.checkboxIdDict) {
+ if (scope.checkboxIdDict[key]['is_box_checked']&&scope.checkboxIdDict[key]['role_id']) {
+ scope.checkBoxObj.isAnyRoleSelected = true;
+ break;
+ }
+ }
+ }
+
+
+
+ scope.$watch('treedata', function (oldValue, newValue) {
+ if (oldValue !== newValue) {
+ $jqTree.tree('loadData', scope.treedata);
+ $jqTree.tree('reload', function () {
+ });
+ }
+ });
+ }
+ };
+ }]);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.less b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.less
new file mode 100644
index 00000000..b712ed26
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.less
@@ -0,0 +1,157 @@
+
+.user-notification-details-modal {
+ width: 595px;
+ margin:auto;
+ background-color:white;
+ .title {
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @blue-active 3px solid;
+
+ }
+ input:not([type="button"]) {
+ height: 13px;
+}
+ .user-notification-details-contents {
+ padding-left: 0px;
+ padding-top: 16px;
+ padding-bottom: 16px;
+ height: 630px;
+ overflow-y: auto;
+ margin-left:10px;
+ padding-right:100px;
+
+ .notif-input-calendar{
+ width: 301px;
+ border-radius: 6px ;
+ border: 1px solid #888;
+ height: 36px;
+ position: relative;
+ z-index: 1;
+ }
+
+.simulateCatGridHeaderRadioButton{
+ line-height: 20px;
+ margin-top: 5px;
+ margin-left: 10px;
+ font-family: Omnes-ECOMP-W02, Arial;
+ color: #444444;
+ float: left;
+ font-size: 13px;
+ margin-left: 0px;
+ width: 80px;
+}
+
+ .left-container{
+ display: inline-block;
+ width: 48%;
+
+
+ }
+
+
+ .right-container{
+ display: inline-block;
+ width: 48%;
+ float: right;
+ .mandatory-categories{
+ color: #cf2a2a;
+ font-size: 10px;
+ position:absolute;
+
+ }
+
+ .notif-input{
+ width:302px;
+ height:50px;
+
+ }
+ }
+
+
+
+ .ngdialog.ngdialog-theme-plain .ngdialog-content {
+ // max-width: 100%;
+ // width: 2000px;
+ }
+ .user-notif-label {
+
+ .checkbox-categories{
+ color: #cf2a2a;
+ font-size: 10px;
+ position:absolute;
+ margin-top: -6px;
+
+ }
+
+
+ .tree{
+ margin-left: 0px;
+ margin-right: 30px;
+ overflow-y: auto;
+ padding-top: 15px;
+ width:100%;
+ max-height: 250px;
+ margin-top:10px;
+ }
+
+
+ }
+
+
+
+ margin-bottom: 5px;
+ color: #5a5a5a;
+ font-family: Omnes-ECOMP-W02, Arial;
+ font-size: 14px;
+ font-weight: bold;
+ // padding-left: 10px;
+
+ .property{
+ position: relative;
+ margin-bottom: 18px;
+
+
+ .property-label{
+ .dGray14r;
+
+
+ }
+ .input-field{
+ .custom-input-field;
+ width: 220px;
+ }
+
+ .input-file-field{
+ width: 220px;
+ }
+ .select-field {
+ .custom-select-field;
+ }
+ .error-container{
+ position: absolute;
+ width: 220px;
+ display: block;
+ height: 12px;
+ line-height: 12px;
+
+ .err-message{
+ color: @funcRed;
+ font-size: 10px;
+ }
+ }
+ .js-node-check{
+ width: 16px;
+ height: 16px;
+ vertical-align:middle;
+ }
+ }
+ }
+ }
+
+ #datepicker{
+ z-index: 10000 !important;
+ width: 302px;
+ padding-left: 6px;
+ }
+
+
diff --git a/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.page.html b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.page.html
new file mode 100644
index 00000000..e342692a
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.modal.page.html
@@ -0,0 +1,161 @@
+<!--
+ ================================================================================
+ 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="user-notification-details-modal">
+ <!--<div class="functional-menu-container">-->
+ <div id="app-title" class="title">{{modalPgTitle}}</div>
+ <div class="user-notification-details-contents">
+ <div class="left-container">
+ <div class="add-widget-field" style="{{isEditMode? 'opacity : 0.6; pointer-events: none;':' '}} ">
+ <div class="user-notif-label">Broadcast to All Categories</div>
+ <label id="label-yes" class="simulateCatGridHeaderRadioButton"> <input
+ id="radio-button-yes" type="radio" ng-model="isForAllRoles" value="Y"> Yes
+ </label> <label id="label-no" class="simulateCatGridHeaderRadioButton"> <input
+ id="radio-button-no" type="radio" ng-model="isForAllRoles" value="N"> No
+ </label>
+ </div>
+ <div
+ style="padding-left: 10px; {{(! isForAllRoles ||isForAllRoles=='Y')? 'opacity : 0.6; pointer-events: none;':' '}} {{isEditMode? 'opacity : 0.6;':' '}} "
+ class="user-notif-label">
+ <div id="notifcation-label-user" class="user-notif-label">
+ <span ng-show="isForAllRoles=='N'" runat="server" ID="required" style="color: Red;">*</span>
+ Categories
+ </div>
+ <div>
+
+ <div id="notifcation-label-user-div" class="user-notif-label"></div>
+ <label id="funcMenu-label-yes" > <input id="radio-button-funcMenu"
+ type="radio" ng-model="isFunctionalMenu" ng-click="getFunctionalMenu()" value="Y"> Functional Menu
+ <br/>
+ </label> <label id="approles-label-no" > <input id="radio-button-approles"
+ type="radio" ng-model="isFunctionalMenu" ng-click="getAppRoleIds()" value="N"> Application Roles
+ </label>
+
+ <div id="approles-checkbox" ng-show="!checkBoxObj.isAnyRoleSelected&&(isForAllRoles=='N')&&(!isEditMode)">
+
+ <div id="approles-checkbox-required" ng-show="!checkBoxObj.isAnyRoleSelected" >
+
+
+ <small class="checkbox-categories">At least
+ one category is required</small>
+
+ </div>
+ </div>
+
+ </div>
+
+ <div id="tree1">
+ <div id="Service_Creation"></div>
+ </div>
+ <div
+
+ id="jq-tree-div" jq-tree-user-notif ></div>
+
+
+
+ <script type="text/ng-template" id="jq-tree-tmpl-user-notif.html">
+ <div id="jqTreeUserNotif" class="tree"></div>
+ </script>
+ </div>
+ <div>
+
+ <div id="add-user-notif-priority" class="add-widget-field" style="{{isEditMode? 'opacity : 0.6; pointer-events: none;':' '}} ">
+ <div id="user-notification-priority-label" class="user-notif-label">Priority</div>
+ <label id="label-important" class="simulateCatGridHeaderRadioButton"> <input
+ id="radio-button-normal" type="radio" ng-model="selectedPriority" value="1"> Normal
+ </label> <label id="label-normal" class="simulateCatGridHeaderRadioButton"> <input
+ id="radio-button-important" type="radio" ng-model="selectedPriority" value="2">
+ Important
+ </label>
+ </div>
+ </div>
+ </div>
+ <div id="app-conatiner-right" class="right-container">
+
+ <div id="add-user-notif-startdate" class="add-widget-field"
+ style="padding-bottom: 12px; width: 301px !important;{{( isEditMode )? 'opacity : 0.6; pointer-events: none;':' '}}">
+ <div id="user-notification-startdate-label" class="user-notif-label">
+ <div class="user-notif-label">
+ <span runat="server" ID="required" style="color: Red;">*</span>
+ Start Date (Local Time)
+ </div>
+ <input class="notif-input-calendar" id="datepicker-start" type="text"
+ ng-model="startTime" b2b-datepicker min="minDate" max="maxDate"
+ required />
+ <div id="user-startdate-required" ng-show="!startTime">
+ <small class="mandatory-categories">Start Date is Required</small>
+ </div>
+ </div>
+ <div ng-show="!isEditMode" ng-init="formatStartDate()"></div>
+ </div>
+
+ <div id="add-user-notif-enddate" class="add-widget-field"
+ style="padding-bottom: 12px; width: 301px !important; {{( isEditMode )? 'opacity : 0.6; pointer-events: none;':' '}}">
+ <div id="user-notification-enddate-label" class="user-notif-label">
+ <span runat="server" ID="required" style="color: Red;">*</span> End Date (Local Time)
+ </div>
+ <input class="notif-input-calendar" type="text" id="datepicker-end"
+ ng-model="endTime" b2b-datepicker min="minDate" max="maxDate"
+ required />
+ <div id="user-enddate-required" ng-show="!endTime" >
+ <small class="mandatory-categories">End Date is Required</small>
+ </div>
+ <div id="user-enddate-error" ng-show="endTime&&startTime&&startTime.getTime()>=endTime.getTime()" style="color: #cf2a2a; font-size: 10px;">
+ <small style="position: absolute;">End Date must be greater than start Date</small>
+ </div>
+ <div ng-show="!isEditMode" ng-init="formatEndDate()" ></div>
+ </div>
+
+ <div id="add-user-notif-title" class="add-widget-field"
+ style="padding-bottom: 12px; {{( isEditMode )? 'opacity : 0.6; pointer-events: none;':' '}}">
+ <div id="user-notification-title-label" class="user-notif-label">
+ <span runat="server" ID="required" style="color: Red;"
+ visible="false"> *</span> Title
+ </div>
+ <textarea id="add-notification-input-title" class="notif-input" ng-model="msgHeader" name="content" style="height: 50px;">
+ </textarea>
+ <div id="user-title-required" ng-show="msgHeader.length == 0">
+ <small class="mandatory-categories">Title is Required</small>
+ </div>
+ </div>
+
+ <div id="add-user-notif-message" class="add-widget-field"
+ style="padding-bottom: 12px; {{( isEditMode )? 'opacity : 0.6; pointer-events: none;':' '}}">
+ <div id="user-notif-message-label" class="user-notif-label">
+ <span runat="server" ID="required" style="color: Red;"
+ visible="false"> *</span> Message
+ </div>
+ <textarea id="user-notif-input-message" class="notif-input" style="height: 150px"
+ ng-model="msgDescription" name="content">
+ </textarea>
+ <div id="user-notif-message-required" ng-show="msgDescription.length ==0 ">
+ <small class="mandatory-categories">Message is Required</small>
+ </div>
+ </div>
+
+ <div class="dialog-control">
+ <a ng-show="!isEditMode">
+ <button id="button-notification-save" class="btn btn-alt btn-small" size="small" ng-disabled="addUserNotificationValidation()"
+ ng-click="addUserNotification()">Save</button></a>
+ <button id="button-notification-cancel" class="btn btn-alt btn-small" ng-click="closeThisDialog()" role="button" tabindex="0">Cancel</button>
+ </div>
+
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.tpl.html b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.tpl.html
new file mode 100644
index 00000000..742946f2
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/user-notifications-admin/user.notifications.tpl.html
@@ -0,0 +1,117 @@
+<!--
+ ================================================================================
+ 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-user-notification-admin"
+ ng-style="{bottom: tabBottom}">
+ <div class="w-ecomp-main-container" >
+ <div class="user-notification" id="page-content">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page" >User Notifications</h1>
+ </div>
+ <div class="userNotifTable">
+
+ <div class="table-control-fields">
+ <input class="input-table-search" type="text" id="table-search-field"
+ placeholder="Search in entire table" ng-model="searchString" />
+ </div>
+ <div class="table-control-buttons" ng-controller="userNotificationsCtrl">
+ <button class="btn btn-alt btn-small"id="button-openAddNewApp" ng-click="openUserNotificationModal()" >
+ <i class="icon-people-userbookmark" aria-hidden="true"></i>&nbsp;Add Notification
+ </button>
+ </div>
+
+ <span class="ecomp-spinner" ng-show="isLoadingTable"></span>
+ <div class="b2b-table-div"
+ ng-hide="isLoadingTable" id="table-main">
+ <table b2b-table id="table-main" table-data="adminNotifications" search-string="searchString"
+ current-page="ignoredCurrentPage">
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="th-notif-0" b2b-table-header key="msgSource"
+ sortable="true" style=" width: 10px;">Message Source</th>
+ <th id="th-notif-1" b2b-table-header key="msgHeader"
+ sortable="true" style=" width: 10px;">Message</th>
+ <th id="th-notif-2" b2b-table-header key="startTime"
+ sortable="true">Start Date (Local Time)</th>
+ <th id="th-notif-3" b2b-table-header key="endTime"
+ sortable="true">End Date (Local Time)</th>
+ <th id="th-notif-4" b2b-table-header key="priority"
+ sortable="true">Priority</th>
+ <th id="th-notif-5" b2b-table-header key="loginId"
+ sortable="true">Created By</th>
+ <th id="th-notif-6" b2b-table-header key="createdDate"
+ sortable="true">Created Time</th>
+ <th id="th-notif-7" b2b-table-header key="isForAllRoles"
+ sortable="true">All Users (Roles)?</th>
+ <th id="th-notif-8" b2b-table-header key="edit" sortable="false">View/Delete</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" class="table-body"
+ row-repeat="rowData in tableAdminNotifItems">
+ <tr>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-msgSource">{{rowData.msgSource}}</div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?' color:lightgray !important':''}}">
+ <div id="{{$index}}-msgHeader" style="font-weight: bold;">{{rowData.msgHeader}}</div>
+ <div id="{{$index}}-message" ng-if="rowData.msgSource==='EP'" style="width:500px" ng-bind="rowData.msgDescription"></div>
+ <div id="{{$index}}-message" ng-if="rowData.msgSource!=='EP'" ng-bind="rowData.msgDescription| elipsis: 27"></div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-startTime">{{rowData.startTime |
+ date:'medium'}}</div>
+ </td >
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-endTime">{{rowData.endTime |
+ date:'medium'}}</div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-priority">{{priorityItems[rowData.priority]}}</div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-loginId">{{!rowData.loginId ?externalNotification: rowData.loginId}}</div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-createdDate">{{rowData.createdDate |
+ date:'medium'}}</div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?'color:lightgray !important':''}}">
+ <div id="{{$index}}-isForAllRoles">{{rowData.isForAllRoles}}</div>
+ </td>
+ <td b2b-table-body style="{{rowData.expired?' color:lightgray !important':''}}">
+
+ <p id="{{$index}}-notification-edit" ng-if="rowData.msgSource==='EP'" ng-click="editUserNotificationModal(rowData)"class="icon-overview" > /</p>
+ <p id="{{$index}}-notification-edit" ng-if="rowData.msgSource!=='EP'" ng-click="showDetailedJsonMessage(rowData)"class="icon-overview" >/ </p>
+ <p id="{{$index}}-notification-delete" ng-click="removeUserNotification(rowData)" class="icon-misc-trash"></p>
+
+
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div b2b-pagination="" total-pages="totalPages1"
+ current-page="currentPage1" click-handler="customPageHandler"
+ role="navigation" aria-label="Customer Data Pages"></div>
+
+
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/userbar/userbar.less b/ecomp-portal-FE-common/client/app/views/userbar/userbar.less
new file mode 100644
index 00000000..20248a9c
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/userbar/userbar.less
@@ -0,0 +1,22 @@
+.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-common/client/app/views/users/new-user-dialogs/bulk-user.ack.html b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.ack.html
new file mode 100644
index 00000000..9527c750
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.ack.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-user-modal">
+ <div class="search-users-directive">
+ <div class="title">Bulk User Upload Acknowledgement</div>
+ <div class="main">
+ <h1>The valid entries have been uploaded.</h1>
+
+ <div class="dialog-control">
+ <div id="search-user-cancel-button" class="cancel-button"
+ ng-click="closeDialog()">OK</div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.confirm.html b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.confirm.html
new file mode 100644
index 00000000..a3c0b534
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.confirm.html
@@ -0,0 +1,83 @@
+<!--
+ ================================================================================
+ 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="bulk-user-modal">
+ <div class="title">Bulk User Upload Confirmation</div>
+ <div class="main">
+
+ <!-- progress indicator -->
+ <div class="upload-instructions"
+ ng-show="isProcessing">
+ {{progressMsg}}
+ <br>
+ <br>
+ <span class="ecomp-spinner"></span>
+ </div>
+
+ <div ng-hide="isProcessing">
+ <div class="upload-instructions">
+ Click OK to upload the valid requests.
+ Invalid requests will be ignored.</div>
+ <div class="c-ecomp-portal-abs-table default"
+ style="height: 250px !important">
+ <table b2b-table table-data="uploadFile"
+ search-string="bulkUser.searchString"
+ view-per-page="bulkUser.viewPerPageIgnored"
+ current-page="bulkUser.currentPageIgnored"
+ total-page="bulkUser.totalPageIgnored">
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="th-line" b2b-table-header sortable="false">Line</th>
+ <th id="th-orgUserId" b2b-table-header sortable="false">Org User ID
+ </th>
+ <th id="th-approle" b2b-table-header sortable="false">App
+ Role</th>
+ <th id="th-status" b2b-table-header sortable="false">Status</th>
+ </tr>
+ </thead>
+ <!-- Use track-by="UNIQUE KEY HERE" or leave out if no unique keys in data -->
+ <tbody b2b-table-row type="body" class="table-body"
+ row-repeat="rowData in uploadFile">
+ <tr id="tr-rowData">
+ <td class="td-first" b2b-table-body>
+ <div ng-bind="rowData.line"></div>
+ </td>
+ <td b2b-table-body>
+ <div ng-bind="rowData.orgUserId"></div>
+ </td>
+ <td b2b-table-body>
+ <div ng-bind="rowData.role"></div>
+ </td>
+ <td b2b-table-body>
+ <div ng-bind="rowData.status"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ </div>
+ <div class="dialog-control">
+ <button id="bulk-user-ok-button" class="btn btn-alt btn-small" ng-class="{disabled: isValidating}"
+ ng-click="updateDB()">Ok</button>
+ <button id="bulk-user-cancel-button" class="btn btn-alt btn-small" ng-click="cancelUpload()">Cancel</button>
+
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.controller.js b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.controller.js
new file mode 100644
index 00000000..e3046b86
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.controller.js
@@ -0,0 +1,577 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+/**
+ * bulk user upload controller
+ */
+'use strict';
+(function () {
+ class BulkUserModalCtrl {
+ constructor($scope, $log, $filter, $q, usersService, applicationsService, confirmBoxService, functionalMenuService, ngDialog) {
+
+ // Set to true for copious console output
+ var debug = false;
+ // Roles fetched from app service
+ var appRolesResult = [];
+ // Users fetched from user service
+ var userCheckResult = [];
+ // Requests for user-role assignment built by validator
+ var appUserRolesRequest = [];
+
+ let init = () => {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::init');
+ // Angular insists on this.
+ $scope.fileModel = {};
+ // Model for drop-down
+ $scope.adminApps = [];
+ // Enable modal controls
+ this.step1 = true;
+ this.fileSelected = false;
+
+ // Flag that indicates background work is proceeding
+ $scope.isProcessing = true;
+
+ // Load user's admin applications
+ applicationsService.getAdminApps().promise().then(apps => {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::init: getAdminApps returned' + JSON.stringify(apps));
+ if (!apps || typeof(apps) != 'object') {
+ $log.error('BulkUserModalCtrl::init: getAdminApps returned unexpected data');
+ }
+ else {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::init: admin apps length is ', apps.length);
+
+ // Sort app names and populate the drop-down model
+ let sortedApps = apps.sort(getSortOrder('name', true));
+ for (let i = 0; i < sortedApps.length; ++i) {
+ $scope.adminApps.push({
+ index: i,
+ id: sortedApps[i].id,
+ value: sortedApps[i].name,
+ title: sortedApps[i].name
+ });
+ }
+ // Pick the first one in the list
+ $scope.selectedApplication = $scope.adminApps[0];
+ }
+ $scope.isProcessing = false;
+ }).catch(err => {
+ $log.error('BulkUserModalCtrl::init: getAdminApps threw', err);
+ $scope.isProcessing = false;
+ });
+
+ }; // init
+
+ // Answers a function that compares properties with the specified name.
+ let getSortOrder = (prop, foldCase) => {
+ return function(a, b) {
+ let aProp = foldCase ? a[prop].toLowerCase() : a[prop];
+ let bProp = foldCase ? b[prop].toLowerCase() : b[prop];
+ if (aProp > bProp)
+ return 1;
+ else if (aProp < bProp)
+ return -1;
+ else
+ return 0;
+ }
+ }
+
+ //This is a fix for dropdown selection, due to b2b dropdown only update value field
+ $scope.$watch('selectedApplication.value', (newVal, oldVal) => {
+ for(var i=0;i<$scope.adminApps.length;i++){
+ if($scope.adminApps[i].value==newVal){
+ $scope.selectedApplication=angular.copy($scope.adminApps[i]);;
+ }
+ }
+ });
+
+ // Invoked when user picks an app on the drop-down.
+ $scope.appSelected = () => {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::appSelected: selectedApplication.id is ' + $scope.selectedApplication.id);
+ this.appSelected = true;
+ }
+
+ // Caches the file name supplied by the event handler.
+ $scope.fileChangeHandler = (event, files) => {
+ this.fileSelected = true;
+ this.fileToRead = files[0];
+ if (debug)
+ $log.debug("BulkUserModalCtrl::fileChangeHandler: file is ", this.fileToRead);
+ }; // file change handler
+
+ /**
+ * Reads the contents of the file, calls portal endpoints
+ * to validate roles, userIds and existing role assignments;
+ * ultimately builds array of requests to be sent.
+ * Creates scope variable with input file contents for
+ * communication with functions.
+ *
+ * This function performs a synchronous step-by-step process
+ * using asynchronous promises. The code could all be inline
+ * here but the nesting becomes unwieldy.
+ */
+ $scope.readValidateFile = () => {
+ $scope.isProcessing = true;
+ $scope.progressMsg = 'Reading upload file..';
+ var reader = new FileReader();
+ reader.onload = function(event) {
+ $scope.uploadFile = $filter('csvToObj')(reader.result);
+ if (debug)
+ $log.debug('BulkUserModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
+ // sort input by orgUserId
+ $scope.uploadFile.sort(getSortOrder('orgUserId', true));
+
+ let appid = $scope.selectedApplication.id;
+ $scope.progressMsg = 'Fetching application roles..';
+ functionalMenuService.getManagedRolesMenu(appid).then(function (rolesObj) {
+ if (debug)
+ $log.debug("BulkUserModalCtrl::readValidateFile: managedRolesMenu returned " + JSON.stringify(rolesObj));
+ appRolesResult = rolesObj;
+ $scope.progressMsg = 'Validating application roles..';
+ $scope.verifyRoles();
+
+ let userPromises = $scope.buildUserChecks();
+ if (debug)
+ $log.debug('BulkUserModalCtrl::readValidateFile: userPromises length is ' + userPromises.length);
+ $scope.progressMsg = 'Validating Org Users..';
+ $q.all(userPromises).then(function() {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::readValidateFile: userCheckResult length is ' + userCheckResult.length);
+ $scope.evalUserCheckResults();
+
+ let appPromises = $scope.buildAppRoleChecks();
+ if (debug)
+ $log.debug('BulkUserModalCtrl::readValidateFile: appPromises length is ' + appPromises.length);
+ $scope.progressMsg = 'Querying application for user roles..';
+ $q.all(appPromises).then( function() {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::readValidateFile: appUserRolesRequest length is ' + appUserRolesRequest.length);
+ $scope.evalAppRoleCheckResults();
+
+ // Re sort by line for the confirmation dialog
+ $scope.uploadFile.sort(getSortOrder('line', false));
+ // We're done, confirm box may show the table
+ if (debug)
+ $log.debug('BulkUserModalCtrl::readValidateFile inner-then ends');
+ $scope.progressMsg = 'Done.';
+ $scope.isProcessing = false;
+ },
+ function(error) {
+ $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving user-app roles');
+ $scope.isProcessing = false;
+ }
+ ); // then of app promises
+ },
+ function(error) {
+ $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving user info');
+ $scope.isProcessing = false;
+ }
+ ); // then of user promises
+ },
+ function(error) {
+ $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app role info');
+ $scope.isProcessing = false;
+ }
+ ); // then of role promise
+
+ } // onload
+
+ // Invoke the reader on the selected file
+ reader.readAsText(this.fileToRead);
+ };
+
+ /**
+ * Evaluates the result set returned by the app role service.
+ * Sets an uploadFile array element status if a role is not defined.
+ * Reads and writes scope variable uploadFile.
+ * Reads closure variable appRolesResult.
+ */
+ $scope.verifyRoles = () => {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::verifyRoles: appRoles is ' + JSON.stringify(appRolesResult));
+ // check roles in upload file against defined app roles
+ $scope.uploadFile.forEach( function (uploadRow) {
+ // skip rows that already have a defined status: headers etc.
+ if (uploadRow.status) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::verifyRoles: skip row ' + uploadRow.line);
+ return;
+ }
+ uploadRow.role = uploadRow.role.trim();
+ var foundRole=false;
+ for (var i=0; i < appRolesResult.length; i++) {
+ if (uploadRow.role.toUpperCase() === appRolesResult[i].rolename.trim().toUpperCase()) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::verifyRoles: match on role ' + uploadRow.role);
+ foundRole=true;
+ break;
+ }
+ };
+ if (!foundRole) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::verifyRoles: NO match on role ' + uploadRow.role);
+ uploadRow.status = 'Invalid role';
+ };
+ }); // foreach
+ }; // verifyRoles
+
+ /**
+ * Builds and returns an array of promises to invoke the
+ * searchUsers service for each unique Org User UID in the input.
+ * Reads and writes scope variable uploadFile, which must be sorted by Org User UID.
+ * The promise function writes to closure variable userCheckResult
+ */
+ $scope.buildUserChecks = () => {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::buildUserChecks: uploadFile length is ' + $scope.uploadFile.length);
+ userCheckResult = [];
+ let promises = [];
+ let prevRow = null;
+ $scope.uploadFile.forEach(function (uploadRow) {
+ if (uploadRow.status) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::buildUserChecks: skip row ' + uploadRow.line);
+ return;
+ };
+ // detect repeated UIDs
+ if (prevRow == null || prevRow.orgUserId.toLowerCase() !== uploadRow.orgUserId.toLowerCase()) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::buildUserChecks: create request for orgUserId ' + uploadRow.orgUserId);
+ let userPromise = usersService.searchUsers(uploadRow.orgUserId).promise().then( (usersList) => {
+ if (typeof usersList[0] !== "undefined") {
+ userCheckResult.push({
+ orgUserId: usersList[0].orgUserId,
+ firstName: usersList[0].firstName,
+ lastName: usersList[0].lastName,
+ jobTitle: usersList[0].jobTitle
+ });
+ }
+ else {
+ // User not found.
+ if (debug)
+ $log.debug('BulkUserModalCtrl::buildUserChecks: searchUsers returned null');
+ }
+ }, function(error){
+ $log.error('BulkUserModalCtrl::buildUserChecks: searchUsers failed ' + JSON.stringify(error));
+ });
+ promises.push(userPromise);
+ }
+ else {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::buildUserChecks: skip repeated orgUserId ' + uploadRow.orgUserId);
+ }
+ prevRow = uploadRow;
+ }); // foreach
+ return promises;
+ }; // buildUserChecks
+
+ /**
+ * Evaluates the result set returned by the user service to set
+ * the uploadFile array element status if the user was not found.
+ * Reads and writes scope variable uploadFile.
+ * Reads closure variable userCheckResult.
+ */
+ $scope.evalUserCheckResults = () => {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::evalUserCheckResult: uploadFile length is ' + $scope.uploadFile.length);
+ $scope.uploadFile.forEach(function (uploadRow) {
+ if (uploadRow.status) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::evalUserCheckResults: skip row ' + uploadRow.line);
+ return;
+ };
+ let foundorgUserId = false;
+ userCheckResult.forEach(function(userItem) {
+ if (uploadRow.orgUserId.toLowerCase() === userItem.orgUserId.toLowerCase()) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::evalUserCheckResults: found orgUserId ' + uploadRow.orgUserId);
+ foundorgUserId=true;
+ };
+ });
+ if (!foundorgUserId) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::evalUserCheckResults: NO match on orgUserId ' + uploadRow.orgUserId);
+ uploadRow.status = 'Invalid orgUserId';
+ }
+ }); // foreach
+ }; // evalUserCheckResults
+
+ /**
+ * Builds and returns an array of promises to invoke the getUserAppRoles
+ * service for each unique Org User in the input file.
+ * Each promise creates an update to be sent to the remote application
+ * with all role names.
+ * Reads scope variable uploadFile, which must be sorted by Org User.
+ * The promise function writes to closure variable appUserRolesRequest
+ */
+ $scope.buildAppRoleChecks = () => {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::buildAppRoleChecks: uploadFile length is ' + $scope.uploadFile.length);
+ appUserRolesRequest = [];
+ let appId = $scope.selectedApplication.id;
+ let promises = [];
+ let prevRow = null;
+ $scope.uploadFile.forEach( function (uploadRow) {
+ if (uploadRow.status) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::buildAppRoleChecks: skip row ' + uploadRow.line);
+ return;
+ }
+ // Because the input is sorted, generate only one request for each Org User
+ if (prevRow == null || prevRow.orgUserId.toLowerCase() !== uploadRow.orgUserId.toLowerCase()) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::buildAppRoleChecks: create request for orgUserId ' + uploadRow.orgUserId);
+ let appPromise = usersService.getUserAppRoles(appId, uploadRow.orgUserId).promise().then( (userAppRolesResult) => {
+ // Reply for unknown user has all defined roles with isApplied=false on each.
+ if (typeof userAppRolesResult[0] !== "undefined") {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::buildAppRoleChecks: adding result '
+ + JSON.stringify(userAppRolesResult));
+ appUserRolesRequest.push({
+ orgUserId: uploadRow.orgUserId,
+ userAppRoles: userAppRolesResult
+ });
+ } else {
+ $log.error('BulkUserModalCtrl::buildAppRoleChecks: getUserAppRoles returned ' + JSON.stringify(userAppRolesResult));
+ };
+ }, function(error){
+ $log.error('BulkUserModalCtrl::buildAppRoleChecks: getUserAppRoles failed ', error);
+ });
+ promises.push(appPromise);
+ } else {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::buildAppRoleChecks: duplicate orgUserId, skip: '+ uploadRow.orgUserId);
+ }
+ prevRow = uploadRow;
+ }); // foreach
+ return promises;
+ }; // buildAppRoleChecks
+
+ /**
+ * Evaluates the result set returned by the app service and adjusts
+ * the list of updates to be sent to the remote application by setting
+ * isApplied=true for each role name found in the upload file.
+ * Reads and writes scope variable uploadFile.
+ * Reads closure variable appUserRolesRequest.
+ */
+ $scope.evalAppRoleCheckResults = () => {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::evalAppRoleCheckResults: uploadFile length is ' + $scope.uploadFile.length);
+ $scope.uploadFile.forEach(function (uploadRow) {
+ if (uploadRow.status) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::evalAppRoleCheckResults: skip row ' + uploadRow.line);
+ return;
+ }
+ // Search for the match in the app-user-roles array
+ appUserRolesRequest.forEach( function (appUserRoleObj) {
+ if (uploadRow.orgUserId.toLowerCase() === appUserRoleObj.orgUserId.toLowerCase()) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::evalAppRoleCheckResults: match on orgUserId ' + uploadRow.orgUserId);
+ let roles = appUserRoleObj.userAppRoles;
+ roles.forEach(function (appRoleItem) {
+ //if (debug)
+ // $log.debug('BulkUserModalCtrl::evalAppRoleCheckResults: checking uploadRow.role='
+ // + uploadRow.role + ', appRoleItem.roleName= ' + appRoleItem.roleName);
+ if (uploadRow.role === appRoleItem.roleName) {
+ if (appRoleItem.isApplied) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::evalAppRoleCheckResults: existing role '
+ + appRoleItem.roleName);
+ uploadRow.status = 'Role exists';
+ }
+ else {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::evalAppRoleCheckResults: new role '
+ + appRoleItem.roleName);
+ // After much back-and-forth I decided a clear indicator
+ // is better than blank in the table status column.
+ uploadRow.status = 'OK';
+ appRoleItem.isApplied = true;
+ }
+ // This count is not especially interesting.
+ // numberUserRolesSucceeded++;
+ }
+ }); // for each role
+ }
+ }); // for each result
+ }); // for each row
+ }; // evalAppRoleCheckResults
+
+ /**
+ * Sends requests to Portal requesting user role assignment.
+ * That endpoint handles creation of the user at the remote app if necessary.
+ * Reads closure variable appUserRolesRequest.
+ * Invoked by the Next button on the confirmation dialog.
+ */
+ $scope.updateDB = () => {
+ $scope.isProcessing = true;
+ $scope.progressMsg = 'Sending requests to application..';
+ if (debug)
+ $log.debug('BulkUserModalCtrl::updateDB: request length is ' + appUserRolesRequest.length);
+ var numberUsersSucceeded = 0;
+ let promises = [];
+ appUserRolesRequest.forEach(function(appUserRoleObj) {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::updateDB: appUserRoleObj is ' + JSON.stringify(appUserRoleObj));
+ let updateRequest = {
+ orgUserId: appUserRoleObj.orgUserId,
+ appId: $scope.selectedApplication.id,
+ appRoles: appUserRoleObj.userAppRoles
+ };
+ if (debug)
+ $log.debug('BulkUserModalCtrl::updateDB: updateRequest is ' + JSON.stringify(updateRequest));
+ let updatePromise = usersService.updateUserAppRoles(updateRequest).promise().then(res => {
+ if (debug)
+ $log.debug('BulkUserModalCtrl::updateDB: updated successfully: ' + JSON.stringify(res));
+ numberUsersSucceeded++;
+ }).catch(err => {
+ // What to do if one of many fails??
+ $log.error('BulkUserModalCtrl::updateDB failed: ', err);
+ confirmBoxService.showInformation(
+ 'Failed to update the user application roles. ' +
+ 'Error: ' + err.status).then(isConfirmed => { });
+ }).finally( () => {
+ // $log.debug('BulkUserModalCtrl::updateDB: finally()');
+ });
+ promises.push(updatePromise);
+ }); // for each
+
+ // Run all the promises
+ $q.all(promises).then(function(){
+ $scope.isProcessing = false;
+ confirmBoxService.showInformation('Processed ' + numberUsersSucceeded + ' users.').then(isConfirmed => {
+ // Close the upload-confirm dialog
+ ngDialog.close();
+ });
+ });
+ }; // updateDb
+
+ // Sets the variable that hides/reveals the user controls
+ $scope.step2 = () => {
+ this.fileSelected = false;
+ $scope.selectedFile = null;
+ $scope.fileModel = null;
+ this.step1 = false;
+ }
+
+ // Navigate between dialog screens using step number: 1,2,...
+ $scope.navigateBack = () => {
+ this.step1 = true;
+ this.fileSelected = false;
+ };
+
+ // Opens a dialog to show the data to be uploaded.
+ // Invoked by the upload button on the bulk user dialog.
+ $scope.confirmUpload = () => {
+ // Start the process
+ $scope.readValidateFile();
+ // Dialog shows progress
+ ngDialog.open({
+ templateUrl: 'app/views/users/new-user-dialogs/bulk-user.confirm.html',
+ scope: $scope
+ });
+ };
+
+ // Invoked by the Cancel button on the confirmation dialog.
+ $scope.cancelUpload = () => {
+ ngDialog.close();
+ };
+
+ init();
+ } // constructor
+ } // class
+ BulkUserModalCtrl.$inject = ['$scope', '$log', '$filter', '$q', 'usersService', 'applicationsService', 'confirmBoxService', 'functionalMenuService', 'ngDialog'];
+ angular.module('ecompApp').controller('BulkUserModalCtrl', BulkUserModalCtrl);
+
+ angular.module('ecompApp').directive('fileChange', ['$parse', function($parse){
+ return {
+ require: 'ngModel',
+ restrict: 'A',
+ link : function($scope, element, attrs, ngModel) {
+ var attrHandler = $parse(attrs['fileChange']);
+ var handler=function(e) {
+ $scope.$apply(function() {
+ attrHandler($scope, { $event:e, files:e.target.files } );
+ $scope.selectedFile = e.target.files[0].name;
+ });
+ };
+ element[0].addEventListener('change',handler,false);
+ }
+ }
+ }]);
+
+ angular.module('ecompApp').filter('csvToObj',function() {
+ return function(input) {
+ var result = [];
+ var len, i, line, o;
+ var lines = input.split('\n');
+ // Need 1-based index below
+ for (len = lines.length, i = 1; i <= len; ++i) {
+ // Use 0-based index for array
+ line = lines[i - 1].trim();
+ if (line.length == 0) {
+ // console.log("Skipping blank line");
+ result.push({
+ line: i,
+ orgUserId: '',
+ role: '',
+ status: 'Blank line'
+ });
+ continue;
+ }
+ o = line.split(',');
+ if (o.length !== 2) {
+ // other lengths not valid for upload
+ result.push({
+ line: i,
+ orgUserId: line,
+ role: '',
+ status: 'Failed to find 2 comma-separated values'
+ });
+ }
+ else {
+ // console.log("Valid line: ", val);
+ let entry = {
+ line: i,
+ orgUserId: o[0],
+ role: o[1]
+ // leave status undefined, this could be valid.
+ };
+ if (o[0].toLowerCase() === 'orgUserId') {
+ // not valid for upload, so set status
+ entry.status = 'Header';
+ }
+ else if (o[0].trim() == '' || o[1].trim() == '') {
+ // defend against line with only a single comma etc.
+ entry.status = 'Failed to find 2 non-empty values';
+ }
+ result.push(entry);
+ } // len 2
+ } // for
+ return result;
+ };
+ });
+
+
+
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.modal.html b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.modal.html
new file mode 100644
index 00000000..3d479cb9
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-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="bulk-user-modal">
+ <div class="title">Bulk User Upload</div>
+ <div class="main">
+ <div ng-show="bulkUser.step1">
+ <div class="upload-instructions">Select Application:</div>
+ <div class="c-ecomp-portal-abs-select default">
+
+ <select id="bulk-user-dropdown-apps" name="dropdown1" b2b-dropdown ng-model="selectedApplication.value" ng-disabled="isProcessing" ng-class="{disabled: isProcessing}">
+ <option b2b-dropdown-list option-repeat="d in adminApps" value="{{d.value}}">{{d.title}}</option>
+ </select>
+
+ </div>
+ </div>
+
+ <div ng-hide="bulkUser.step1">
+ <div class="upload-instructions">Select Upload File:</div>
+
+ <!-- input type=file is difficult to style.
+ Instead use a label styled as a button. -->
+ <label class="file-label">
+ <input type="file"
+ file-change="fileChangeHandler($event,files)"
+ ng-model="fileModel" />
+ <span>Browse...</span>
+ </label>{{selectedFile}}
+ <div class="upload-instructions">File must have one entry per line with this format:
+ <pre>orgUserId, role name</pre>
+ </div>
+ </div>
+
+ <!-- progress indicator in middle -->
+ <div ng-show="isProcessing">
+ <span class="ecomp-spinner"></span>
+ </div>
+
+ <div class="dialog-control">
+
+ <button id="bulk-user-back-button" class="btn btn-alt btn-small"
+ ng-hide="bulkUser.step1" ng-click="navigateBack()">Back</button>
+ <button id="bulk-user-next-button" class="btn btn-alt btn-small"
+ ng-hide="!bulkUser.step1" ng-click="!isProcessing && step2()"
+ ng-class="{disabled: isProcessing}">Next</button>
+ <button id="bulk-user-upload-button" class="btn btn-alt btn-small"
+ ng-hide="bulkUser.step1"
+ ng-click="bulkUser.fileSelected && confirmUpload()"
+ ng-class="{disabled: !bulkUser.fileSelected}">Upload</button>
+ <button id="bulk-user-cancel-button" class="btn btn-alt btn-small"
+ ng-click="closeThisDialog()">Cancel</button>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.modal.less b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.modal.less
new file mode 100644
index 00000000..b6ee63f8
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/bulk-user.modal.less
@@ -0,0 +1,60 @@
+.bulk-user-modal {
+ height: 430px;
+
+ .title {
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @blue-active 3px solid;
+ }
+
+ .main {
+ margin: 16px;
+
+ .upload-instructions {
+ .dGray14r;
+ }
+
+ // http://stackoverflow.com/questions/572768/styling-an-input-type-file-button
+
+ .file-label {
+ border: 1px solid #ffffff;
+ border-radius: 6px;
+ margin-top: 4px;
+ margin-bottom: 4px;
+ margin-left: 0px;
+ margin-right: 8px;
+ color: #ffffff;
+ background: #067ab4;
+ display: inline-block;
+ text-align: center;
+ font-family: Omnes-ECOMP-W02-Medium,Arial;
+ font-size: 14px;
+ height: 29px;
+ line-height: 29px;
+ width: 90px;
+
+ input[type="file"] {
+ // Hide the browser's control
+ display: none;
+ }
+
+ }
+
+ .file-label:hover {
+ background: #009fdb;
+ }
+
+ .file-label:active {
+ background: #009fdb;
+ }
+
+ .file-label:invalid+span {
+ color: #ffffff;
+ }
+
+ .file-label:valid+span {
+ color: #ffffff;
+ }
+
+ }
+
+}
diff --git a/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.controller.js b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.controller.js
new file mode 100644
index 00000000..882f1e8f
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.controller.js
@@ -0,0 +1,216 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+/**
+ * Created by nnaffar on 12/20/15.
+ */
+'use strict';
+(function () {
+ class NewUserModalCtrl {
+ constructor($scope, $log, usersService, applicationsService, confirmBoxService) {
+ let init = () => {
+ //$log.info('NewUserModalCtrl::init');
+ this.isSaving = false;
+ this.anyChanges = false;
+ this.adminApps = [];
+ 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){
+ this.anyChanges = true;
+ app.isChanged = true;
+ app.isDeleted = true; // use this to hide the app in the display
+ app.appRoles.forEach(function(role){
+ role.isApplied = false;
+ });
+ }
+ }).catch(err => {
+ $log.error('NewUserModalCtrl::deleteApp error: ',err);
+ confirmBoxService.showInformation('There was a problem deleting the the applications. ' +
+ 'Please try again later. Error: ' + err.status).then(isConfirmed => {});
+ });
+ };
+
+ 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 admin 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);
+ // Keep track of which app has changed, so we know which apps to update using a BE API
+ app.isChanged = false;
+ // Each of these specifies a state, which corresponds to a different message and style that gets displayed
+ 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).promise().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);
+ })
+
+ }
+
+ /**
+ * Update the selected user apps with the new roles.
+ * If no roles remain, set the user to inactive.
+ */
+ this.updateUserAppsRoles = () => {
+ // $log.debug('NewUserModalCtrl::updateUserAppsRoles: entering updateUserAppsRoles');
+ if(!this.selectedUser || !this.selectedUser.orgUserId || !this.adminApps){
+ $log.error('NewUserModalCtrl::updateUserAppsRoles: mmissing arguments');
+ 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;
+ var newUserAppRoles = {
+ orgUserId: this.selectedUser.orgUserId,
+ appId: app.id,
+ appRoles: app.appRoles,
+ appName: app.name
+ };
+ usersService.updateUserAppRoles(newUserAppRoles).promise()
+ .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;
+ confirmBoxService.showInformation(
+ 'Failed to update the user application roles: ' + err.status)
+ .then(isConfirmed => {});
+ }).finally(()=>{
+ this.numberAppsProcessed++;
+ if (this.numberAppsProcessed === this.adminApps.length) {
+ this.isSaving = false; // hide the spinner
+ }
+ if (this.numberAppsSucceeded === this.adminApps.length) {
+ $scope.closeThisDialog(true);//close and resolve dialog promise with true (to update the table)
+ }
+ })
+ } 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; //remove the error message; just show the No Changes messages
+ this.numberAppsProcessed++;
+ this.numberAppsSucceeded++;
+ if (this.numberAppsProcessed === this.adminApps.length) {
+ this.isSaving = false; // hide the spinner
+ }
+ if (this.numberAppsSucceeded === this.adminApps.length) {
+ $scope.closeThisDialog(true);//close and resolve dialog promise with true (to update the table)
+ }
+ }
+ });
+ };
+
+ /**
+ * Navigate between dialog screens using step number: 1,2,...
+ */
+ this.navigateBack = () => {
+ if (this.dialogState === 1) {
+ //back from 1st screen?
+ }
+ if (this.dialogState === 3) {
+ this.dialogState = 1;
+ }
+ };
+
+ init();
+
+ $scope.$on('$stateChangeStart', e => {
+ //Disable navigation when modal is opened
+ //**Nabil - note: this will cause the history back state to be replaced with current state
+ e.preventDefault();
+ });
+ }
+ }
+ NewUserModalCtrl.$inject = ['$scope', '$log', 'usersService', 'applicationsService', 'confirmBoxService'];
+ angular.module('ecompApp').controller('NewUserModalCtrl', NewUserModalCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.controller.spec.js b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.controller.spec.js
new file mode 100644
index 00000000..8d5ac749
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.controller.spec.js
@@ -0,0 +1,255 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+/**
+ * Created by nnaffar on 12/20/15.
+ */
+'use strict';
+
+describe('Controller: NewUserModalCtrl ', () => {
+ beforeEach(module('testUtils'));
+ beforeEach(module('ecompApp'));
+
+ let promisesTestUtils;
+ //destroy $http default cache before starting to prevent the error 'default cache already exists'
+ //_promisesTestUtils_ comes from testUtils for promises resolve/reject
+ 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']);
+
+ //applicationsServiceMock.getAdminApps().promise().and.returnValue(deferredAdminApps.promise);
+ 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
+ });
+ //$scope.users = users;
+ });
+
+ /*beforeEach(()=> {
+ scope = $rootScope.$new();
+ newUser = $controller('NewUserModalCtrl', {
+ $scope: scope,
+ $log: $log,
+ usersService: usersService,
+ applicationsService: applicationsService,
+ confirmBoxService: confirmBoxService
+ });
+ });*/
+
+
+ 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 = {orgUserId: 'asdfjl'};
+
+ deferredUsersAppRoles.resolve(roles);
+ deferredAdminApps.resolve(roles.apps);
+
+ $scope.ngDialogData = {
+ selectedUser: someUser,
+ dialogState: 2
+ };
+
+ //inject ngDialogData to the scope controller
+ 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 = {orgUserId: 'asdfjl'};
+
+ deferredUsersAppRoles.resolve(roles);
+ //deferredAdminApps.resolve(roles.apps);
+
+ $scope.ngDialogData = {
+ selectedUser: someUser,
+ dialogState: 2
+ };
+
+ //inject ngDialogData to the scope controller
+ newUser = $controller('NewUserModalCtrl', {
+ $scope: $scope,
+ $log: $log,
+ usersService: usersServiceMock,
+ applicationsService: applicationsServiceMock,
+ confirmBoxService: confirmBoxServiceMock
+ });
+
+ $scope.$apply();
+
+ // expect(newUser.appsOrder).toEqual([13]);
+ });
+
+ 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 = {orgUserId: 'asdfjl'};
+
+ // promisesTestUtils.resolvePromise(usersService, 'getUserAppsRoles', roles);
+ deferredUsersAppRoles.resolve(roles);
+
+ $scope.ngDialogData = {
+ selectedUser: someUser,
+ dialogState: 2
+ };
+
+ //inject ngDialogData to the scope controller
+ newUser = $controller('NewUserModalCtrl', {
+ $scope: $scope,
+ $log: $log,
+ usersService: usersServiceMock,
+ applicationsService: applicationsServiceMock,
+ confirmBoxService: confirmBoxServiceMock
+ });
+
+ //$scope.$apply();
+ //newUser.updateAppsOrder({appId: 39, appRoles: [{id: 13, isApplied: true}]});
+ $scope.$apply();
+
+ // expect(newUser.appsOrder).toEqual([13, 39]);
+ });
+
+
+ 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 = {orgUserId: 'asdfjl'};
+
+ // promisesTestUtils.resolvePromise(usersService, 'getUserAppsRoles', roles);
+ promisesTestUtils.resolvePromise(confirmBoxServiceMock, 'deleteItem', true);
+
+ deferredUsersAppRoles.resolve(roles);
+
+ $scope.ngDialogData = {
+ selectedUser: someUser,
+ dialogState: 2
+ };
+
+ //inject ngDialogData to the scope controller
+ newUser = $controller('NewUserModalCtrl', {
+ $scope: $scope,
+ $log: $log,
+ usersService: usersServiceMock,
+ applicationsService: applicationsServiceMock,
+ confirmBoxService: confirmBoxServiceMock
+ });
+
+ $scope.$apply();
+ newUser.deleteApp(roles.apps[0]);
+ $scope.$apply();
+
+ // expect(newUser.appsOrder).toEqual([20]);
+ });
+
+ 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 = {orgUserId: 'asdfjl'};
+ //promisesTestUtils.resolvePromise(usersServiceMock, 'getUserAppsRoles', roles);
+ //promisesTestUtils.resolvePromise(usersServiceMock, 'updateUserAppsRoles');
+ deferredUsersAppRoles.resolve(roles);
+ deferredUsersAppRoleUpdate.resolve();
+ deferredAdminApps.resolve(roles.apps);
+
+ $scope.ngDialogData = {
+ selectedUser: someUser,
+ dialogState: 2
+ };
+
+ //inject ngDialogData to the scope controller
+ 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-common/client/app/views/users/new-user-dialogs/new-user.modal.html b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.modal.html
new file mode 100644
index 00000000..5f26152b
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.modal.html
@@ -0,0 +1,84 @@
+<!--
+ ================================================================================
+ 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">
+ <button class="btn btn-alt btn-small" id="next-button" ng-click="newUser.selectedUser && newUser.getUserAppsRoles()"
+ ng-class="{disabled: !newUser.selectedUser}">Next
+ </button>
+ <button class="btn btn-alt btn-small" id="cancel-button" ng-click="closeThisDialog()">Cancel</button>
+
+ </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" id="app-name-{{app.name.split(' ').join('-')}}" 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" id="div-app-name-dropdown-{{app.name.split(' ').join('-')}}" 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>
+ <div id="app-item-delete" class="app-item-delete" ng-click="newUser.deleteApp(app)" ng-show="!app.isLoading && !app.isError"></div>
+ <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>
+ <button id="new-user-back-button" class="btn btn-alt btn-small" ng-show="newUser.isShowBack" ng-click="newUser.navigateBack()">Back</button>
+ <button id="new-user-save-button" class="btn btn-alt btn-small" ng-click="newUser.updateUserAppsRoles()"
+ ng-disabled="(newUser.anyChanges == false)">Save
+ </button>
+ <button id="new-user-cancel-button" class="btn btn-alt btn-small" ng-click="closeThisDialog()">Cancel</button>
+ </div>
+
+ </div>
+
+ </div>
+
+
+
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.modal.less b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.modal.less
new file mode 100644
index 00000000..68c23e52
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/new-user-dialogs/new-user.modal.less
@@ -0,0 +1,112 @@
+.new-user-modal {
+ height: 430px;
+
+ .user-apps-roles{
+ .title{
+ //.n18r;
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @portalDBlue 3px solid;
+ }
+
+ input:not([type="button"]) {
+ height: 13px;
+ }
+
+ .app-roles-list{
+ height: 286px;
+ overflow-y: auto;
+
+ .app-item{
+ border: 1px solid @portalLGray;
+ border-radius: 2px;
+ background-color: @funcBkgGray;
+
+ padding: 10px;
+ margin-top: 8px;
+ //margin-right: 6px;
+ //margin-left: 6px;
+
+ .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 @portalLGray;
+ margin-right: 10px;
+ padding-left: 4px;
+ background: @portalWhite;
+ white-space: nowrap;
+
+ }
+ .app-item-right{
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid @portalLGray;
+ background: @portalWhite;
+ vertical-align: middle;
+ }
+
+ .app-item-right-error{
+ .portalRed;
+ padding: 7px 7px 7px 7px;
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid @portalLGray;
+ background: @portalWhite;
+ vertical-align: middle;
+ }
+
+ .app-item-right-contacting{
+ .portalGreen;
+ padding: 7px 7px 7px 7px;
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid @portalLGray;
+ background: @portalWhite;
+ 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 @portalLGray;
+ margin-right: 10px;
+ padding-left: 4px;
+ background: @portalWhite;
+ 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-common/client/app/views/users/users.controller.js b/ecomp-portal-FE-common/client/app/views/users/users.controller.js
new file mode 100644
index 00000000..1aa67601
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/users.controller.js
@@ -0,0 +1,243 @@
+/*-
+ * ================================================================================
+ * 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, confirmBoxService, $scope, ngDialog) {
+ this.$log = $log;
+ $scope.adminAppsIsNull = false;
+ $scope.appsIsDown = false;
+ $scope.noUsersInApp = false;
+ $scope.multiAppAdmin = false;
+
+ $log.info('UsersCtrl:: initializing...');
+ /**
+ * Handle all active HTTP requests
+ * activeRequests @type {Array[requests with cancel option]}
+ */
+ let activeRequests = [];
+ let clearReq = (req) => {
+ activeRequests.splice(activeRequests.indexOf(req), 1);
+ };
+
+ let init = () => {
+ this.isLoadingTable = false;
+ this.selectedApp = null;
+ this.isAppSelectDisabled = false;
+ this.selectApp = 'Select application';
+ this.adminApps = [{index: 0, id: 0, value: this.selectApp, title: this.selectApp}];
+ getAdminApps();
+
+ /*Table general configuration params*/
+ this.searchString = '';
+ /*Table data*/
+ this.usersTableHeaders = ['First Name', 'Last Name', 'User ID', 'Roles'];
+ this.accountUsers = [];
+ };
+
+ let getAdminApps = () => {
+ $log.debug('UsersCtrl::getAdminApps: - Starting getAdminApps');
+ try {
+ this.isLoadingTable = true;
+ let adminAppsReq = applicationsService.getAdminApps();
+ adminAppsReq.promise().then(apps => {
+ if (!apps || !apps.length) {
+ $log.error('UsersCtrl::getAdminApps: - no apps found');
+ return null;
+ }
+ $log.debug('UsersCtrl::getAdminApps: Apps for this user are: ' + JSON.stringify(apps));
+ if (apps.length >= 2) {
+ $log.info('UsersCtrl::getAdminApps: - more than one app for this admin:', apps.length, ' apps');
+ $scope.multiAppAdmin = true;
+ } else {
+ this.adminApps = [] ;
+ }
+ let sortedApps = apps.sort(getSortOrder("name"));
+ let realAppIndex = 1;
+ for(let i=1; i<=sortedApps.length; i++){
+ this.adminApps.push({
+ index: realAppIndex,
+ id: sortedApps[i - 1].id,
+ value: sortedApps[i - 1].name,
+ title: sortedApps[i - 1].name
+ });
+ realAppIndex = realAppIndex + 1;
+ }
+
+ $log.debug('UsersCtrl::getAdminApps: Apps for this user are: ' + JSON.stringify(this.adminApps));
+
+ this.selectedApp = this.adminApps[0];
+ clearReq(adminAppsReq);
+ $scope.adminAppsIsNull = false;
+ }).catch(e => {
+ $scope.adminAppsIsNull = true;
+ $log.error('UsersCtrl::getAdminApps: - getAdminApps() failed = '+ e.message);
+ clearReq(adminAppsReq);
+ confirmBoxService.showInformation('There was a problem retrieving the applications. ' +
+ 'Please try again later.').then(isConfirmed => {});
+
+ }).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;
+ $scope.noUsersInApp = false;
+ // $log.debug('UsersCtrl::updateUsersList: Starting updateUsersList');
+ //reset search string
+ this.searchString = '';
+ //should i disable this too in case of moving between tabs?
+ this.isAppSelectDisabled = true;
+ //activate spinner
+ this.isLoadingTable = true;
+
+ if(this.adminApps!=null && this.selectedApp!=null){
+ var tempSelected = null;
+ for(let i=0; i<=this.adminApps.length; i++){
+ if(typeof this.adminApps[i] != 'undefined' && this.selectedApp.value==this.adminApps[i].value){
+ tempSelected=_.clone(this.adminApps[i]);
+ }
+ }
+ if(tempSelected!=null){
+ this.selectedApp= tempSelected;
+ }
+ }
+
+ if (this.selectedApp.title != this.selectApp) { // 'Select Application'
+ usersService.getAccountUsers(this.selectedApp.id)
+ .then(accountUsers => {
+ $log.debug('UsersCtrl::updateUsersList accountUsers: '+ accountUsers);
+ if (angular.isObject(accountUsers)===false) {
+ $log.error('UsersCtrl::updateUsersList accountUsers: App is down!');
+ $scope.appsIsDown = true;
+ }
+ $log.debug('UsersCtrl::updateUsersList length: '+ Object.keys(accountUsers).length);
+ this.isAppSelectDisabled = false;
+ this.accountUsers = accountUsers;
+ if (angular.isObject(accountUsers) && Object.keys(accountUsers).length === 0) {
+ $log.debug('UsersCtrl::updateUsersList accountUsers: App has no users.');
+ $scope.noUsersInApp = true;
+ }
+ }).catch(err => {
+ this.isAppSelectDisabled = false;
+ $log.error('UsersCtrl::updateUsersList error: ' + err);
+ confirmBoxService.showInformation('There was a problem updating the users List. ' +
+ 'Please try again later.').then(isConfirmed => {});
+ $scope.appsIsDown = true;
+ }).finally(() => {
+ this.isLoadingTable = false;
+ $scope.noAppSelected = false;
+ });
+ } else {
+ // this.selectedApp = this.adminApps[0];
+ this.isAppSelectDisabled = false;
+ this.isLoadingTable = false;
+ $scope.noUsersInApp = false;
+ $scope.noAppSelected = true;
+ }
+ };
+
+
+ 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.openBulkUserUploadModal = (adminApps) => {
+ let data = null;
+ if (adminApps) {
+ data = {
+ dialogState: 3,
+ selectedApplication: {
+ appid: adminApps[0].appid,
+ appName: adminApps[0].appName
+ }
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/users/new-user-dialogs/bulk-user.modal.html',
+ controller: 'BulkUserModalCtrl',
+ controllerAs: 'bulkUser',
+ data: data
+ }).closePromise.then(needUpdate => {
+ this.updateUsersList();
+ });
+ };
+
+
+ $scope.$watch('users.selectedApp.value', (newVal, oldVal) => {
+ if (!newVal || _.isEqual(newVal, oldVal)) {
+ return;
+ }
+ $log.debug('UsersCtrl::openAddNewUserModal:$watch selectedApp -> Fire with: ', newVal);
+ this.accountUsers = []; //reset table and show swirl here
+ this.updateUsersList();
+ });
+
+ $scope.$on('$destroy', () => {
+ //cancel all active requests when closing the modal
+ activeRequests.forEach(req => {
+ req.cancel();
+ });
+ });
+
+ init();
+ }
+ }
+ UsersCtrl.$inject = ['$log', 'applicationsService', 'usersService', 'confirmBoxService', '$scope', 'ngDialog'];
+ angular.module('ecompApp').controller('UsersCtrl', UsersCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/users/users.controller.spec.js b/ecomp-portal-FE-common/client/app/views/users/users.controller.spec.js
new file mode 100644
index 00000000..96231163
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/users.controller.spec.js
@@ -0,0 +1,141 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+// /**
+// * Created by nnaffar on 12/17/15.
+// */
+// 'use strict';
+//
+// describe('Controller: UsersCtrl ', () => {
+// beforeEach(module('ecompApp'));
+//
+// //destroy $http default cache before starting to prevent the error 'default cache already exists'
+// beforeEach(inject((_CacheFactory_)=> {
+// _CacheFactory_.destroyAll();
+// }));
+//
+// let users, $controller, $q, $rootScope, $log, $scope;
+//
+// beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_)=> {
+// [$controller, $q, $rootScope, $log] = [_$controller_, _$q_, _$rootScope_, _$log_];
+// }));
+//
+// let applicationsServiceMock, usersServiceMock;
+// let deferredAdminApps, deferredUsersAccounts;
+// beforeEach(()=> {
+// [deferredAdminApps, deferredUsersAccounts] = [$q.defer(), $q.defer()];
+//
+// applicationsServiceMock = {
+// getAdminApps: () => {
+// var promise = () => {return deferredAdminApps.promise};
+// var cancel = jasmine.createSpy();
+// return {
+// promise: promise,
+// cancel: cancel
+// }
+// }
+// };
+//
+// usersServiceMock = jasmine.createSpyObj('usersServiceMock', ['getAccountUsers']);
+//
+// //applicationsServiceMock.getAdminApps().promise().and.returnValue(deferredAdminApps.promise);
+// usersServiceMock.getAccountUsers.and.returnValue(deferredUsersAccounts.promise);
+//
+// $scope = $rootScope.$new();
+// users = $controller('UsersCtrl', {
+// $log: $log,
+// applicationsService: applicationsServiceMock,
+// usersService: usersServiceMock,
+// $scope: $scope
+// });
+// $scope.users = users;
+// });
+//
+// //MOCKS
+// let appsListMock = [
+// {value: 'SSP', title: 'SSP', id: 3},
+// {value: 'ASDC', title: 'ASDC', id: 23},
+// {value: 'Formation', title: 'Formation', id: 223}
+// ];
+//
+// let usersListMock = [
+// {
+// "orgUserId": "nn605g",
+// "firstName": "Nabil",
+// "lastName": "Naffar",
+// "roles": [
+// {
+// "roleId": 1,
+// "roleName": "Standard user"
+// },
+// {
+// "roleId": 9,
+// "roleName": "Super standard user"
+// },
+// {
+// "roleId": 2,
+// "roleName": "Super duper standard user"
+// }
+// ]
+// }];
+// let secondUsersListMock = [
+// {
+// "orgUserId": "sadf7",
+// "firstName": "John",
+// "lastName": "Hall",
+// "roles": [
+// {
+// "roleId": 1,
+// "roleName": "Standard user"
+// },
+// {
+// "roleId": 2,
+// "roleName": "Super duper standard user"
+// }
+// ]
+// }];
+//
+// it('should get all user\'s administrated applications when initializing the view', ()=> {
+// deferredAdminApps.resolve(appsListMock);
+// deferredUsersAccounts.resolve(usersListMock);
+// $scope.$apply();
+// expect(users.adminApps).toEqual(appsListMock);
+// expect(users.selectedApp).toEqual(appsListMock[0]);
+// });
+//
+// it('should get first application users by default when initializing the view', () => {
+// $scope.$apply();
+// deferredAdminApps.resolve(appsListMock);
+// deferredUsersAccounts.resolve(usersListMock);
+// $scope.$apply();
+// expect(users.accountUsers).toEqual(usersListMock);
+// });
+//
+// it('should get application users when changing application', () => {
+// $scope.$apply();
+// deferredAdminApps.resolve(appsListMock);
+// $scope.$apply();
+//
+// users.selectedApp = appsListMock[1];
+// deferredUsersAccounts.resolve(secondUsersListMock);
+// $scope.$apply('users');//change app
+//
+// expect(users.accountUsers).toEqual(secondUsersListMock);
+// });
+// });
diff --git a/ecomp-portal-FE-common/client/app/views/users/users.less b/ecomp-portal-FE-common/client/app/views/users/users.less
new file mode 100644
index 00000000..7a0e9ebb
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/users.less
@@ -0,0 +1,47 @@
+.users-page-main{
+ .bg_portalWhite;//white for 1702
+ //.bg_portalGray; // 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;
+#input-table-search::-webkit-input-placeholder,
+{
+font-style: italic;
+ color: #D7D7D7;
+}
+ .users-table {
+ width: @table-width;
+ //margin-left: @table-margin-left;
+ margin: 0 auto;
+
+ }
+
+ .error-text {
+ width: 1170px;
+ margin: auto;
+ padding: 10px;
+ left: 20px;
+ font-weight: bold;
+ font-size: 16px;
+ text-align: left;
+ color: @err;
+ // background-color: @portalWhite;
+ background-color: @u;
+
+ .error-help {
+ color: @o; //@portalDGray;
+ font-weight: normal;
+ }
+
+ .error-help-bold {
+ color: @o; //@portalDGray;
+ font-weight: bold;
+ }
+
+ }
+}
diff --git a/ecomp-portal-FE-common/client/app/views/users/users.tpl.html b/ecomp-portal-FE-common/client/app/views/users/users.tpl.html
new file mode 100644
index 00000000..ff3edde0
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/users/users.tpl.html
@@ -0,0 +1,98 @@
+<!--
+ ================================================================================
+ 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="page-content">
+ <div id="users-page-title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page" >Users</h1>
+ </div>
+ <div class="users-table">
+ <div class="table-control">
+ <div class="table-control-fields">
+ <div class="table-dropdown">
+ <select id="dropdown1" name="dropdown1" b2b-dropdown placeholder-text="Select Application" ng-model="users.selectedApp.value">
+ <option b2b-dropdown-list option-repeat="d in users.adminApps" value="{{d.value}}">{{d.title}}</option>
+ </select>
+ </div>
+ <div>
+ <input id="input-table-search" placeholder="Search" class="table-search-field" type="text" data-ng-model="users.searchString">
+ </div>
+ <button class="btn btn-alt btn-small" ng-click="users.openAddNewUserModal()"><i class="icon-people-userbookmark" aria-hidden="true"></i>&nbsp;Add User</button>
+ <button class="btn btn-alt btn-small" ng-click="users.openBulkUserUploadModal()"><i class="icon-arrows-upload" aria-hidden="true"></i>&nbsp;Bulk Upload</button>
+ </div>
+ </div>
+ <div ng-hide="users.isLoadingTable">
+ <div class="error-text" id="div-select-app" ng-show="noAppSelected">
+ <p class="error-help">Use the 'Select application' dropdown to see users.</p>
+ </div>
+ <div class="error-text"
+ id="div-error-no-users"
+ ng-show="noUsersInApp">
+ <p>&nbsp;</p>
+ <p class="error-help">
+ No users found. Select "Add User" to add a User to the application.
+ </p>
+ </div>
+ <div class="error-text"
+ id="div-error-app-down"
+ ng-show="appsIsDown" >
+ <p>&nbsp;</p>
+ <p class="error-help">
+ Failed to communicate with the application.
+ Please try again later or contact a system administrator.
+ </p>
+ </div>
+ </div>
+ <span class="ecomp-spinner" ng-show="users.isLoadingTable"></span>
+ <div b2b-table table-data="users.accountUsers" ng-hide="users.isLoadingTable" search-string="users.searchString" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr >
+ <th b2b-table-header key="firstName" sortable="true" id="col1">First Name</th>
+ <th b2b-table-header key="lastName" sortable="true" id="col2">Last Name</th>
+ <th b2b-table-header key="orgUserId" sortable="true" id="col3">User ID</th>
+ <th b2b-table-header key="" sortable="falses" id="col4">Roles</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" row-repeat="rowData in users.accountUsers">
+ <tr ng-click="users.openAddNewUserModal(rowData)">
+ <td b2b-table-body id="rowheader_t1_{{$index}}" headers="col1" ng-bind="rowData.firstName"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col2" ng-bind="rowData.lastName"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col3" ng-bind="rowData.orgUserId"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col4">
+ <div class="ecomp-table-repeat" 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>&nbsp;</p>
+ <p class="error-help">It appears that you have not been added as an admin yet to an application.</p>
+ <p>&nbsp;</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-common/client/app/views/widget-catalog/widget-catalog.controller.js b/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.controller.js
new file mode 100644
index 00000000..2b0cc5cf
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.controller.js
@@ -0,0 +1,350 @@
+/*-
+ * ================================================================================
+ * 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 WidgetCatalogCtrl {
+ constructor(widgetsService, $log, $cookies, $scope, conf, beReaderService, widgetsCatalogService, userProfileService,dashboardService,$compile, ngDialog) {
+
+ $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();
+ }
+ });
+ };
+
+ $scope.WidgetCatView = [];
+ $scope.applyPresentationDetailsToWidgetsCatalog = function(widgetsReturned){
+ var rowNo = 0;
+ for (var i = 0; i < widgetsReturned.length; i++) {
+ $scope.WidgetCatView[i] = {
+ sizeX : 2,
+ sizeY : 2,
+ headerText:'',
+ widgetIdentifier : '',
+ url : '',
+ widgetid: '',
+ attrb:'',
+ select: false,
+ };
+ $scope.WidgetCatView[i].widgetid = widgetsReturned[i].id;
+ $scope.WidgetCatView[i].headerText = widgetsReturned[i].headerName;
+
+ if(widgetsReturned[i].headerName === 'widget-news'){
+ $scope.WidgetCatView[i].widgetIdentifier = 'NEWS';
+ }
+ else
+ if(widgetsReturned[i].headerName === 'widget-resources'){
+ $scope.WidgetCatView[i].widgetIdentifier = 'IMPORTANTRESOURCES';
+ }
+ else
+ if(widgetsReturned[i].headerName === 'widget-events'){
+ $scope.WidgetCatView[i].widgetIdentifier = 'EVENTS';
+ }
+
+ $scope.WidgetCatView[i].url = widgetsReturned[i].url;
+ $scope.WidgetCatView[i].attrb = widgetsReturned[i].attrs;
+ $scope.WidgetCatView[i].select = widgetsReturned[i].select;
+ }
+
+ $scope.widgetViewData = $scope.WidgetCatView;
+
+ }
+
+ /** Widget code starts */
+ let getUserWidgets = (loginName) => {
+
+ this.isCommError = false;
+ var conf = this.conf;
+ widgetsCatalogService.getUserWidgets(loginName).then(res => {
+
+ if(!(res instanceof Array)){
+ this.isCommError = true;
+ return;
+ }
+ for(var i = 0; i < res.length; i++){
+ var widget_id = res[i][0];
+ var widget_name = res[i][1];
+ let url = this.conf.api.widgetCommon + "/" + widget_id + "/framework.js";
+ $scope.widgetsList.push({
+ id: widget_id,
+ name: widget_name,
+ headerName: widget_name,
+ url: url,
+ attrs: [{attr: 'data-' + widget_id, value: ''}],
+ status: res[i][4],
+ select: (res[i][4] == 'S' || res[i][4] === null) ? true : false
+ });
+ var script = document
+ .createElement('script');
+ script.src = url;
+ script.async = true;
+ var entry = document
+ .getElementsByTagName('script')[0];
+ entry.parentNode
+ .insertBefore(script, entry);
+ }
+ $scope.applyPresentationDetailsToWidgetsCatalog($scope.widgetsList);
+ }).catch(err => {
+ $log.error('WidgetCatalogCtrl::getUserWidgets caught error', err);
+ }).finally(()=> {
+
+ });
+ };
+
+ let init = () => {
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ // $log.info('WidgetCatalogCtrl::getUserProfile: ',
+ // profile);
+ $scope.orgUserId = profile.orgUserId;
+ $scope.widgetsViewData = [];
+ $scope.widgetsView = [];
+ getUserWidgets($scope.orgUserId);
+ });
+ this.conf = conf;
+ $scope.widgetsList = [];
+ };
+
+ /** Widget code ends */
+
+ $scope.activateThis = function(ele){
+ $compile(ele.contents())($scope);
+ $scope.$apply();
+ };
+ $scope.setCommonWidget = function() {
+ /* News Events Resources */
+ var widgetLength = ($scope.widgetsViewData==null || $scope.widgetsViewData.length==0) ? 0:$scope.widgetsViewData.length;
+ $scope.widgetsViewData[widgetLength] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: 'News',
+ width: '',
+ height: '',
+ url: '',
+ selected:true
+ };
+ $scope.widgetsViewData[widgetLength+1] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: 'Calendar Events',
+ width: '',
+ height: '',
+ url: '',
+ selected:true
+ };
+ $scope.widgetsViewData[widgetLength+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);
+ });
+ }
+ $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);
+ });
+ }
+ $scope.updateEvents();
+ /* Setting Important Resources data */
+
+ $scope.importResData = [];
+ $scope.updateImportRes = function() {
+ $scope.importResData.length=0;
+ dashboardService.getCommonWidgetData('IMPORTANTRESOURCES').then(
+ function(res) {
+ // $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);
+ });
+ }
+ $scope.updateImportRes();
+
+ /** ******End hardcoded news events and resources*************** */
+
+ }
+
+ $scope.newsGridsterItem = {
+ headerText : 'Test',
+ subHeaderText : ''
+ };
+
+ $scope.newsGridsterItem = {
+ headerText : 'News',
+ subHeaderText : ''
+ };
+
+ $scope.eventsGridsterItem = {
+ headerText : 'Events',
+ subHeaderText : ''
+ };
+
+ $scope.impoResGridsterItem = {
+ headerText : 'Resources',
+ subHeaderText : ''
+ };
+
+ this.gridsterOpts = {
+ columns: 6,
+ colWidth: 190,
+ rowHeight: 190,
+ margins: [20, 20],
+ outerMargin: true,
+ pushing: true,
+ floating: true,
+ swapping: true,
+ draggable: {
+ handle: '.icon-content-gridguide'
+ }
+ };
+
+ // Run this function when user clicks on checkbox.
+ $scope.storeSelection = function(widget) {
+
+ // not needed as only 'SHOW' and 'HIDE' status_cd is expected from the micro service now
+ /*var pendingFlag = false;
+ if(widget.access)
+ pendingFlag = false;
+ else
+ pendingFlag = widget.pending; */
+
+ var appData = {
+ widgetId: widget.widgetid,
+ select : widget.select,
+ // pending : pendingFlag
+ };
+
+ widgetsCatalogService.updateWidgetCatalog(appData).then(
+ function(result) {
+ // $log.debug('CatalogCtrl:storeSelection result is ', result);
+ })['catch'](function(err) {
+ $log.error('CatalogCtrl:storeSelection: exception: ', err);
+ });
+ };
+
+ init();
+ }
+ }
+
+
+
+ WidgetCatalogCtrl.$inject = ['widgetsService', '$log', '$cookies', '$scope', 'conf', 'beReaderService', 'widgetsCatalogService', 'userProfileService','dashboardService','$compile','ngDialog'];
+ angular.module('ecompApp').controller('WidgetCatalogCtrl', WidgetCatalogCtrl);
+
+ 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
+ };
+ } ]);
+
+})();
+
+
+app.directive('dynAttr', function() {
+ return {
+ scope: { list: '=dynAttr' },
+ link: function(scope, elem, attrs){
+ for(var attr in scope.list){
+ elem.attr(scope.list[attr].attr, scope.list[attr].value);
+ }
+ }
+ };
+});
diff --git a/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.controller.spec.js b/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.controller.spec.js
new file mode 100644
index 00000000..77659d93
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.controller.spec.js
@@ -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.
+ * ================================================================================
+ */
+
diff --git a/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.less b/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.less
new file mode 100644
index 00000000..65a74a5f
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.less
@@ -0,0 +1,156 @@
+.widget-catalog{
+ .bg_portalWhite;//white for 1702
+ //.bg_portalGray; // 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;
+ margin: auto;
+
+ .widget-title {
+ padding-left: 20px;
+ margin-bottom: -15px;
+ }
+
+ .widget-gridster-header {
+ color: #0574ac;
+ font-family: Omnes-ECOMP-W02-Bold, Arial;
+ font-size: 18px;
+ }
+
+ .widget-gridster-footer {
+ background-color: @portalWhite
+ }
+
+ .widget-iframe {
+ width: 100%;
+ height: 100%;
+ }
+
+ .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 @funcBkgGray;
+ }
+
+ .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%;
+ //margin-left: @table-margin-left;
+
+ .portals-list {
+ min-height: 70vh;
+ //display: flex;
+ //justify-content: center;
+ flex-flow: row wrap;
+ width: @table-width;
+ margin:auto;
+ //margin-left: @table-margin-left;
+ margin-bottom: 63px;
+
+ .portals-list-item {
+ background-color: @portalWhite;
+ 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: @portalWhite;
+ height: 50px;
+ top: 180px;
+ // position: relative;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ padding: 16px;
+
+ .info-title {
+ //.a24r;
+ .dBlue24r; // AT&T Dark Blue
+ margin-bottom: 4px;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ .info-description {
+ .portalDBlue16r;
+ 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-y:auto;
+ overflow-x:hidden;
+}
+
+@keyframes fadein {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+#widget-page-content{
+ padding-left:250px;
+}
+
+.widget-centerAlignment{
+margin: auto;
+width: 1170px;
+}
+#widget-checkbox-label{
+margin-left: 190px;
+top: 3px;
+}
diff --git a/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.tpl.html b/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.tpl.html
new file mode 100644
index 00000000..1b77a28c
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-catalog/widget-catalog.tpl.html
@@ -0,0 +1,76 @@
+<!--
+ ================================================================================
+ 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="widget-catalog" id="widget-page-content">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page">Widget Catalog</h1>
+ </div>
+ <div class="widget-centerAlignment">
+ <div class="portals-list">
+ <div ng-show="widgetCatalog.isCommError">Failed to
+ communicate with the widget microservice.</div>
+ <div id="widgets" class="information-section"
+ ng-hide="widgetCatalog.isCommError">
+ <div id="news-gridster-container"
+ class="gridster-container override_background">
+ <div class="content" gridster="widgetCatalog.gridsterOpts">
+ <ul>
+ <li gridster-item="widget" ng-repeat="widget in widgetViewData">
+ <div class="box">
+ <div class="box-header">
+ <i style="cursor: move;" class="icon-content-gridguide"></i>
+ <h3>{{ widget.headerText}}</h3>
+ <div class="form-row">
+ <label id="widget-checkbox-label" class="checkbox"> <input type="checkbox"
+ id="{{widget.headerText.split(' ').join('-')}}-checkbox"
+ ng-model="widget.select"
+ ng-change="storeSelection(widget)"> <i
+ class="skin"></i>
+ </label>
+ </div>
+ </div>
+ <div class="box-content">
+ <div dyn-attr="widget.attrb"></div>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ <div ng-show="widgetCatalog.isEnlarged">
+ <div id="widgets-button-enlarge-close"
+ class="enlarge-close-button"
+ ng-click="widgetCatalog.isEnlarged=false">X</div>
+ <iframe id="iframe-widget-enlarge-close"
+ ng-src="{{widgetCatalog.enlargeURL | trusted}}"
+ class="enlarge-content"> </iframe>
+ </div>
+ </div>
+ </div>
+
+ <div id="widgets-disclaimer" class="w-ecomp-main-disclaimer">
+ To request access to an application widget, please visit the <a
+ ng-href="getAccess">Get Access</a> page.
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.controller.js b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.controller.js
new file mode 100644
index 00000000..0a9dd12c
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.controller.js
@@ -0,0 +1,381 @@
+/*-
+ * ================================================================================
+ * 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 WidgetOnboardingDetailsModalCtrl {
+ constructor($scope, $log, $interval, applicationsService, adminsService, microserviceService, widgetsCatalogService, errorMessageByCode, ECOMP_URL_REGEX, $window,userProfileService, confirmBoxService, $cookies) {
+
+ this.appUpdate = function(){
+ this.hasSelectedApp = false;
+ this.appCounter = 0;
+ for(var i = 0; i < this.availableApps.length; i++){
+ if(this.availableApps[i].isSelected){
+ this.appCounter++;
+ if(!this.hasSelectedApp)
+ this.hasSelectedApp = true;
+ }
+ if(this.availableApps[i].isSelected
+ && this.availableApps[i].roles.length == 0){
+ var index = i;
+ this.availableRoles = [];
+ adminsService.getRolesByApp(this.availableApps[i].id).then(roles => {
+ for(var i = 0; i < roles.length; i++){
+ this.availableRoles.push({
+ id: roles[i].id,
+ name: roles[i].name,
+ isSelected: false,
+ });
+ }
+ this.availableApps[index].roles = this.availableRoles;
+ });
+ }
+ }
+ this.allRoleSelected = true;
+ this.checkRoleSelected();
+ }
+
+ this.roleUpdate = function(app){
+ this.allRoleSelected = true;
+ for(var i = 0; i < app.roles.length; i++){
+ if(app.roles[i].isSelected){
+ app.roleSelected = true;
+ this.checkRoleSelected();
+ return;
+ }
+ }
+ app.roleSelected = false;
+ this.checkRoleSelected();
+ }
+
+ this.checkRoleSelected = function(){
+ for(var i = 0; i < this.availableApps.length; i++){
+ if(this.availableApps[i].isSelected
+ && !this.availableApps[i].roleSelected){
+ this.allRoleSelected = false;
+ return;
+ }
+ }
+ }
+
+ this.getAppName = function(appId){
+ for(var i = 0; i < this.availableApps.length; i++){
+ if(this.availableApps[i].id == appId){
+ return this.availableApps[i].name;
+ }
+ }
+ }
+
+ let newWidgetModel = {
+ name: null,
+ roleId: null,
+ roleName: null,
+ appId: null,
+ appName: null,
+ url: null,
+ showAppOptions: false,
+ showRoleOptions: false,
+ hasSelectedApp: false
+ };
+
+ let getAvailableApps = () => {
+
+ if(this.isEditMode == false){
+ applicationsService.getAppsForSuperAdminAndAccountAdmin().then(apps => {
+ this.availableApps=[];
+ for(var i=0;i<apps.length;i++) {
+ if (!apps[i].restrictedApp) {
+ this.availableApps.push({
+ id: apps[i].id,
+ name: apps[i].name,
+ roles: [],
+ roleSelected: false,
+ isSelected: false,
+ });
+ }
+ }
+ }).catch(err => {
+ $log.error(err);
+ });
+ }
+ else if(this.isEditMode == true){
+ if(this.widget.allowAllUser == "Y")
+ this.widget.allUser = true;
+ applicationsService.getAppsForSuperAdminAndAccountAdmin().then(apps => {
+ this.availableApps=[];
+ let selectedApps = {};
+ var availableApps = this.availableApps;
+ this.allRoleSelected = true;
+ for(var i=0; i < this.widget.widgetRoles.length; i++){
+ if(selectedApps[this.widget.widgetRoles[i].app.appId] != undefined)
+ selectedApps[this.widget.widgetRoles[i].app.appId] += this.widget.widgetRoles[i].roleId + ";" + this.widget.widgetRoles[i].roleName + ";";
+ else{
+ selectedApps[this.widget.widgetRoles[i].app.appId] = this.widget.widgetRoles[i].roleId + ";" + this.widget.widgetRoles[i].roleName + ";";
+ this.appCounter++;
+ }
+ }
+ apps.forEach(function(app, index){
+ availableApps.push({
+ id: app.id,
+ name: app.name,
+ roles: [],
+ roleSelected: false,
+ isSelected: false,
+ });
+ if(selectedApps[app.id] != undefined){
+ adminsService.getRolesByApp(app.id).then(roles => {
+ var role = selectedApps[app.id].split(';');
+ var selectedRoles = [];
+ var n = 0;
+ while((n+1) < role.length){
+ selectedRoles.push({
+ id: role[n++],
+ name: role[n++],
+ isSelected: true,
+ });
+ }
+ for(var m = 0; m < roles.length; m++){
+ var hasSelected = true;
+ for(var n = 0; n < selectedRoles.length; n++){
+ if(selectedRoles[n].id == roles[m].id){
+ hasSelected = false;
+ break;
+ }
+ }
+ if(hasSelected){
+ selectedRoles.push({
+ id: roles[m].id,
+ name: roles[m].name,
+ isSelected: false,
+ });
+ }
+ }
+ availableApps[index].roleSelected = true;
+ availableApps[index].isSelected = true;
+ availableApps[index].roles = selectedRoles;
+ });
+ }
+ })
+ })
+ }
+ };
+
+ let getAvailableServices = () =>{
+ microserviceService.getServiceList().then(services => {
+ this.availableServices = [];
+ for(var i = 0; i < services.length; i++){
+ this.availableServices.push({
+ id: services[i].id,
+ name: services[i].name,
+ option: services[i].name + ": " + services[i].url
+ });
+
+ if(this.widget.service != null && this.widget.service.id == services[i].id){
+ this.widget.serviceURL = this.availableServices[i];
+ }
+ }
+ }).catch(err => {
+ $log.error(err);
+ });
+ }
+
+
+ let init = () => {
+ $log.info('WidgetOnboardingDetailsModalCtrl::init');
+ this.widgetsList = [];
+ this.duplicatedName = true;
+ this.allRoleSelected = false;
+ this.appCounter = 0;
+ this.isSaving = false;
+ this.allUser = false;
+ this.emptyWidgetName = false;
+
+ if ($scope.ngDialogData && $scope.ngDialogData.widget) {
+ this.isEditMode = true;
+ this.allRoleSelected = true;
+ this.widget = _.clone($scope.ngDialogData.widget);
+ } else {
+ this.isEditMode = false;
+ this.widget = _.clone(newWidgetModel);
+ }
+
+ widgetsCatalogService.getManagedWidgets().then(res => {
+ for(var i = 0; i < res.length; i++){
+ this.widgetsList.push(res[i].name);
+ }
+ }).catch(err => {
+ $log.error('WidgetOnboardingDetailsModalCtrl::init error: ' + err);
+ }).finally(()=> {
+ this.isLoadingTable = false;
+ });
+ getAvailableApps();
+ getAvailableServices();
+ };
+ this.ECOMP_URL_REGEX = ECOMP_URL_REGEX;
+ this.conflictMessages = {};
+ this.scrollApi = {};
+
+ let resetConflict = fieldName => {
+ delete this.conflictMessages[fieldName];
+ if($scope.widgetForm[fieldName]){
+ $scope.widgetForm[fieldName].$setValidity('conflict', true);
+ }
+ };
+
+ this.updateSelectedRole = () => {
+ if (!this.selectedRole) {
+ return;
+ }
+ this.widget.RoleId = this.selectedRole.id;
+ this.widget.RoleName = this.selectedRole.name;
+ };
+
+ let emptyCookies = () => {
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ $log.info('AppDetailsModalCtrl::emptyCookies profile: ', profile);
+ $scope.orgUserId = profile.orgUserId;
+ $log.info('user has the following orgUserId: ' + profile.orgUserId);
+ if ($cookies.getObject($scope.orgUserId + '_widget') != undefined && $cookies.getObject($scope.orgUserId + '_widget') != null) {
+ $cookies.remove($scope.orgUserId + '_widget');
+ }
+ });
+ };
+
+ this.updateWidgetName = () => {
+ for(var i = 0; i < this.widgetsList.length; i++){
+ if(this.widget.name.toUpperCase() == this.widgetsList[i].toUpperCase()){
+ this.duplicatedName = false;
+ return;
+ }
+ }
+ this.duplicatedName = true;
+ };
+
+ this.saveChanges = () => {
+
+ if(!this.isEditMode)
+ this.updateWidgetName();
+
+ if(this.duplicatedName == false
+ || this.widget.name == ''
+ || this.widget.name == undefined){
+ this.emptyWidgetName = true;
+ return;
+ }
+
+ if((this.widget.file == undefined && !this.isEditMode) ||
+ (!this.widget.allUser && this.appCounter == 0) ||
+ this.widget.name == null ||
+ (!this.widget.allUser && !this.allRoleSelected) ||
+ this.widget.saving == true)
+ return;
+
+
+ this.widget.saving = true;
+ var selectedRoles = [];
+ if(!this.widget.allUser){
+ for(var i = 0; i < this.availableApps.length; i++){
+ if(this.availableApps[i].isSelected){
+ for(var n = 0; n < this.availableApps[i].roles.length; n++) {
+ if(this.availableApps[i].roles[n].isSelected){
+ var role = {
+ app: {
+ appId: this.availableApps[i].id
+ },
+ roleId: this.availableApps[i].roles[n].id,
+ roleName: this.availableApps[i].roles[n].name,
+ };
+ selectedRoles.push(role);
+ }
+ }
+ }
+ }
+ }
+
+ var allowAllUser = 0;
+ if(this.widget.allUser)
+ allowAllUser = 1;
+
+ var serviceId = null;
+ if(this.widget.serviceURL != null &&
+ this.widget.serviceURL != undefined){
+ serviceId = this.widget.serviceURL.id;
+ }
+
+
+ var file_loc = this.widget.name + ".zip";
+ var newWidget = {
+ name: this.widget.name,
+ desc: this.widget.desc,
+ widgetRoles: selectedRoles,
+ fileLocation: file_loc,
+ allowAllUser: allowAllUser,
+ serviceId: serviceId
+ };
+
+ if(this.isEditMode){
+
+ if(this.widget.file != undefined){
+ widgetsCatalogService.updateWidgetWithFile(this.widget.file, this.widget.id, newWidget).then(res => {
+ if(!res.valid){
+ if(!res.error){
+ confirmBoxService.showInformation("Could not save. Please retry.");
+ this.widget.saving = false;
+ return;
+ }
+ confirmBoxService.showInformation(res.error);
+ this.widget.saving = false;
+ return;
+ }
+ $scope.closeThisDialog(true);
+ this.widget.saving = false;
+ });
+ }
+ else{
+ widgetsCatalogService.updateWidget(this.widget.id, newWidget)
+ .then(() => {
+ $scope.closeThisDialog(true);
+ });
+ }
+ }
+ else{
+ widgetsCatalogService.createWidget(newWidget, this.widget.file).then(res => {
+ if(!res.valid){
+ if(!res.error)
+ confirmBoxService.showInformation("Could not save. Please retry.");
+ else
+ confirmBoxService.showInformation(res.error);
+ this.widget.saving = false;
+ return;
+ }
+ $scope.closeThisDialog(true);
+ this.widget.saving = false;
+ });
+ }
+ };
+ init();
+ $scope.$on('$stateChangeStart', e => {
+ e.preventDefault();
+ });
+ }
+ }
+ WidgetOnboardingDetailsModalCtrl.$inject = ['$scope', '$log', '$interval', 'applicationsService', 'adminsService', 'microserviceService', 'widgetsCatalogService', 'errorMessageByCode', 'ECOMP_URL_REGEX', '$window','userProfileService', 'confirmBoxService', '$cookies'];
+ angular.module('ecompApp').controller('WidgetOnboardingDetailsModalCtrl', WidgetOnboardingDetailsModalCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.controller.spec.js b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.controller.spec.js
new file mode 100644
index 00000000..1762fadb
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.controller.spec.js
@@ -0,0 +1,154 @@
+/*-
+ * ================================================================================
+ * 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: WidgetDetailsModalCtrl', ()=> {
+// /**
+// * INITIALIZATION
+// */
+// beforeEach(module('testUtils'));
+// beforeEach(module('ecompApp'));
+//
+// let promisesTestUtils;
+// //destroy $http default cache before starting to prevent the error 'default cache already exists'
+// //_promisesTestUtils_ comes from testUtils for promises resolve/reject
+// beforeEach(inject((_CacheFactory_, _promisesTestUtils_)=> {
+// _CacheFactory_.destroyAll();
+// promisesTestUtils = _promisesTestUtils_;
+// }));
+//
+// let widgetDetails, scope, $controller, $q, $rootScope, $log, widgetsService, errorMessageByCode, ECOMP_URL_REGEX;
+// let deferredAdminApps, deferredUserProfile;
+// let applicationsServiceMock, widgetsServiceMock, userProfileServiceMock;
+// beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_)=> {
+// [$controller, $q, $rootScope, $log] = [_$controller_, _$q_, _$rootScope_, _$log_];
+//
+// deferredAdminApps = $q.defer();
+// deferredUserProfile = $q.defer();
+// /*applicationsServiceMock = {
+// getAppsForSuperAdminAndAccountAdmin: () => {
+// var promise = () => {return deferredAdminApps.promise};
+// var cancel = jasmine.createSpy();
+// return {
+// promise: promise,
+// cancel: cancel
+// }
+// }
+// };*/
+//
+// widgetsServiceMock = {
+// updateWidget: () => {
+// var promise = () => {return deferredAdminApps.promise};
+// var cancel = jasmine.createSpy();
+// return {
+// promise: promise,
+// cancel: cancel
+// }
+// },
+// createWidget: () => {
+// var promise = () => {return deferredAdminApps.promise};
+// var cancel = jasmine.createSpy();
+// return {
+// promise: promise,
+// cancel: cancel
+// }
+// }
+// };
+//
+// userProfileServiceMock = jasmine.createSpyObj('userProfileServiceMock',['getUserProfile']);
+// userProfileServiceMock.getUserProfile.and.returnValue(deferredUserProfile.promise);
+//
+// applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock',['getAppsForSuperAdminAndAccountAdmin']);
+// applicationsServiceMock.getAppsForSuperAdminAndAccountAdmin.and.returnValue(deferredAdminApps.promise);
+//
+// }));
+//
+// beforeEach(()=> {
+// errorMessageByCode = [];
+// ECOMP_URL_REGEX = "";
+// scope = $rootScope.$new();
+// createController(scope);
+// });
+//
+// let createController = scopeObj => {
+// widgetDetails = $controller('WidgetDetailsModalCtrl', {
+// $scope: scope,
+// $log: $log,
+// applicationsService: applicationsServiceMock,
+// widgetsService: widgetsServiceMock,
+// errorMessageByCode: errorMessageByCode,
+// ECOMP_URL_REGEX: ECOMP_URL_REGEX,
+// userProfileService: userProfileServiceMock
+// });
+// };
+//
+// /**
+// * MOCK DATA
+// */
+// let newWidgetModel = {
+// name: null,
+// appId: null,
+// appName: null,
+// width: 360,
+// height: 300,
+// url: null
+// };
+// let exsistingWidget = {
+// name: 'some widget',
+// appId: 1,
+// appName: 'APP NAME',
+// width: 360,
+// height: 300,
+// url: 'http://a.com'
+// };
+// let adminApps = [{id: 1, name: 'a'}, {id: 2, name: 'b'}];
+//
+// /**
+// * TEST CASES
+// */
+// it('should initialize controller with new widget mode when opening the modal without selected widget', ()=> {
+// expect(widgetDetails.widget).toEqual(newWidgetModel);
+// });
+//
+// it('should initialize controller with exsisting widget details when opening the modal with selected widget', ()=> {
+// scope.ngDialogData = {
+// widget: exsistingWidget
+// };
+// createController(scope);
+// expect(widgetDetails.widget).toEqual(exsistingWidget);
+// });
+//
+// it('should populate widget selected app name and id when initializing controller with widget', () =>{
+// deferredAdminApps.resolve(adminApps);
+// scope.ngDialogData = {
+// widget: exsistingWidget
+// };
+// createController(scope);
+// scope.$apply();
+// expect(widgetDetails.widget.appId).toEqual(adminApps[0].id);
+// expect(widgetDetails.widget.appName).toEqual(adminApps[0].name);
+// });
+//
+// //TODO:
+// //save changes fail - conflict handling
+// //save changes success
+//
+//
+//
+// });
diff --git a/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.modal.html b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.modal.html
new file mode 100644
index 00000000..2fa56443
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.modal.html
@@ -0,0 +1,156 @@
+<!--
+ ================================================================================
+ 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="widgetOnboardingDetails.scrollApi">
+ <form id="widgets-details-form" name="widgetForm" novalidate
+ autocomplete="off">
+
+ <div class="item required">
+ <div class="item-label">Widget Name</div>
+ <input id="widgets-details-input-name" class="input-field"
+ type="text" ng-model="widgetOnboardingDetails.widget.name"
+ ng-change="widgetOnboardingDetails.updateWidgetName()" name="name"
+ ng-pattern="/^[\w -]*$/" maxlength="100"
+ ng-disabled="widgetOnboardingDetails.isEditMode" required />
+
+ <div class="error-container"
+ ng-show="(widgetOnboardingDetails.emptyWidgetName || widgetForm.name.$dirty) && !widgetOnboardingDetails.duplicatedName">
+ <div id="widgets-details-input-name-conflict" class="err-message">Name
+ not available - choose different name</div>
+ </div>
+
+ <div class="error-container"
+ ng-show="(widgetOnboardingDetails.emptyWidgetName || widgetForm.name.$dirty) && widgetOnboardingDetails.duplicatedName">
+ <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">
+ <div class="item-label">Widget Description</div>
+ <input id="widgets-details-input-name" class="input-field"
+ type="text" ng-model="widgetOnboardingDetails.widget.desc"
+ name="desc" maxlength="200" />
+ </div>
+
+ <div class="widget-property">
+ <input id="widgets-checkbox-app-is-enabled" type="checkbox"
+ class="checkbox-field"
+ ng-model="widgetOnboardingDetails.widget.allUser" />
+ <div class="property-label checkbox-label">Allow all user
+ access</div>
+ </div>
+
+
+ <div class="item" ng-show="!widgetOnboardingDetails.isEditMode">
+ <div class="item-label">Service Endpoint</div>
+ <div>
+ <select id="widgets-details-input-endpoint-url" name="url"
+ class="input-field"
+ ng-model="widgetOnboardingDetails.widget.serviceURL"
+ ng-options="service as service.option
+ for service in widgetOnboardingDetails.availableServices">
+ <option value="" selected="selected">Select Microservice
+ Endpoint</option>
+ </select>
+ </div>
+ </div>
+
+ <div class="item required"
+ ng-show="!widgetOnboardingDetails.widget.allUser">
+ <div class="item-label">Application Name</div>
+ <div>
+ <multiple-select id="widget-applications" unique-data="{{$index}}"
+ placeholder="Select Applications"
+ ng-model="widgetOnboardingDetails.availableApps"
+ on-change="widgetOnboardingDetails.appUpdate()" name-attr="name"
+ value-attr="isSelected"> </multiple-select>
+ </div>
+ <div class="error-container"
+ ng-show="widgetOnboardingDetails.appCounter == 0">
+ <div id="widgets-details-input-name-conflict" class="err-message">Please
+ select at least one Application</div>
+ </div>
+ </div>
+
+ <div class="item" ng-show="!widgetOnboardingDetails.widget.allUser">
+ <div ng-show="widgetOnboardingDetails.hasSelectedApp"
+ class="item-label">User Role Name</div>
+ <div ng-repeat="appRoles in widgetOnboardingDetails.availableApps"
+ id="roles-{{appRoles.roles.split(' ').join('-')}}">
+ <div class="item required">
+ <div class="app-item-left" ng-show="appRoles.isSelected">{{appRoles.name}}</div>
+ <div class="app-item-right" ng-show="appRoles.isSelected">
+ <multiple-select id="widget-roles-by-application"
+ unique-data="{{$index}}" placeholder="Select Roles"
+ ng-model="appRoles.roles" name-attr="name"
+ on-change="widgetOnboardingDetails.roleUpdate(appRoles)"
+ value-attr="isSelected"></multiple-select>
+ <div class="error-container" ng-show="!appRoles.roleSelected">
+ <div id="widgets-details-input-name-conflict"
+ class="err-message">Please select at least one role</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="item required">
+ <div class="item-label">Upload Widget</div>
+ <div>
+ <input id="widget-onboarding-details-upload-file"
+ file-model="widgetOnboardingDetails.widget.file" type="file"
+ style="height: 24px;" />
+
+
+ <div class="error-container"
+ ng-show="widgetOnboardingDetails.widget.file == undefined && !widgetOnboardingDetails.isEditMode">
+ <div class="err-message">Please upload your widget (.zip)</div>
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+
+ <div class="dialog-control">
+ <span class="ecomp-save-spinner"
+ ng-show="widgetOnboardingDetails.isSaving"></span>
+ <button id="widgets-details-save-button" class="btn btn-alt btn-small"
+ ng-class="{disabled: widgetOnboardingDetails.widget.name == undefined || !widgetOnboardingDetails.duplicatedName
+ || (!widgetOnboardingDetails.widget.allUser && widgetOnboardingDetails.appCounter == 0) || (widgetOnboardingDetails.widget.file == undefined && !widgetOnboardingDetails.isEditMode)
+ || (!widgetOnboardingDetails.widget.allUser && !widgetOnboardingDetails.allRoleSelected) || (widgetOnboardingDetails.widget.saving)}"
+ ng-click="widgetOnboardingDetails.saveChanges()">Save</button>
+ <button id="widgets-details-cancel-button"
+ class="btn btn-alt btn-small" ng-click="closeThisDialog()">Cancel</button>
+
+ </div>
+
+
+
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.modal.less b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.modal.less
new file mode 100644
index 00000000..d7cf2671
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-details-dialog/widget-details.modal.less
@@ -0,0 +1,102 @@
+.widget-details-modal {
+ height: 580px;
+ .title {
+ //.n18r;
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @portalDBlue 3px solid;
+ }
+ .widget-input-field{
+ width:250px;
+ }
+ .widget-properties-main {
+ padding: 16px;
+ height: 460px;
+ overflow-y: auto;
+
+ .widget-property{
+ position: relative;
+ margin-bottom: 18px;
+ .property-label{
+ .dGray14r;
+ }
+ .checkbox-label{
+ display: inline-block;
+ padding-left: 3px;
+ }
+ .checkbox-field{
+ padding: 0;
+ margin: 0;
+ vertical-align: middle;
+ position: relative;
+ top: -1px;
+ height:15px;
+ }
+
+ }
+ .widget-upload-field{
+ height:24px;
+ border: 0px solid #d2d2d2;
+ box-shadow: 0px 0px 2px -2px rgba(0, 0, 0, 0.08) inset;
+ padding-left: 2px;
+ }
+ .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{
+ .dGray14r;
+ }
+
+ .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: @funcRed;
+ font-size: 9px;
+ }
+ .valid-message{
+ color: @funcGreen;
+ font-size: 9px;
+ }
+ }
+
+ }
+
+ }
+
+}
diff --git a/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.controller.js b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.controller.js
new file mode 100644
index 00000000..a9e5c416
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.controller.js
@@ -0,0 +1,204 @@
+/*-
+ * ================================================================================
+ * 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 WidgetOnboardingCtrl {
+ constructor($log, applicationsService, widgetsCatalogService, ngDialog, confirmBoxService,
+ userProfileService, $cookies, $scope) {
+ $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) {
+ this.availableApps.push({
+ index: realAppIndex,
+ title: reSortedApp[i - 1].name,
+ value: reSortedApp[i - 1].name
+ })
+ realAppIndex = realAppIndex + 1;
+ }
+ }
+ }).catch(err => {
+ $log.error('WidgetOnboardingCtrl:getAppsForSuperAdmin failed', err);
+ });
+ };
+
+ let getOnboardingWidgets = () => {
+ this.isLoadingTable = true;
+ this.isCommError = false;
+ widgetsCatalogService.getManagedWidgets().then(res => {
+ if(!(res instanceof Array)){
+ this.isCommError = true;
+ return;
+ }
+
+ var reSortedWidget = res.sort(getSortOrder("name"));
+ $scope.widgetsList = reSortedWidget;
+ for(var i = 0; i < $scope.widgetsList.length; i++){
+ let set = new Set();
+ var info = "";
+ var appContent = [];
+ var appName = [];
+ for(var n = 0; n < $scope.widgetsList[i].widgetRoles.length; n++){
+ set.add($scope.widgetsList[i].widgetRoles[n].app.appName);
+ }
+ if($scope.widgetsList[i].allowAllUser == "Y"){
+ info = "All Applications";
+ appContent.push("All Applications");
+ appName.push("All Applications");
+ }
+
+ set.forEach(function (item) {
+ info = item.toString() + " - ";
+ for(var n = 0; n < $scope.widgetsList[i].widgetRoles.length; n++){
+ if(item.toString() == $scope.widgetsList[i].widgetRoles[n].app.appName){
+ info += $scope.widgetsList[i].widgetRoles[n].roleName + "; ";
+ }
+ }
+ appContent.push(info);
+ appName.push(item.toString());
+ });
+ $scope.widgetsList[i].appContent = appContent;
+ $scope.widgetsList[i].appName = appName;
+ }
+ populateAvailableApps(reSortedWidget);
+ }).catch(err => {
+ // Land here when the micro service is down
+ $log.error('WidgetOnboardingCtrl::getOnboardingWidgets caught error', err);
+ }).finally(()=> {
+ this.isLoadingTable = false;
+ });
+
+ };
+
+
+ // Refactor this into a directive
+ 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}
+ ];
+ $scope.widgetsList = [];
+ };
+
+ this.filterByDropdownValue = item => {
+ if(this.filterByApp.value === '')
+ return true;
+
+ for(var i = 0; i < item.appName.length; i++){
+ if(item.appName[i] == this.filterByApp.value
+ || item.appName[i] == 'All Applications'){
+ return true;
+ }
+ }
+ return false;
+ };
+
+ this.openWidgetCatalogDetailsModal = (selectedWidget) => {
+ let data = null;
+ if(selectedWidget){
+ if(!selectedWidget.id){
+ $log.error('WidgetOnboardingCtrl:openWidgetCatalogDetailModal: widget id not found');
+ return;
+ }
+ data = {
+ widget: selectedWidget
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/widget-onboarding/widget-details-dialog/widget-details.modal.html',
+ controller: 'WidgetOnboardingDetailsModalCtrl',
+ controllerAs: 'widgetOnboardingDetails',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if(needUpdate.value === true){
+ getOnboardingWidgets();
+ }
+ });
+ };
+
+ this.deleteWidget = widget => {
+
+ confirmBoxService.deleteItem(widget.name).then(isConfirmed => {
+ if(isConfirmed){
+ if(!widget || !widget.id){
+ $log.error('WidgetOnboardingCtrl::deleteWidget: No widget or ID... cannot delete');
+ return;
+ }
+ widgetsCatalogService.deleteWidget(widget.id).then(() => {
+ $scope.widgetsList.splice($scope.widgetsList.indexOf(widget), 1);
+ }).catch(err => {
+ $log.error('WidgetOnboardingCtrl::deleteWidget error:',err);
+ });
+ }
+ }).catch(err => {
+ $log.error('WidgetOnboardingCtrl::deleteWidget error:',err);
+ });
+
+ };
+
+
+ this.downloadWidget = widget => {
+ widgetsCatalogService.downloadWidgetFile(widget.id).then(res => {
+ var data = res;
+ var filename = widget.name + ".zip";
+
+ if (data == undefined || data == null){
+ confirmBoxService.showInformation("Could not download. Please retry.");
+ return;
+ }
+ var a = document.createElement('a');
+ var blob = new Blob([data], {type: 'application/octet-stream'});
+ a.href = URL.createObjectURL(blob);
+ a.download = filename;
+ a.click();
+ });
+ };
+
+ init();
+ }
+ }
+ WidgetOnboardingCtrl.$inject = ['$log', 'applicationsService', 'widgetsCatalogService', 'ngDialog', 'confirmBoxService',
+ 'userProfileService','$cookies', '$scope'];
+ angular.module('ecompApp').controller('WidgetOnboardingCtrl', WidgetOnboardingCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.controller.spec.js b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.controller.spec.js
new file mode 100644
index 00000000..77659d93
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.controller.spec.js
@@ -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.
+ * ================================================================================
+ */
+
diff --git a/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.less b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.less
new file mode 100644
index 00000000..f832b8f9
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.less
@@ -0,0 +1,32 @@
+.widget-onboarding{
+ .bg_portalWhite;//white for 1702
+ 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-onboarding-table-search::-webkit-input-placeholder,
+{
+font-style: italic;
+ color: #999999;
+
+}
+
+ .widgets-table {
+ width: @table-width;
+ margin: 0 auto;
+ }
+
+ .delete-widget{
+ .ico_trash_default;
+ }
+ .c-ecomp-abs-select{
+ width: 440px;
+ display: inline-block;
+ margin-right: 10px;
+ }
+
+}
diff --git a/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.tpl.html b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.tpl.html
new file mode 100644
index 00000000..808deb8c
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widget-onboarding/widget-onboarding.tpl.html
@@ -0,0 +1,113 @@
+<!--
+ ================================================================================
+ 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="widget-onboarding" id="page-content">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page">Widget Onboarding</h1>
+ </div>
+ <div class="widgets-table">
+ <div class="table-control">
+ <div class="c-ecomp-abs-select default">
+ <div class="table-dropdown">
+ <select id="dropdown1" name="dropdown1" b2b-dropdown
+ placeholder-text="All application"
+ ng-model="widgetOnboarding.filterByApp.value">
+ <option b2b-dropdown-list
+ option-repeat="d in widgetOnboarding.availableApps"
+ value="{{d.value}}">{{d.title}}</option>
+ </select>
+ </div>
+ </div>
+ <input class="table-search" type="text"
+ id="widget-onboarding-table-search"
+ placeholder="Search in entire table"
+ ng-model="widgetOnboarding.searchString" />
+
+ <button id="widget-onboarding-button-add"
+ class="btn btn-alt btn-small"
+ ng-click="widgetOnboarding.openWidgetCatalogDetailsModal()">
+ <i class="icon-people-userbookmark" aria-hidden="true"></i>&nbsp;Add
+ Widget
+ </button>
+
+ <div id="widget-onboarding-communcation-message"
+ ng-show="widgetOnboarding.isCommError">Failed to communicate
+ with the widget microservice.</div>
+
+ <div ng-hide="widgetOnboarding.isCommError">
+ <div b2b-table table-data="portalAdmin.portalAdminsTableData"
+ ng-hide="widgetOnboarding.isLoadingTable"
+ search-string="portalAdmin.searchString" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="widgets-catalog-th-header-name"
+ ng-repeat="header in widgetOnboarding.widgetsTableHeaders"
+ b2b-table-header key="{{header.value}}"
+ sortable="{{header.isSortable}}">{{header.name}}</th>
+ <th id="widgets-catalog-th-header-url" b2b-table-header
+ key="appName" sortable="false">Application</th>
+
+ <th id="widgets-catalog-th-header-download" b2b-table-header
+ sortable="false">Download</th>
+
+ <th id="widgets-catalog-th-header-delete" b2b-table-header
+ sortable="false">Delete</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" class="table-body"
+ row-repeat="rowData in widgetsList | filter:widgetOnboarding.filterByDropdownValue">
+ <tr>
+ <td b2b-table-body
+ ng-repeat="header in widgetOnboarding.widgetsTableHeaders"
+ ng-click="widgetOnboarding.openWidgetCatalogDetailsModal(rowData)">
+ <div id="widgets-catalog-widget-name-{{rowData.id}}"
+ ng-bind="rowData[header.value]"></div>
+ </td>
+
+ <td b2b-table-body
+ ng-click="widgetOnboarding.openWidgetCatalogDetailsModal(rowData)">
+ <div ng-repeat="row in rowData.appContent">
+ <div id="widget-catalog-widget-application-{{rowData.id}}"
+ ng-bind="row"></div>
+ </div>
+ </td>
+
+ <td b2b-table-body>
+ <div class="icon-download"
+ ng-click="widgetOnboarding.downloadWidget(rowData)"></div>
+ </td>
+
+ <td b2b-table-body>
+ <div id="widget-onboarding-div-delete-widget-{{$index}}" class="delete-widget"
+ ng-click="widgetOnboarding.deleteWidget(rowData)"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</div>
diff --git a/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.controller.js b/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.controller.js
new file mode 100644
index 00000000..eb628b4f
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.controller.js
@@ -0,0 +1,226 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+/**
+ * Created by nnaffar on 12/20/15.
+ */
+'use strict';
+(function () {
+ class WidgetDetailsModalCtrl {
+ constructor($scope, $log, applicationsService, widgetsService, errorMessageByCode,
+ ECOMP_URL_REGEX, $window, userProfileService, $cookies, $rootScope) {
+
+ 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){
+ //workaround to display validation errors for apps dropdown in case selectedApp isn't valid
+ $scope.widgetForm.app.$dirty = true;
+ }
+ } else {
+ this.selectedApp = null;
+ }
+ //init appId & appName with selectedApp
+ this.updateSelectedApp();
+ }).catch(err => {
+ confirmBoxService.showInformation('There was a problem retrieving the Widgets. ' +
+ 'Please try again later.').then(isConfirmed => {});
+ $log.error('WidgetDetailsModalCtrl::getAvailableApps error: '+ err);
+ });
+ };
+ /**/
+
+ let 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 part handles conflict errors (409)
+ this.conflictMessages = {};
+ this.scrollApi = {};
+ let handleConflictErrors = err => {
+ if(!err.data){
+ return;
+ }
+ if(!err.data.length){ //support objects
+ err.data = [err.data]
+ }
+ _.forEach(err.data, item => {
+ _.forEach(item.fields, field => {
+ //set conflict message
+ this.conflictMessages[field.name] = errorMessageByCode[item.errorCode];
+ //set field as invalid
+ $scope.widgetForm[field.name].$setValidity('conflict', false);
+ //set watch once to clear error after user correction
+ 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=> {
+ $scope.orgUserId = profile.orgUserId;
+ if ($cookies.getObject($scope.orgUserId + '_widget') != undefined && $cookies.getObject($scope.orgUserId + '_widget') != null) {
+ $cookies.remove($scope.orgUserId + '_widget');
+ }
+ }).catch(err => {
+ $log.error('WidgetDetailsModalCtrl::emptyCookies: There was a problem emptying the cookies! No user error presented though.');
+ });
+ };
+
+ 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){//Conflict
+ handleConflictErrors(err);
+ } else {
+ confirmBoxService.showInformation('There was a problem saving the Widget. ' +
+ 'Please try again later. Error Status: ' + err.status).then(isConfirmed => {});
+ }
+ $log.error('WidgetDetailsModalCtrl::saveChanges error: ', err);
+ }).finally(()=>{
+ this.isSaving = false;
+ // for bug in IE 11
+ var objOffsetVersion = objAgent.indexOf("MSIE");
+ if (objOffsetVersion != -1) {
+ $log.debug('WidgetDetailsModalCtrl::saveChanges: Browser is IE, forcing Refresh');
+ $window.location.reload(); // for bug in IE 11
+ }
+ // for bug in IE 11
+ });
+ } else {
+ widgetsService.createWidget(this.widget)
+ .then(() => {
+ $log.debug('WidgetDetailsModalCtrl::createWidget: Widget creation succeeded!');
+ $scope.closeThisDialog(true);
+ emptyCookies();
+ $rootScope.noWidgets = false;
+ }).catch(err => {
+ if(err.status === 409){//Conflict
+ handleConflictErrors(err);
+ } else {
+ confirmBoxService.showInformation('There was a problem creating the Widget. ' +
+ 'Please try again later. Error Status: ' + err.status).then(isConfirmed => {});
+ }
+ $log.error('WidgetDetailsModalCtrl::createWidget error: ',err);
+ }).finally(()=>{
+ this.isSaving = false;
+ // for bug in IE 11
+ var objOffsetVersion = objAgent.indexOf("MSIE");
+ if (objOffsetVersion != -1) {
+ $log.debug('WidgetDetailsModalCtrl::createWidget: Browser is IE, forcing Refresh');
+ $window.location.reload(); // for bug in IE 11
+ }
+ // for bug in IE 11
+ });
+ }
+ };
+
+ init();
+
+ $scope.$on('$stateChangeStart', e => {
+ //Disable navigation when modal is opened
+ e.preventDefault();
+ });
+ }
+ }
+ WidgetDetailsModalCtrl.$inject = ['$scope', '$log', 'applicationsService', 'widgetsService', 'errorMessageByCode',
+ 'ECOMP_URL_REGEX', '$window','userProfileService','$cookies', '$rootScope'];
+ angular.module('ecompApp').controller('WidgetDetailsModalCtrl', WidgetDetailsModalCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js b/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js
new file mode 100644
index 00000000..1762fadb
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js
@@ -0,0 +1,154 @@
+/*-
+ * ================================================================================
+ * 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: WidgetDetailsModalCtrl', ()=> {
+// /**
+// * INITIALIZATION
+// */
+// beforeEach(module('testUtils'));
+// beforeEach(module('ecompApp'));
+//
+// let promisesTestUtils;
+// //destroy $http default cache before starting to prevent the error 'default cache already exists'
+// //_promisesTestUtils_ comes from testUtils for promises resolve/reject
+// beforeEach(inject((_CacheFactory_, _promisesTestUtils_)=> {
+// _CacheFactory_.destroyAll();
+// promisesTestUtils = _promisesTestUtils_;
+// }));
+//
+// let widgetDetails, scope, $controller, $q, $rootScope, $log, widgetsService, errorMessageByCode, ECOMP_URL_REGEX;
+// let deferredAdminApps, deferredUserProfile;
+// let applicationsServiceMock, widgetsServiceMock, userProfileServiceMock;
+// beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_)=> {
+// [$controller, $q, $rootScope, $log] = [_$controller_, _$q_, _$rootScope_, _$log_];
+//
+// deferredAdminApps = $q.defer();
+// deferredUserProfile = $q.defer();
+// /*applicationsServiceMock = {
+// getAppsForSuperAdminAndAccountAdmin: () => {
+// var promise = () => {return deferredAdminApps.promise};
+// var cancel = jasmine.createSpy();
+// return {
+// promise: promise,
+// cancel: cancel
+// }
+// }
+// };*/
+//
+// widgetsServiceMock = {
+// updateWidget: () => {
+// var promise = () => {return deferredAdminApps.promise};
+// var cancel = jasmine.createSpy();
+// return {
+// promise: promise,
+// cancel: cancel
+// }
+// },
+// createWidget: () => {
+// var promise = () => {return deferredAdminApps.promise};
+// var cancel = jasmine.createSpy();
+// return {
+// promise: promise,
+// cancel: cancel
+// }
+// }
+// };
+//
+// userProfileServiceMock = jasmine.createSpyObj('userProfileServiceMock',['getUserProfile']);
+// userProfileServiceMock.getUserProfile.and.returnValue(deferredUserProfile.promise);
+//
+// applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock',['getAppsForSuperAdminAndAccountAdmin']);
+// applicationsServiceMock.getAppsForSuperAdminAndAccountAdmin.and.returnValue(deferredAdminApps.promise);
+//
+// }));
+//
+// beforeEach(()=> {
+// errorMessageByCode = [];
+// ECOMP_URL_REGEX = "";
+// scope = $rootScope.$new();
+// createController(scope);
+// });
+//
+// let createController = scopeObj => {
+// widgetDetails = $controller('WidgetDetailsModalCtrl', {
+// $scope: scope,
+// $log: $log,
+// applicationsService: applicationsServiceMock,
+// widgetsService: widgetsServiceMock,
+// errorMessageByCode: errorMessageByCode,
+// ECOMP_URL_REGEX: ECOMP_URL_REGEX,
+// userProfileService: userProfileServiceMock
+// });
+// };
+//
+// /**
+// * MOCK DATA
+// */
+// let newWidgetModel = {
+// name: null,
+// appId: null,
+// appName: null,
+// width: 360,
+// height: 300,
+// url: null
+// };
+// let exsistingWidget = {
+// name: 'some widget',
+// appId: 1,
+// appName: 'APP NAME',
+// width: 360,
+// height: 300,
+// url: 'http://a.com'
+// };
+// let adminApps = [{id: 1, name: 'a'}, {id: 2, name: 'b'}];
+//
+// /**
+// * TEST CASES
+// */
+// it('should initialize controller with new widget mode when opening the modal without selected widget', ()=> {
+// expect(widgetDetails.widget).toEqual(newWidgetModel);
+// });
+//
+// it('should initialize controller with exsisting widget details when opening the modal with selected widget', ()=> {
+// scope.ngDialogData = {
+// widget: exsistingWidget
+// };
+// createController(scope);
+// expect(widgetDetails.widget).toEqual(exsistingWidget);
+// });
+//
+// it('should populate widget selected app name and id when initializing controller with widget', () =>{
+// deferredAdminApps.resolve(adminApps);
+// scope.ngDialogData = {
+// widget: exsistingWidget
+// };
+// createController(scope);
+// scope.$apply();
+// expect(widgetDetails.widget.appId).toEqual(adminApps[0].id);
+// expect(widgetDetails.widget.appName).toEqual(adminApps[0].name);
+// });
+//
+// //TODO:
+// //save changes fail - conflict handling
+// //save changes success
+//
+//
+//
+// });
diff --git a/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.modal.html b/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.modal.html
new file mode 100644
index 00000000..46a5b1c7
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.modal.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 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">
+ <!-- We can remove this script once we get to AT&T Corporate Firefox version 47
+ autocomplete="off" won't work until v47 -->
+ <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="url-validation-button"
+ ng-class="{'disabled': widgetForm.url.$invalid}"
+ ng-click="(widgetForm.url.$invalid) || widgetDetails.validateUrl()">Validate URL
+ </div>
+ -->
+
+ <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 class="error-container" ng-show="widgetForm.url.$valid && !widgetDetails.conflictMessages.url">
+ <small class="err-message" ng-hide="widgetDetails.urlValidity.isValid" ng-bind="widgetDetails.urlValidity.message"></small>
+ <small class="valid-message" ng-show="widgetDetails.urlValidity.isValid" ng-bind="widgetDetails.urlValidity.message"></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-common/client/app/views/widgets/widget-details-dialog/widget-details.modal.less b/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.modal.less
new file mode 100644
index 00000000..568b6180
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog/widget-details.modal.less
@@ -0,0 +1,75 @@
+.widget-details-modal {
+ height: 580px;
+
+ .title {
+ //.n18r;
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @portalDBlue 3px solid;
+ }
+
+ .widget-properties-main {
+ padding: 16px;
+ height: 460px;
+ 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{
+ .dGray14r;
+ }
+
+ .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: @funcRed;
+ font-size: 9px;
+ }
+ .valid-message{
+ color: @funcGreen;
+ font-size: 9px;
+ }
+ }
+
+ }
+
+ }
+
+}
diff --git a/ecomp-portal-FE-common/client/app/views/widgets/widgets.controller.js b/ecomp-portal-FE-common/client/app/views/widgets/widgets.controller.js
new file mode 100644
index 00000000..e46f1ce5
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widgets/widgets.controller.js
@@ -0,0 +1,168 @@
+/*-
+ * ================================================================================
+ * 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, $rootScope) {
+ //$log.info('WidgetsCtrl::init: Starting Up');
+ $scope.infoMessage = true;
+ $rootScope.noWidgets = false;
+
+ 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 => {
+ $log.debug('WidgetsCtrl.getOnboardingWidgets:: ' + JSON.stringify(res));
+ // if (JSON.stringify(res) === '[]') {
+ // confirmBoxService.showInformation('There are currently no Widgets. ').then(isConfirmed => {});
+ // }
+ var reSortedWidget = res.sort(getSortOrder("name"));
+ this.widgetsList = reSortedWidget;
+ populateAvailableApps(reSortedWidget);
+ // $log.info('WidgetsHomeCtrl::getUserWidgets count : ' + $scope.widgetsList.length);
+ if (Object.keys(res).length === 0 ) {
+ $rootScope.noWidgets = true;
+ $scope.isLoadingTable = false;
+ $log.info('WidgetsHomeCtrl::getUserWidgets: There are no available Widgets');
+ }
+ }).catch(err => {
+ confirmBoxService.showInformation('There was a problem retrieving the Widgets. ' +
+ 'Please try again later.').then(isConfirmed => {});
+ $log.error('WidgetsCtrl::getOnboardingWidgets error: ' + err);
+ }).finally(()=> {
+ this.isLoadingTable = false;
+ });
+ };
+
+ // Refactor this into a directive
+ let getSortOrder = (prop) => {
+ return function(a, b) {
+ // $log.debug('a = ' + JSON.stringify(a) + "| b = " + JSON.stringify(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();
+
+ /*Table general configuration params*/
+ this.searchString = '';
+ /*Table data*/
+ 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);
+ confirmBoxService.showInformation('There was a problem deleting the Widget. ' +
+ 'Please try again later.').then(isConfirmed => {});
+ });
+ }
+ }).catch(err => {
+ $log.error('WidgetsCtrl::deleteWidget error:',err);
+ });
+ };
+
+ init();
+ }
+ }
+ WidgetsCtrl.$inject = ['$log', 'applicationsService', 'widgetsService', 'ngDialog', 'confirmBoxService',
+ 'userProfileService','$cookies', '$scope', '$rootScope'];
+ angular.module('ecompApp').controller('WidgetsCtrl', WidgetsCtrl);
+})();
diff --git a/ecomp-portal-FE-common/client/app/views/widgets/widgets.controller.spec.js b/ecomp-portal-FE-common/client/app/views/widgets/widgets.controller.spec.js
new file mode 100644
index 00000000..3841a2b3
--- /dev/null
+++ b/ecomp-portal-FE-common/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-common/client/app/views/widgets/widgets.less b/ecomp-portal-FE-common/client/app/views/widgets/widgets.less
new file mode 100644
index 00000000..43bae91f
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widgets/widgets.less
@@ -0,0 +1,48 @@
+.widgets-page-main{
+ .bg_portalWhite;//white for 1702
+ 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-left: @table-margin-left;
+ //margin: @table-margin;
+ margin:auto;
+ .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; // @portalWhite;
+
+ .error-help {
+ color: @o; // @portalDGray;
+ font-weight: normal;
+ }
+
+ .informational {
+ color: @o; // @portalDGray;
+ font-weight: normal;
+ font-style: italic;
+ }
+ }
+
+ } \ No newline at end of file
diff --git a/ecomp-portal-FE-common/client/app/views/widgets/widgets.tpl.html b/ecomp-portal-FE-common/client/app/views/widgets/widgets.tpl.html
new file mode 100644
index 00000000..8afb8267
--- /dev/null
+++ b/ecomp-portal-FE-common/client/app/views/widgets/widgets.tpl.html
@@ -0,0 +1,81 @@
+<!--
+ ================================================================================
+ 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="page-content">
+ <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-portal-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()">&times;</button>
+ </div>
+ <div class="error-text" ng-show="noWidgets">
+ <span class="informational">There are currently no widgets available.</span>
+ </div>
+
+ <span class="ecomp-spinner" ng-show="widgets.isLoadingTable"></span>
+ <div class="c-ecomp-portal-abs-table default" ng-hide="widgets.isLoadingTable">
+ <table b2b-table
+ table-data="widgets.widgetsList"
+ search-string="widgets.searchString"
+ view-per-page="widgets.viewPerPageIgnored"
+ current-page="widgets.currentPageIgnored"
+ total-page="widgets.totalPageIgnored">
+ <thead b2b-table-row type="header">
+ <tr>
+ <th id="widget-onboarding-th-header-name" ng-repeat="header in widgets.widgetsTableHeaders" b2b-table-header key="{{header.value}}" sortable="{{header.isSortable}}">{{header.name}}</th>
+ <th id="widget-onboarding-th-header-url" b2b-table-header key="url" sortable="{{false}}">URL</th>
+ <th id="widget-onboarding-th-header-delete" b2b-table-header sortable="{{false}}">Delete</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body"
+ class="table-body"
+ row-repeat="rowData in widgets.widgetsList | filter:widgets.filterByDropdownValue">
+ <tr >
+ <td b2b-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 b2b-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 b2b-table-body>
+ <div id="widget-onboarding-div-delete-widget-{{$index}}" class="delete-widget" ng-click="widgets.deleteWidget(rowData)"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+
+</div>