diff options
author | st782s <statta@research.att.com> | 2017-05-04 07:48:42 -0400 |
---|---|---|
committer | st782s <statta@research.att.com> | 2017-05-04 12:28:17 -0400 |
commit | b54df0ddd0c6a0372327c5aa3668e5a6458fcd64 (patch) | |
tree | e69cfa9b314a801bd187cf0145d1d4306436229c /ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog | |
parent | 39d1e62c84041831bfc52cca73b5ed5efaf57d27 (diff) |
[PORTAL-7] Rebase
This rebasing includes common libraries and common overlays projects
abstraction of components
Change-Id: I9a24a338665c7cd058978e8636bc412d9e2fdce8
Signed-off-by: st782s <statta@research.att.com>
Diffstat (limited to 'ecomp-portal-FE-common/client/app/views/widgets/widget-details-dialog')
4 files changed, 607 insertions, 0 deletions
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; + } + } + + } + + } + +} |