diff options
Diffstat (limited to 'portal-FE-os/src/app/pages')
22 files changed, 2610 insertions, 0 deletions
diff --git a/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.html b/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.html new file mode 100644 index 00000000..bef37b84 --- /dev/null +++ b/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.html @@ -0,0 +1,275 @@ +<!-- + ============LICENSE_START========================================== + ONAP Portal + =================================================================== + Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + =================================================================== + + Unless otherwise specified, all software contained herein is licensed + under the Apache License, Version 2.0 (the "License"); + you may not use this software 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. + + Unless otherwise specified, all documentation contained herein is licensed + under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + you may not use this documentation except in compliance with the License. + You may obtain a copy of the License at + + https://creativecommons.org/licenses/by/4.0/ + + Unless required by applicable law or agreed to in writing, documentation + 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. + + ============LICENSE_END============================================ + +--> + +<div class="container"> + <!--Modal Headers--> + <div class="modal-header"> + <h4 class="modal-title">Application Details</h4> + <button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross')"> + <span aria-hidden="true">×</span> + </button> + </div> + <!--Modal Body goes here--> + <div class="modal-body"> + <div class="application-details-modal"> + <div class="app-properties-main" scroll-top="appDetails.scrollApi"> + <form name="appForm" novalidate autocomplete="off"> + <div id="app-left-container" class="left-container"> + <div class="property-label checkbox-label"> + <mat-checkbox name="isRestrictedApp" type="checkbox" [(ngModel)]="applicationObj.restrictedApp" + id="checkbox-app-is-restricted" [disabled]="isEditMode" [checked]="applicationObj.restrictedApp"> + Hyperlink only application + </mat-checkbox> + </div> + + <div class="property required"> + <div class="property-label">Application Name</div> + <input id="input-app-name" type="text" + [(ngModel)]="applicationObj.name" maxlength="100" name="name" + pattern="/^[a-zA-Z0-9_\-\s\&]*$/" required="true" /> + + <div id="error-container-conflict" class="error-container" ng-show="appDetails.conflictMessages.name" + id="conflictMessages-name"> + <small id="app-name-error-conflict" class="err-message" + ng-bind="appDetails.conflictMessages.name"> + </small> + </div> + + <div id="error-container-edit" class="error-container" + *ngIf="(!applicationObj.name || applicationObj.name.length == 0)"> + <small id="app-name-error-required" class="err-message"> + Application name is required + </small> + </div> + </div> + + <div [ngClass]="(applicationObj.isEnabled) ? 'property required' : 'property'"> + <div id="url-property-label" class="property-label">URL</div> + <input id="input-app-url" [(ngModel)]="applicationObj.url" + maxlength="256" name="url" type="url" placeholder="https://" + pattern="/^((?:https?\:\/\/|ftp?\:\/\/)?(w{3}.)?(?:[-a-z0-9]+\.)*[-a-z0-9]+.*)[^-_.]$/i" required /> + + <div id="error-container-edit" class="error-container" + *ngIf="(applicationObj.isEnabled && applicationObj.url=='')"> + <small id="app-name-error-required" class="err-message"> + Application URL is required + </small> + </div> + </div> + + <div [ngClass]="(applicationObj.isEnabled) ? 'property required' : 'property'" + [hidden] ="applicationObj.restrictedApp"> + <div class="property-label">Rest API URL</div> + <input id="input-app-rest-url" [(ngModel)]="applicationObj.restUrl" + name="restUrl" type="url" placeholder="https://" + pattern="/^((?:https?\:\/\/|ftp?\:\/\/)?(w{3}.)?(?:[-a-z0-9]+\.)*[-a-z0-9]+.*)[^-_.]$/i" maxlength="256" + ng-required="!applicationObj.restrictedApp" /> + + <div id="error-container-edit" class="error-container" + *ngIf="(applicationObj.isEnabled && applicationObj.restUrl=='')"> + <small id="app-name-error-required" class="err-message"> + Rest API URL is required + </small> + </div> + </div> + + <div [ngClass]="(applicationObj.isEnabled || applicationObj.isCentralAuth) ? 'property required' : 'property'" + [hidden] ="applicationObj.restrictedApp"> + <div id="username-property-label" class="property-label">Username</div> + <input type="text" id="input-username-property" [(ngModel)]="applicationObj.username" name="username" + maxlength="256" ng-required="!appDetails.app.restrictedApp" /> + + <div id="error-container-edit" class="error-container" + *ngIf="((applicationObj.isEnabled || applicationObj.isCentralAuth) && applicationObj.username =='')"> + <small id="app-name-error-required" class="err-message"> + App Username is required + </small> + </div> + </div> + + <div [ngClass]="(applicationObj.isEnabled && !applicationObj.isCentralAuth) ? 'property required' : 'property'" + [hidden] ="applicationObj.restrictedApp"> + <div id="pwd-property-label" class="property-label">Password</div> + <input type="password" id="input-mylogins-password" + [(ngModel)]="applicationObj.appPassword" autocomplete="new-password" + name="appPassword" maxlength="256"/> + + <div id="error-container-edit" class="error-container" + *ngIf="(applicationObj.isEnabled && !applicationObj.isCentralAuth && applicationObj.appPassword =='')"> + <small id="app-name-error-required" class="err-message"> + Password is required + </small> + </div> + </div> + </div> + + <!-- Right container--> + <div class="right-container"> + <div class="property"> + <div class="property-label">Upload Image</div> + <input type="file" id="input-app-image-upload" + class="input-file-field input-app-image-upload-ht" + accept="image/*" [(ngModel)]="applicationObj.originalImage" + name="appImage" image-upload="applicationObj.originalImage" + image-upload-resize-max-height="300" + image-upload-resize-max-width="360" + image-upload-resize-quality="0.7" + image-upload-api="appDetails.imageApi" + (change)="appImageHandler($event)" /> + + <div id="app-error-image-upload-type" class="error-container" + *ngIf="appImageTypeError" class="ng-hide"> + <div class="error-container"> + <small id="error-app-invalid-image-size" class="err-message">File must be an image</small> + </div> + </div> + + <div id="app-error-image-upload" class="error-container" + *ngIf="(applicationObj.originalImage && applicationObj.originalImage.dirty)"> + <div ng-messages="appForm.appImage.$error" + class="error-container"> + <small id="error-app-invalid-image-size" class="err-message" + ng-message="imageSize">Image file must be smaller than + 1MB</small> + </div> + </div> + + <div class="property-label preview"> + <span class="left-label">Preview</span> + <span class="remove" (click)="removeImage()">Remove</span> + </div> + <img id="image-app-preview" class="image-preview" + src="{{applicationObj.imageLink || applicationObj.thumbnail || emptyImgForPreview}}" /> + + <br/> + <div id="property-active" class="property-active"> + <mat-checkbox name="isEnabled" [(ngModel)]="applicationObj.isEnabled" + [checked]="applicationObj.isEnabled" + id="checkbox-app-is-enabled">Active + </mat-checkbox> + </div> + + <div id="property-guest-access" class="property-guest-access"> + <mat-checkbox name="isOpen" [(ngModel)]="applicationObj.isOpen" id="checkbox-app-is-open" + [checked]="(applicationObj.isOpen || applicationObj.restrictedApp)" + [disabled]="applicationObj.restrictedApp">Allow guest access</mat-checkbox> + </div> + + <div class="table-control" style="display: inline-flex;" [hidden] ="applicationObj.restrictedApp"> + <div id="property-is-central-auth" class="property" [hidden] ="applicationObj.restrictedApp"> + <mat-checkbox name="isCentralAuth" [(ngModel)]="applicationObj.isCentralAuth" + id="checkbox-app-is-central-auth" [checked]="applicationObj.isCentralAuth"> + Centralized + </mat-checkbox> + </div> + + <div class="centralized-key" aria-haspopup="true" style="padding-left: 150px;"> + <div b2b-flyout-toggler class="notification-div" > + <div id="tooltip" class="icon-primary-flat-info" tabindex="0" + b2b-accessibility-click="13,32" aria-label="notifications" + aria-haspopup="true" + role="button"></div> + </div> + + <!--<div id="notification" align="left"> + <p class="uuid-text" + style="font: normal 12px Omnes-ECOMP-W02, Arial;"> + To convert the non-centralized app to centralized app please + follow below steps. <a href="https://wiki.web.att.com/display/ECops/BulkUpload" target="_blank"> + https://wiki.web.att.com/display/ECops/BulkUpload</a> + </p> + </div>--> + </div> + </div> + + <div class="table-control" style="display: inline-flex;" [hidden] ="applicationObj.restrictedApp"> + <div id="property-communication-key" class="property" + [hidden] ="applicationObj.restrictedApp" style="width: 250px;"> + <div id="property-communication-key-label" class="property-label">Application UUID</div> + <input type="text" id="input-UEB-communication-key" + [(ngModel)]="applicationObj.uebKey" name="uebKey" + readonly="readonly" [disabled]="true"/> + </div> + + <div class="communitcaion-key" aria-haspopup="true" style="padding-left: 20px;"> + <div b2b-flyout-toggler class="notification-div"> + <div id="tooltip" class="icon-primary-flat-info" tabindex="0" + b2b-accessibility-click="13,32" aria-label="notifications" + aria-haspopup="true" + role="button"></div> + </div> + <!--<div id="notification" align="left"> + <p class="uuid-text" style="font: normal 12px Omnes-ECOMP-W02, Arial;">Application UUID is used as a communication + key between application and portal.Please place this value in + portal.properties</p> + </div>--> + </div> + </div> + + <div class="table-control" style="display: inline-flex;" [hidden] ="applicationObj.restrictedApp"> + + <div [ngClass]="(applicationObj.isCentralAuth) ? 'property required' : 'property'" + [hidden] ="applicationObj.restrictedApp" style="width: 250px;"> + <div id="pwd-property-label" class="property-label" >Name Space</div> + <input type="text" id="input-mylogins-auth-namespace" + [(ngModel)]="applicationObj.nameSpace" name="appAuthNameSpace" + maxlength="256" [disabled]="!applicationObj.isCentralAuth" /> + </div> + <div class="communitcaion-key" aria-haspopup="true" style="padding-left: 20px;"> + + <div b2b-flyout-toggler class="notification-div"> + <div id="tooltip" class="icon-primary-flat-info" tabindex="0" + b2b-accessibility-click="13,32" aria-label="notifications" + aria-haspopup="true" + role="button"></div> + </div> + <!--<div id="notification" align="left"> + <p class="nameSpace-text" style="font: normal 12px Omnes-ECOMP-W02, Arial;">NameSpace should be created in AAF and portal mechid should be admin of the given namespace.</p> + </div>--> + </div> + </div> + </div> + </div> + </form> + </div> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-primary" [disabled]="(!applicationObj.name || applicationObj.name.length == 0)" (click)="saveChanges()">Save</button> + <button type="button" class="btn btn-primary" (click)="activeModal.close('Close')">Cancel</button> + </div> +</div>
\ No newline at end of file diff --git a/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.scss b/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.scss new file mode 100644 index 00000000..0e042a96 --- /dev/null +++ b/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.scss @@ -0,0 +1,163 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ + +.application-details-modal .app-properties-mainapp-properties-main{ + padding-left: 40px; + padding-top: 16px; + padding-bottom: 16px; +} + +.application-details-modal .app-properties-main .left-container{ + display: inline-block; + width: 48%; + +} + +.application-details-modal .app-properties-main .right-container{ + display: inline-block; + width: 48%; + float: right; + margin-right:10px; + +} + +.application-details-modal .app-properties-main .property{ + position: relative; + margin-bottom: 18px; +} +.application-details-modal .app-properties-main .checkbox-label{ + display: inline-block; + padding-left: 3px; +} +.application-details-modal .app-properties-main .checkbox-field{ + padding: 0; + margin: 0; + vertical-align: middle; + position: relative; + top: -1px; +} +.application-details-modal .app-properties-main .preview{ + width: 220px; + margin-top: 22px; + display: block; +} + +.application-details-modal .app-properties-main .left-label{ + display:inline-block; + float: left; +} +.application-details-modal .app-properties-main .remove{ + cursor: pointer; + display: inline-block; + float: right; +} + +.application-details-modal .app-properties-main .input-field{ + width: 220px; +} + +.application-details-modal .app-properties-main .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; +} + +.application-details-modal .app-properties-main .image-preview{ + background: gray; + background-size: cover; + width: 220px; + height: 184px; + margin-top: 10px; + border: 2px solid #e8e8e8; + border-radius: 4px; +} + +.application-details-modal .app-properties-main .error-container{ + position: absolute; + width: 220px; + display: block; + height: 12px; + line-height: 12px; +} + +.application-details-modal .app-properties-main .err-message{ + font-size: 10px; +} + +.application-details-modal .app-properties-main .checkbox .skin { + left: 0px; + top: 0px; +} + +.application-details-modal input[type="text"] { + width: 16em; +} + +.application-details-modal input[type="url"] { + width: 16em; +} + +.application-details-modal input[type="number"] { + width: 16em; +} + +.application-details-modal input[type="password"] { + width: 16em; +} + +::ng-deep .modal-dialog { + max-width: 700px; + width: 630px; + overflow-x: auto; + overflow-y: auto; +} + +.required::before { + color: rgb(207, 42, 42); + margin-right: 2px; + content: "* "; + position: absolute; + top: 28px; + left: -10px; +} +.remove{ + cursor: pointer; + color: #007bff; +} +
\ No newline at end of file diff --git a/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.spec.ts b/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.spec.ts new file mode 100644 index 00000000..0ea27629 --- /dev/null +++ b/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.spec.ts @@ -0,0 +1,63 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ + +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ApplicationDetailsDialogComponent } from './application-details-dialog.component'; + +describe('ApplicationDetailsDialogComponent', () => { + let component: ApplicationDetailsDialogComponent; + let fixture: ComponentFixture<ApplicationDetailsDialogComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ApplicationDetailsDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ApplicationDetailsDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.ts b/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.ts new file mode 100644 index 00000000..60654461 --- /dev/null +++ b/portal-FE-os/src/app/pages/application-onboarding/application-details-dialog/application-details-dialog.component.ts @@ -0,0 +1,380 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ + +import { Component, OnInit, Input, Output, EventEmitter} from '@angular/core'; +import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { IApplications } from 'src/app/shared/model/applications-onboarding/applications'; +import { ApplicationsService } from 'src/app/shared/services'; +import { ConfirmationModalComponent } from 'src/app/modals/confirmation-modal/confirmation-modal.component'; +import { InformationModalComponent } from 'src/app/modals/information-modal/information-modal.component'; + +@Component({ + selector: 'app-application-details-dialog', + templateUrl: './application-details-dialog.component.html', + styleUrls: ['./application-details-dialog.component.scss'], +}) +export class ApplicationDetailsDialogComponent implements OnInit { + + emptyImg = null; + emptyImgForPreview:string; + conflictMessages = {}; + result: any; + isEditMode: boolean = false; + appImageTypeError: boolean = false; + isSaving: boolean = false; + originalImage: any; + ECOMP_URL_REGEX = "/^((?:https?\:\/\/|ftp?\:\/\/)?(w{3}.)?(?:[-a-z0-9]+\.)*[-a-z0-9]+.*)[^-_.]$/i"; + + constructor(public activeModal: NgbActiveModal, public ngbModal: NgbModal, + public applicationsService : ApplicationsService) { } + + @Input() applicationObj: IApplications; + @Output() passEntry: EventEmitter<any> = new EventEmitter(); + + newAppModel = { + 'id': null, + 'name': null, + 'imageUrl': null, + 'description': null, + 'notes': null, + 'url': null, + 'alternateUrl': null, + 'restUrl': null, + 'isOpen': false, + 'username': null, + 'appPassword': null, + 'thumbnail': this.emptyImg, + 'isEnabled': false, + 'restrictedApp': false, + 'nameSpace':null, + 'isCentralAuth': false, + 'uebTopicName':null, + 'uebKey': null, + 'uebSecret': null, + 'imageLink': null + }; + + + ngOnInit() { + if(this.applicationObj.id){ + this.isEditMode = true; + }else{ + this.isEditMode = false; + } + //console.log("isEditMode :: ",this.isEditMode); + this.originalImage = null + this.emptyImgForPreview = '../../../assets/images/default_app_image.gif'; + } + + appImageHandler(event: any){ + var reader = new FileReader(); + if(event.target.files && event.target.files[0]){ + reader.readAsDataURL(event.target.files[0]); // read file as data url + var fileName = event.target.files[0].name; + var validFormats = ['jpg', 'jpeg', 'bmp', 'gif', 'png']; + //Get file extension + var ext = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase(); + //console.log("fileName::>>",fileName ,ext) + //console.log("fileExtetion::>>",ext) + //Check for valid format + if(validFormats.indexOf(ext) == -1){ + this.newAppModel.thumbnail = this.emptyImg; + this.originalImage = null; + this.applicationObj.imageUrl = null; + this.applicationObj.imageLink = null; + this.applicationObj.thumbnail = null; + if(!this.isEditMode){ + this.newAppModel.imageUrl = null; + this.newAppModel.imageLink = null; + this.newAppModel.thumbnail = null; + } + this.appImageTypeError=true; + }else{ + reader.onload = (event: any) => { // called once readAsDataURL is completed + this.applicationObj.imageLink = event.target.result; + this.applicationObj.imageUrl = event.target.result; + this.applicationObj.thumbnail = event.target.result; + this.originalImage = event.target.result; + if(!this.isEditMode){ + this.newAppModel.imageLink = event.target.result; + this.newAppModel.imageUrl = event.target.result; + this.newAppModel.thumbnail = event.target.result; + this.originalImage = event.target.result; + } + } + } + } + } + + removeImage(){ + let confirmationMsg = 'Are you sure you want to remove the image?'; + this.openInformationModal("Confirmation",confirmationMsg).result.then((result) => { + if (result === 'Ok') { + //this.imageApi.clearFile(); + this.applicationObj.thumbnail = this.emptyImg; + this.originalImage = null; + this.applicationObj.imageUrl = null; + this.applicationObj.imageLink = null; + this.emptyImgForPreview = '../../../assets/images/default_app_image.gif'; + } + }, (resut) => { + return; + }) + } + + /** Add/Edit Application Method*/ + saveChanges() { + //console.log("addNewApplication getting called.."); + if(this.applicationObj.isCentralAuth){ + //if valid. + if(!this.applicationObj.isEnabled){ + if(((this.applicationObj.name == 'undefined' || !this.applicationObj.name)||(this.applicationObj.nameSpace == 'undefined' + || !this.applicationObj.nameSpace) ||(this.applicationObj.username == 'undefined' || !this.applicationObj.username))) { + this.openConfirmationModal('','Please fill in all required fields(*) for centralized application'); + return; + } + } + if(this.applicationObj.isEnabled){ + if(((this.applicationObj.name == 'undefined' || !this.applicationObj.name) + ||(this.applicationObj.url == 'undefined'|| !this.applicationObj.url) + ||(this.applicationObj.username == 'undefined' || !this.applicationObj.username)||(this.applicationObj.nameSpace == 'undefined' + || !this.applicationObj.nameSpace))) { + + this.openConfirmationModal('','Please fill in all required fields(*) for centralized active application'); + return; + } + } + }else{ + if(!this.applicationObj.isEnabled) { + if((this.applicationObj.name == 'undefined' || !this.applicationObj.name)){ + this.openConfirmationModal('','Please fill in all required field(*) ApplicationName to Save the applictaion'); + return; + } + }else if(this.applicationObj.isEnabled && !this.applicationObj.restrictedApp){ + if((this.applicationObj.name == 'undefined' || !this.applicationObj.name) + ||(this.applicationObj.url == 'undefined'|| !this.applicationObj.url) + ||(this.applicationObj.username == 'undefined' || !this.applicationObj.username)|| + (this.applicationObj.appPassword== 'undefined' || !this.applicationObj.appPassword)) { + + this.openConfirmationModal('','Please fill in all required fields(*) along with password as the app is not centralized'); + return; + } + }else if(this.applicationObj.isEnabled && this.applicationObj.restrictedApp){ + if((this.applicationObj.name == 'undefined' || !this.applicationObj.name) ||(this.applicationObj.url == 'undefined' + || !this.applicationObj.url)){ + this.openConfirmationModal('','Please fill in all required fields(*)'); + return; + } + } + } + + //URL Validation + if(this.applicationObj.isEnabled){ + if(this.applicationObj.url && this.applicationObj.url !='undefined' && this.applicationObj.url != ''){ + let isValidURL = this.isUrlValid(this.applicationObj.url); + if(!isValidURL){ + this.openConfirmationModal('Error','Application URL must be a valid URL.'); + return; + } + }else{ + this.openConfirmationModal('Error','Application URL is required.'); + return; + } + } + + + this.isSaving = true; + // For a restricted app, null out all irrelevant fields + if(this.applicationObj.restrictedApp) { + this.newAppModel.restUrl = null; + this.newAppModel.isOpen = true; + this.newAppModel.username = null; + this.newAppModel.appPassword = null; + this.newAppModel.uebTopicName = null; + this.newAppModel.uebKey = null; + this.newAppModel.uebSecret = null; + + /**Need to set below fields values based on input provided in the dialog */ + this.newAppModel.restrictedApp = this.applicationObj.restrictedApp; + this.newAppModel.name = this.applicationObj.name; + this.newAppModel.url = this.applicationObj.url; + if(this.applicationObj.isEnabled){ + this.newAppModel.isEnabled = this.applicationObj.isEnabled; + }else{ + this.newAppModel.isEnabled = false; + } + + }else{ + + /**Need to set below fields values based on input provided in the dialog */ + this.newAppModel.restrictedApp = false; + this.newAppModel.name = this.applicationObj.name; + this.newAppModel.url = this.applicationObj.url; + this.newAppModel.restUrl = this.applicationObj.restUrl; + this.newAppModel.username = this.applicationObj.username; + this.newAppModel.appPassword = this.applicationObj.appPassword; + + if(this.applicationObj.isEnabled){ + this.newAppModel.isEnabled = this.applicationObj.isEnabled; + }else{ + this.newAppModel.isEnabled = false; + } + + if(this.applicationObj.isOpen){ + this.newAppModel.isOpen = this.applicationObj.isOpen; + }else{ + this.newAppModel.isOpen = false; + } + //console.log("this.applicationObj.isOpen",this.applicationObj.isOpen); + + if(this.applicationObj.isCentralAuth){ + this.newAppModel.isCentralAuth = this.applicationObj.isCentralAuth; + }else{ + this.newAppModel.isCentralAuth = false; + } + + } + + if (this.applicationObj.nameSpace=="") { + this.newAppModel.nameSpace = null; + } + + if(this.isEditMode){ + this.applicationsService.updateOnboardingApp(this.applicationObj) + .subscribe( _data => { + this.result = _data; + //console.log("update application response :: ",this.result); + if(this.result !=null && this.result.httpStatusCode ==200){ + this.passEntry.emit(this.result); + this.ngbModal.dismissAll(); + }else{ + this.openConfirmationModal('Error','There was a problem updating the application changes. Please try again. If the problem persists, then try again later. Error: '+this.result); + return + } + }, error => { + console.log(error); + if(error.status == 409){ + this.openConfirmationModal('Error', 'There was a problem updating the application changes. ' + + 'The Application Name and URL should be unique. Error: ' + + error.status); + return; + }else if(error.status == 500){ + this.openConfirmationModal('Error', 'There was a problem updating the application information. ' + + 'Please try again later. Error: ' +error.status); + return; + }else if(error.status == 404 || error.status == 403){ + this.openConfirmationModal('Error', 'There was a problem updating the application information. ' + + 'Invalid namespace. Error: ' +error.status); + return; + }else if(error.status == 401){ + this.openConfirmationModal('Error', 'There was a problem updating the application information. ' + + 'Portal Mechid is unauthorized to access the given namespace. Error: ' +error.status); + return; + }else if(error.status == 400){ + this.openConfirmationModal('Error','Bad Request Error: ' + error.status); + return; + } else{ + this.openConfirmationModal('Error', 'There was a problem updating the application changes. ' + + 'Please try again. If the problem persists, then try again later. Error: ' + + error.status); + return; + } + }); + + }else{ + //console.log("Coming inside add application",this.newAppModel); + + this.applicationsService.addOnboardingApp(this.newAppModel) + .subscribe( _data => { + this.result = _data; + //console.log("Add application response :: ",this.result); + if(this.result !=null && this.result.httpStatusCode ==200){ + this.passEntry.emit(this.result); + this.ngbModal.dismissAll(); + }else{ + this.openConfirmationModal('Error','There was a problem adding the application changes. Please try again. If the problem persists, then try again later. Error: '+this.result); + return + } + }, error => { + console.log(error); + if(error.status == 409){ + this.openConfirmationModal('Error', 'There was a problem adding the application changes. ' + + 'The Application Name and URL should be unique. Error: ' + + error.status); + return; + } else if(error.status == 500){ + this.openConfirmationModal('Error', 'There was a problem adding the application information. ' + + 'Please try again later. Error: ' +error.status); + return; + }else if(error.status == 400){ + this.openConfirmationModal('Error','Bad Request Error: ' + error.status); + return; + } else{ + this.openConfirmationModal('Error', 'There was a problem adding the application changes. ' + + 'Please try again. If the problem persists, then try again later. Error: ' + + error.status); + return; + } + }); + } + } + + isUrlValid(userInput){ + //let regexQuery = "/^((?:https?\:\/\/|ftp?\:\/\/)?(w{3}.)?(?:[-a-z0-9]+\.)*[-a-z0-9]+.*)[^-_.]$/i"; + let regexQuery = "https?://.+"; + let res = userInput.match(regexQuery); + if(res == null){ + return false; + }else{ + return true; + } + } + + openConfirmationModal(_title: string, _message: string) { + const modalInfoRef = this.ngbModal.open(ConfirmationModalComponent); + modalInfoRef.componentInstance.title = _title; + modalInfoRef.componentInstance.message = _message; + } + + openInformationModal(_title: string, _message: string){ + const modalInfoRef = this.ngbModal.open(InformationModalComponent); + modalInfoRef.componentInstance.title = _title; + modalInfoRef.componentInstance.message = _message; + return modalInfoRef; + } +} diff --git a/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.html b/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.html new file mode 100644 index 00000000..8a678917 --- /dev/null +++ b/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.html @@ -0,0 +1,144 @@ +<!-- + ============LICENSE_START========================================== + ONAP Portal + =================================================================== + Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + =================================================================== + + Unless otherwise specified, all software contained herein is licensed + under the Apache License, Version 2.0 (the "License"); + you may not use this software 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. + + Unless otherwise specified, all documentation contained herein is licensed + under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + you may not use this documentation except in compliance with the License. + You may obtain a copy of the License at + + https://creativecommons.org/licenses/by/4.0/ + + Unless required by applicable law or agreed to in writing, documentation + 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. + + ============LICENSE_END============================================ + + --> + +<div class="container"> + <div class="w-ecomp-main-container"> + <div class="applications-page-main" id="page-content"> + <div id="microservice-onboarding-title" class="w-ecomp-main-view-title"> + <h1 class="heading-page"> Application Onboarding</h1> + </div> + + <mat-form-field> + <input matInput type="text" (keyup)="applyFilter($event.target.value)" placeholder="Search in entire table"> + </mat-form-field> + + <button type="button" style="float: right;" class="btn btn-primary" (click)="openAddApplicationModal('')"> + <i class="icon ion-md-person-add"></i> Add App + </button> + + <div class="apps-table"> + <!-- Applications Table goes here--> + <table mat-table [dataSource]="dataSource" matSort> + <!-- Thumbnail Column --> + <ng-container matColumnDef="thumbnail"> + <th id="col1" mat-header-cell *matHeaderCellDef> Thumbnail </th> + <td (click)="openAddApplicationModal(element)" id="rowheader_t1_{{i}}-thumbnail" + mat-cell *matCellDef="let element; let i = index;"> + <div><img class="small-thumbnail" src={{element.imageLink}}></div> + </td> + </ng-container> + + <!-- Application Name Column --> + <ng-container matColumnDef="applicationName"> + <th id="col2" mat-header-cell *matHeaderCellDef> Application Name </th> + <td (click)="openAddApplicationModal(element)" id="rowheader_t1_{{i}}-applicationName" + mat-cell *matCellDef="let element; let i=index;"> {{element.name}} </td> + </ng-container> + + <!-- Active Column --> + <ng-container matColumnDef="active"> + <th id="col2" mat-header-cell *matHeaderCellDef> Active </th> + <td (click)="openAddApplicationModal(element)" id="rowheader_t1_{{i}}-active" + mat-cell *matCellDef="let element; let i=index;"> {{(element.isEnabled) ? 'yes' : 'no'}} </td> + </ng-container> + + <!-- Integration Type Column --> + <ng-container matColumnDef="integrationType"> + <th id="col2" mat-header-cell *matHeaderCellDef> Integration Type </th> + <td (click)="openAddApplicationModal(element)" id="rowheader_t1_{{i}}-integrationType" + mat-cell *matCellDef="let element; let i=index;"> {{(element.restrictedApp) ? 'link' : 'standard'}} </td> + </ng-container> + + <!-- Guest Access Column--> + <ng-container matColumnDef="guestAccess"> + <th id="col2" mat-header-cell *matHeaderCellDef> Guest Access </th> + <td (click)="openAddApplicationModal(element)" id="rowheader_t1_{{i}}-guestAccess" + mat-cell *matCellDef="let element; let i=index;">{{(element.isOpen) ? 'yes' : 'no'}} </td> + </ng-container> + + <!-- url column --> + <ng-container matColumnDef="url"> + <th id="col2" mat-header-cell *matHeaderCellDef> Url </th> + <td (click)="openAddApplicationModal(element)" id="rowheader_t1_{{i}}-url" + mat-cell *matCellDef="let element; let i=index;">{{element.url}} </td> + </ng-container> + + <!-- rest url column --> + <ng-container matColumnDef="restURL"> + <th id="col2" mat-header-cell *matHeaderCellDef> REST Url </th> + <td (click)="openAddApplicationModal(element)" id="rowheader_t1_{{i}}-restURL" + mat-cell *matCellDef="let element; let i=index;">{{element.restUrl}} </td> + </ng-container> + + <!-- communicationKey column --> + <ng-container matColumnDef="communicationKey"> + <th id="col2" mat-header-cell *matHeaderCellDef> Communication Key </th> + <td (click)="openAddApplicationModal(element)" id="rowheader_t1_{{i}}-communicationKey" + mat-cell *matCellDef="let element; let i=index;">{{element.uebKey}} </td> + </ng-container> + + <!-- applicationNamespace column --> + <ng-container matColumnDef="applicationNamespace"> + <th id="col2" mat-header-cell *matHeaderCellDef> Application Namespace </th> + <td (click)="openAddApplicationModal(element)" id="rowheader_t1_{{i}}-applicationNamespace" + mat-cell *matCellDef="let element; let i=index;">{{element.nameSpace}} </td> + </ng-container> + + <!-- centralAuthAccess column --> + <ng-container matColumnDef="centralAuthAccess"> + <th id="col2" mat-header-cell *matHeaderCellDef> Central Auth Access </th> + <td (click)="openAddApplicationModal(element)" id="rowheader_t1_{{i}}-centralAuthAccess" + mat-cell *matCellDef="let element; let i=index;">{{(element.isCentralAuth) ? 'yes' : 'no'}} </td> + </ng-container> + + <!-- Delete Column --> + <ng-container matColumnDef="delete"> + <th id="col4" mat-header-cell *matHeaderCellDef> Delete </th> + <td id="rowheader_t1_{{i}}" mat-cell *matCellDef="let element; let i=index;"> + <span class="icon-trash" id="{{i}}-button-portal-admin-remove" (click)="deleteApplication(element)"> + <i class="icon ion-md-trash"></i> + </span> + </td> + </ng-container> + <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> + <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> + </table> + <mat-paginator [pageSizeOptions]="[10, 20]" showFirstLastButtons></mat-paginator> + </div> + </div> + </div> + </div>
\ No newline at end of file diff --git a/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.scss b/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.scss new file mode 100644 index 00000000..6bca524d --- /dev/null +++ b/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.scss @@ -0,0 +1,59 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ + + +.container{ + overflow-y: auto; +} + +.container .apps-table .small-thumbnail { + width: 72px; + height: 60px; + border: 1px solid#d2d2d2; + border-radius: 2px; +} + +.container .apps-table th{ + padding-bottom: 15px; + padding-right: 40px; + font-weight: bold; +} + +.ion-md-trash{ + cursor: pointer; +}
\ No newline at end of file diff --git a/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.spec.ts b/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.spec.ts new file mode 100644 index 00000000..8e198387 --- /dev/null +++ b/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.spec.ts @@ -0,0 +1,63 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ + +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ApplicationOnboardingComponent } from './application-onboarding.component'; + +describe('ApplicationOnboardingComponent', () => { + let component: ApplicationOnboardingComponent; + let fixture: ComponentFixture<ApplicationOnboardingComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ApplicationOnboardingComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ApplicationOnboardingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.ts b/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.ts new file mode 100644 index 00000000..8cbf1d28 --- /dev/null +++ b/portal-FE-os/src/app/pages/application-onboarding/application-onboarding.component.ts @@ -0,0 +1,188 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ + +import { Component, OnInit, ViewChild, Input} from '@angular/core'; +import { MatTableDataSource } from '@angular/material'; +import { MatSort, MatPaginator } from '@angular/material'; +import { ApplicationsService } from '../../shared/services/index'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { IApplications } from 'src/app/shared/model/applications-onboarding/applications'; +import { environment } from '../../../environments/environment'; +import { ApplicationDetailsDialogComponent } from './application-details-dialog/application-details-dialog.component'; +import { ConfirmationModalComponent } from 'src/app/modals/confirmation-modal/confirmation-modal.component'; +import { InformationModalComponent } from 'src/app/modals/information-modal/information-modal.component'; + +@Component({ + selector: 'app-application-onboarding', + templateUrl: './application-onboarding.component.html', + styleUrls: ['./application-onboarding.component.scss'] +}) +export class ApplicationOnboardingComponent implements OnInit { + + api = environment.api; + appsList: Array<IApplications> = []; + result: any; + isEditMode: boolean = false; + emptyImgForPreview: string; + isUserSuperAdmin: boolean = false; + displayedColumns: string[] = ['thumbnail', 'applicationName','active', + 'integrationType', 'guestAccess', 'url','restURL', + 'communicationKey', 'applicationNamespace', 'centralAuthAccess']; + dataSource = new MatTableDataSource(this.appsList); + @ViewChild(MatSort) sort: MatSort; + @ViewChild(MatPaginator) paginator: MatPaginator; + + constructor(public applicationsService: ApplicationsService, public ngbModal: NgbModal) { } + + ngOnInit() { + this.emptyImgForPreview = '../../../assets/images/default_app_image.gif'; + this.checkIfUserIsSuperAdmin(); + this.getOnboardingApps(); + } + + getOnboardingApps(){ + //console.log("getOnboardingApps called"); + this.applicationsService.getOnboardingApps() + .subscribe(_data => { + this.result = _data; + if (this.result == null || this.result == 'undefined') { + console.log('WidgetOnboardingService::getOnboardingWidgets Failed: Result or result.data is null'); + }else { + this.appsList = _data; + for (var i = 0; i < this.appsList.length; i++) { + this.appsList[i].imageLink = ''; + if (this.appsList[i].imageUrl){ + this.appsList[i].imageLink = this.api.appThumbnail.replace(':appId', this.appsList[i].id); + this.appsList[i].imageLink = this.appsList[i].imageLink+'?' + new Date().getTime(); + }else{ + this.appsList[i].imageLink = this.emptyImgForPreview; + } + } + this.populateTableData(this.appsList); + } + }, error =>{ + console.log(error); + this.openConfirmationModal('Error', error.message); + }); + } + + applyFilter(filterValue: string) { + this.dataSource.filter = filterValue.trim().toLowerCase(); + }; + + + populateTableData(appsList: Array<IApplications>){ + this.dataSource = new MatTableDataSource(appsList); + this.dataSource.sort = this.sort; + this.dataSource.paginator = this.paginator; + }; + + openAddApplicationModal(rowData: any) { + const modalRef = this.ngbModal.open(ApplicationDetailsDialogComponent, { size: 'lg' }); + modalRef.componentInstance.title = 'Application Details'; + //console.log("selectedData in parent",rowData); + if(rowData != 'undefined' && rowData){ + modalRef.componentInstance.applicationObj = rowData; + this.isEditMode = true; + }else{ + modalRef.componentInstance.applicationObj = {}; + this.isEditMode = false; + } + modalRef.componentInstance.passEntry.subscribe((receivedEntry: any) => { + //console.log("receivedEntry >>> ",receivedEntry); + if(receivedEntry){ + this.appsList.push(receivedEntry); + //this.populateTableData(this.appsList); + this.getOnboardingApps(); + } + }); + } + + deleteApplication(application: IApplications){ + let confirmationMsg = 'You are about to delete this App : ' + application.name+ '. Click OK to continue.'; + this.openInformationModal("Confirmation",confirmationMsg).result.then((result) => { + if (result === 'Ok') { + if(!application || application == null){ + console.log('ApplicationOnboardingCtrl::deleteApplication: No apllication or ID... cannot delete'); + return; + } + this.appsList.splice(this.appsList.indexOf(application), 1); + this.applicationsService.deleteOnboardingApp(application.id) + .subscribe( data => { + this.result = data; + this.getOnboardingApps(); + }, error => { + console.log(error); + this.openConfirmationModal('Error', error); + }); + } + }, (resut) => { + return; + }) + } + + checkIfUserIsSuperAdmin(){ + this.applicationsService.checkIfUserIsSuperAdmin() + .subscribe(res => { + if(res) { + this.isUserSuperAdmin = true; + this.displayedColumns = ['thumbnail', 'applicationName','active', + 'integrationType', 'guestAccess', 'url','restURL', + 'communicationKey', 'applicationNamespace', 'centralAuthAccess', 'delete']; + } + //console.log("isUserSuperAdmin :: ",this.isUserSuperAdmin); + }, error =>{ + console.log(error); + this.openConfirmationModal('Error', 'ApplicationsCtrl.checkIfUserIsSuperAdmin:: Failed '+error.message); + }); + } + + openConfirmationModal(_title: string, _message: string) { + const modalInfoRef = this.ngbModal.open(ConfirmationModalComponent); + modalInfoRef.componentInstance.title = _title; + modalInfoRef.componentInstance.message = _message; + } + + openInformationModal(_title: string, _message: string){ + const modalInfoRef = this.ngbModal.open(InformationModalComponent); + modalInfoRef.componentInstance.title = _title; + modalInfoRef.componentInstance.message = _message; + return modalInfoRef; + } + +} diff --git a/portal-FE-os/src/app/pages/pages-routing.module.ts b/portal-FE-os/src/app/pages/pages-routing.module.ts new file mode 100644 index 00000000..d39db03c --- /dev/null +++ b/portal-FE-os/src/app/pages/pages-routing.module.ts @@ -0,0 +1,89 @@ +/* + * ============LICENSE_START========================================== + * ONAP Portal SDK + * =================================================================== + * Copyright � 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { AdminsComponent } from './admins/admins.component'; +import { DashboardComponent } from '../pages/dashboard/dashboard.component'; +import { PortalAdminsComponent } from './portal-admins/portal-admins.component'; +import { RoleComponent } from './role/role.component'; +import { UsersComponent } from './users/users.component'; +import { FunctionalMenuComponent } from './functional-menu/functional-menu.component'; +import { UserNotificationAdminComponent } from './user-notification-admin/user-notification-admin.component'; +import { WebAnalyticsComponent } from './web-analytics/web-analytics.component'; +import { ApplicationCatalogComponent } from './application-catalog/application-catalog.component'; +import { WidgetCatalogComponent } from './widget-catalog/widget-catalog.component'; +import { MicroserviceOnboardingComponent } from './microservice-onboarding/microservice-onboarding.component'; +import { ApplicationOnboardingComponent } from './application-onboarding/application-onboarding.component'; +import { WidgetOnboardingComponent } from './widget-onboarding/widget-onboarding.component'; +import { AccountOnboardingComponent } from './account-onboarding/account-onboarding.component'; +import { ContactUsComponent } from './contact-us/contact-us.component'; +import { RoleFunctionsComponent } from './role/role-functions/role-functions.component'; +import { NotificationHistoryComponent } from './notification-history/notification-history.component'; +import { GetAccessComponent } from './get-access/get-access.component'; + +const routes: Routes = [ + { path: '', component: DashboardComponent }, + { path: 'applicationsHome', component: DashboardComponent }, + { path: 'admins', component: AdminsComponent }, + { path: 'portalAdmins', component: PortalAdminsComponent }, + { path: 'appCatalog', component: ApplicationCatalogComponent }, + { path: 'widgetCatalog', component: WidgetCatalogComponent }, + { path: 'roles', component: RoleComponent }, + { path: 'roleFunctions', component: RoleFunctionsComponent }, + { path: 'users', component: UsersComponent }, + { path: 'applications', component: ApplicationOnboardingComponent }, + { path: 'widgetOnboarding', component: WidgetOnboardingComponent }, + { path: 'functionalMenu', component: FunctionalMenuComponent }, + { path: 'userNotifications', component: UserNotificationAdminComponent }, + { path: 'microserviceOnboarding', component: MicroserviceOnboardingComponent }, + { path: 'accountOnboarding', component: AccountOnboardingComponent }, + { path: 'webAnlayticsSource', component: WebAnalyticsComponent }, + { path: 'contactUs', component: ContactUsComponent }, + { path: 'getAccess', component: GetAccessComponent }, + { path: 'recentNotifications', component: NotificationHistoryComponent }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class PagesRoutingModule { } + + + diff --git a/portal-FE-os/src/app/pages/pages.component.html b/portal-FE-os/src/app/pages/pages.component.html new file mode 100644 index 00000000..9196ffe5 --- /dev/null +++ b/portal-FE-os/src/app/pages/pages.component.html @@ -0,0 +1,39 @@ +<!-- + ============LICENSE_START========================================== + ONAP Portal + =================================================================== + Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + =================================================================== + + Unless otherwise specified, all software contained herein is licensed + under the Apache License, Version 2.0 (the "License"); + you may not use this software 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. + + Unless otherwise specified, all documentation contained herein is licensed + under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + you may not use this documentation except in compliance with the License. + You may obtain a copy of the License at + + https://creativecommons.org/licenses/by/4.0/ + + Unless required by applicable law or agreed to in writing, documentation + 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. + + ============LICENSE_END============================================ + + + --> + +<router-outlet></router-outlet>
\ No newline at end of file diff --git a/portal-FE-os/src/app/pages/pages.component.scss b/portal-FE-os/src/app/pages/pages.component.scss new file mode 100644 index 00000000..b7697e62 --- /dev/null +++ b/portal-FE-os/src/app/pages/pages.component.scss @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ +/* ecomp page main title */ +.ecomp-main-view-title { + color: #191919; + font-size: 24px; + width: 1170px; + padding-bottom: 15px; + margin: auto; +} + +.heading-page { + margin-bottom: 40px; +}
\ No newline at end of file diff --git a/portal-FE-os/src/app/pages/pages.component.spec.ts b/portal-FE-os/src/app/pages/pages.component.spec.ts new file mode 100644 index 00000000..1bef834a --- /dev/null +++ b/portal-FE-os/src/app/pages/pages.component.spec.ts @@ -0,0 +1,62 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PagesComponent } from './pages.component'; + +describe('PagesComponent', () => { + let component: PagesComponent; + let fixture: ComponentFixture<PagesComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PagesComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PagesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/portal-FE-os/src/app/pages/pages.component.ts b/portal-FE-os/src/app/pages/pages.component.ts new file mode 100644 index 00000000..69153667 --- /dev/null +++ b/portal-FE-os/src/app/pages/pages.component.ts @@ -0,0 +1,52 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-pages', + templateUrl: './pages.component.html', + styleUrls: ['./pages.component.scss'] +}) +export class PagesComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/portal-FE-os/src/app/pages/pages.module.ts b/portal-FE-os/src/app/pages/pages.module.ts new file mode 100644 index 00000000..7ccb3f95 --- /dev/null +++ b/portal-FE-os/src/app/pages/pages.module.ts @@ -0,0 +1,230 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MatIconModule } from '@angular/material/icon'; +import { MatCheckboxModule } from '@angular/material/checkbox'; + +import { AdminsComponent } from './admins/admins.component'; +import { ApplicationCatalogComponent } from './application-catalog/application-catalog.component'; +import { ApplicationOnboardingComponent } from './application-onboarding/application-onboarding.component'; +import { ApplicationDetailsDialogComponent } from './application-onboarding/application-details-dialog/application-details-dialog.component'; +import { AccountOnboardingComponent } from './account-onboarding/account-onboarding.component'; +import { ApplicationCatalogService } from '../shared/services/application-catalog/application-catalog.service'; +import { ContactUsComponent } from './contact-us/contact-us.component'; +import { ContactUsManageComponent } from './contact-us/contact-us-manage/contact-us-manage.component'; +import { ConfirmationModalComponent } from '../modals/confirmation-modal/confirmation-modal.component'; +import { CatalogModalComponent } from './catalog-modal/catalog-modal.component'; +import { DashboardComponent } from '../pages/dashboard/dashboard.component'; +import { FunctionalMenuComponent } from './functional-menu/functional-menu.component'; +import { GridsterModule } from 'angular-gridster2'; +import { InformationModalComponent } from '../modals/information-modal/information-modal.component'; +import { NgMaterialModule } from '../ng-material-module'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NewPortalAdminComponent } from './portal-admins/new-portal-admin/new-portal-admin.component'; +import { NotificationHistoryComponent } from './notification-history/notification-history.component'; +import { PagesComponent } from './pages.component'; +import { PagesRoutingModule } from './pages-routing.module'; +import { PortalAdminsComponent } from './portal-admins/portal-admins.component'; +import { RoleComponent } from './role/role.component'; +import { SearchUsersComponent } from '../layout/components/search-users/search-users.component'; +import { SchedulerComponent } from './scheduler/scheduler.component'; +import { UserNotificationAdminComponent } from './user-notification-admin/user-notification-admin.component'; +import { WidgetsComponent } from './widgets/widgets.component'; +import { WidgetCatalogComponent } from './widget-catalog/widget-catalog.component'; +import { WebAnalyticsComponent } from './web-analytics/web-analytics.component'; +import { NewAdminComponent } from './admins/new-admin/new-admin.component'; +import { ExternalRequestAccessService } from '../shared/services/external-request-access-service/external-request-access.service'; +import { UsersService } from '../shared/services/users/users.service'; +import { DynamicWidgetComponent } from './dynamic-widget/dynamic-widget.component'; +import { MicroserviceOnboardingComponent } from './microservice-onboarding/microservice-onboarding.component'; +import { WidgetOnboardingComponent } from './widget-onboarding/widget-onboarding.component'; +import { WebAnalyticsDetailsDialogComponent } from './web-analytics/web-analytics-details-dialog/web-analytics-details-dialog.component'; +import { BulkUploadRoleComponent } from './role/bulk-upload-role/bulk-upload-role.component'; +import { AddRoleComponent } from './role/add-role/add-role.component'; +import { RoleFunctionsComponent } from './role/role-functions/role-functions.component'; +import { RoleFunctionModalComponent } from './role/role-functions/role-function-modal/role-function-modal.component'; +import { NewNotificationModalComponent } from './user-notification-admin/new-notification-modal/new-notification-modal.component'; +import { AccountAddDetailsComponent } from './account-onboarding/account-add-details/account-add-details.component'; +import { MicroserviceAddDetailsComponent } from './microservice-onboarding/microservice-add-details/microservice-add-details.component'; +import { DashboardApplicationCatalogComponent } from './dashboard-application-catalog/dashboard-application-catalog.component'; +import { DashboardWidgetCatalogComponent } from './dashboard-widget-catalog/dashboard-widget-catalog.component'; +import { WidgetDetailsDialogComponent } from './widget-onboarding/widget-details-dialog/widget-details-dialog.component'; +import { FunctionalMenuDialogComponent } from './functional-menu/functional-menu-dialog/functional-menu-dialog.component'; +import { GetAccessComponent } from './get-access/get-access.component'; +import { PluginModule } from '../shared/plugin/plugin.module'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { HeaderInterceptor } from '../shared/interceptors/header-interceptor'; +import { ApplicationPipesModule } from '../shared/pipes/application-pipes.module'; +import { UsersComponent } from './users/users.component'; +import { NewUserModalComponent } from './users/new-user-modal/new-user-modal.component'; +import { BulkUserComponent } from './users/bulk-user/bulk-user.component'; +import { UserDetailsFormComponent } from './users/user-details-form/user-details-form.component'; + + +@NgModule({ + declarations: [ + PagesComponent, + DashboardComponent, + PortalAdminsComponent, + AccountOnboardingComponent, + FunctionalMenuComponent, + MicroserviceOnboardingComponent, + NotificationHistoryComponent, + RoleComponent, + SchedulerComponent, + UserNotificationAdminComponent, + UsersComponent, + WidgetCatalogComponent, + WidgetOnboardingComponent, + WidgetsComponent, + WebAnalyticsComponent, + NewAdminComponent, + NewUserModalComponent, + BulkUserComponent, + SearchUsersComponent, + InformationModalComponent, + ConfirmationModalComponent, + AdminsComponent, + NewPortalAdminComponent, + SchedulerComponent, + UserNotificationAdminComponent, + WidgetCatalogComponent, + ApplicationOnboardingComponent, + AccountOnboardingComponent, + ApplicationDetailsDialogComponent, + ContactUsComponent, + ContactUsManageComponent, + WebAnalyticsDetailsDialogComponent, + ApplicationCatalogComponent, + WidgetCatalogComponent, + CatalogModalComponent, + DynamicWidgetComponent, + BulkUploadRoleComponent, + AddRoleComponent, + RoleFunctionsComponent, + RoleFunctionModalComponent, + NewNotificationModalComponent, + AccountAddDetailsComponent, + MicroserviceAddDetailsComponent, + WidgetDetailsDialogComponent, + DashboardApplicationCatalogComponent, + DashboardWidgetCatalogComponent, + FunctionalMenuDialogComponent, + GetAccessComponent, + UserDetailsFormComponent + ], + imports: [ + CommonModule, + NgMaterialModule, + ReactiveFormsModule, + FormsModule, + PagesRoutingModule, + ApplicationPipesModule, + NgbModule, + GridsterModule, + MatIconModule, + MatCheckboxModule, + FormsModule, + PluginModule + ], + entryComponents: [ + SchedulerComponent, + InformationModalComponent, + SearchUsersComponent, + ConfirmationModalComponent, + NewPortalAdminComponent, + NewAdminComponent, + BulkUserComponent, + NewUserModalComponent, + ApplicationDetailsDialogComponent, + ContactUsManageComponent, + CatalogModalComponent, + WebAnalyticsDetailsDialogComponent, + AddRoleComponent, + BulkUploadRoleComponent, + RoleFunctionModalComponent, + NewNotificationModalComponent, + AccountAddDetailsComponent, + MicroserviceAddDetailsComponent, + WidgetDetailsDialogComponent, + FunctionalMenuDialogComponent + ], + providers: [ + { + provide: HTTP_INTERCEPTORS, + useClass: HeaderInterceptor, + multi: true, + }], + + +}) +export class PagesModule { + + constructor(public ngbModalService: NgbModal) { + this.addSchdulerEventListners(); + } + + + addSchdulerEventListners() { + let eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; + let eventer = window[eventMethod]; + let messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message"; + + eventer(messageEvent, function (e) { + if (e.data != null && e.data['widgetName'] == 'Portal-Common-Scheduler') { + this.openAddSchedulerModal(e.data); + } + }.bind(this), false); + + } + + openAddSchedulerModal(rowData: any) { + const modalRef = this.ngbModalService.open(SchedulerComponent, { size: 'lg' }); + modalRef.componentInstance.title = 'Scheduler Change'; + console.log("selectedData in parent", rowData); + if (rowData != 'undefined' && rowData) { + modalRef.componentInstance.payload = rowData; + } else { + modalRef.componentInstance.payload = {}; + } + + } +} diff --git a/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.html b/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.html new file mode 100644 index 00000000..927b5073 --- /dev/null +++ b/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.html @@ -0,0 +1,125 @@ +<!-- + ============LICENSE_START========================================== + ONAP Portal + =================================================================== + Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + =================================================================== + + Unless otherwise specified, all software contained herein is licensed + under the Apache License, Version 2.0 (the "License"); + you may not use this software 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. + + Unless otherwise specified, all documentation contained herein is licensed + under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + you may not use this documentation except in compliance with the License. + You may obtain a copy of the License at + + https://creativecommons.org/licenses/by/4.0/ + + Unless required by applicable law or agreed to in writing, documentation + 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. + + ============LICENSE_END============================================ + + + --> +<div class="container" *ngIf="dialogState===1"> + <div class="modal-header"> + <h4 class="modal-title">{{title}}</h4> + <button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross')"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body" *ngIf="!addUserFlag"> + <app-search-users [searchTitle]="searchTitleText" [isSystemUser]="isSystemUserCheck" [placeHolder]="placeholderText" + (passBackSelectedUser)='changeSelectedUser($event)' (userNotFoundFlag)='addNewUser($event)'></app-search-users> + </div> + <div *ngIf="addUserFlag"> + <app-user-details-form></app-user-details-form> + </div> + <div class="modal-footer" *ngIf="!addUserFlag"> + <button type="submit" class="btn btn-primary" [disabled]='!changedSelectedUser && !isSystemUserCheck' + (click)="getUserAppsRoles()">Next</button> + <button type="button" class="btn btn-primary" (click)="activeModal.close('Close')">Close</button> + </div> +</div> +<div class="container" *ngIf="dialogState===2"> + <div class="modal-header"> + <h4 class="modal-title">{{userTitle}}</h4> + <button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross')"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body adminApps"> + <!-- User App Roles List --> + <span class="ecomp-spinner" *ngIf="adminApps.length === 0"></span> + <div class="container adminApps" *ngIf="adminApps.length > 0"> + Access and roles: + <table mat-table [dataSource]="userAdminAppsSource"> + <!-- Search Result Column--> + <ng-container matColumnDef="applications"> + <th id="rowheader-result" mat-header-cell *matHeaderCellDef> Application + <td id="table-data-{{i}}" mat-cell *matCellDef="let element; let i = index;">{{element.name}} + </td> + </ng-container> + <ng-container matColumnDef="roles"> + <th id="rowheader-result" mat-header-cell *matHeaderCellDef> Roles + <td id="rowheader_t1_{{i}}-roles" mat-cell *matCellDef="let element; let i=index;"> + <mat-form-field + [hidden]="element.isError || element.showSpinner || element.noChanges || element.isUpdating || element.isDoneUpdating || element.isErrorUpdating"> + <mat-select (selectionChange)="roleSelectChange(element)" + [(ngModel)]="modelSelectedRoles" multiple> + <mat-option *ngFor="let appRole of element.appRoles; let j = index;" [value]="appRole"> + {{appRole.roleName}} + </mat-option> + </mat-select> + </mat-form-field> + <span id="app-item-no-contact" class="app-item-right-error" + [hidden]="!element.isError">{{element.errorMessage}}</span> + <span id="app-item-contacting" class="app-item-right-contacting" [hidden]="!element.showSpinner">Contacting + application... <span class="ecomp-small-spinner" *ngIf="showSpinner"></span></span> + <span id="app-item-no-changes" class="app-item-right-contacting" [hidden]="!element.noChanges">No + changes</span> + <span id="app-item-no-updating" class="app-item-right-contacting" [hidden]="!element.isUpdating">Updating + application... <span class="ecomp-small-spinner" *ngIf="showSpinner"></span></span> + <span id="app-item-done-updating" class="app-item-right-contacting" + [hidden]="!element.isDoneUpdating">Finished updating application</span> + <span id="app-item-cannot-update" class="app-item-right-error" [hidden]="!element.isErrorUpdating">Could not + update application... </span> + </td> + </ng-container> + <ng-container matColumnDef="delete"> + <th id="rowheader-result" mat-header-cell *matHeaderCellDef> Delete + <td id="table-data-{{i}}" mat-cell *matCellDef="let element; let i = index;"> + <span class="icon-trash" id="app-item-delete-{{i}}" [hidden]="element.showSpinner || element.isError" + (click)="deleteApp(element)"> + <i class="icon ion-md-trash"></i> + </span> + </td> + </ng-container> + <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr> + <tr [hidden]="row.isDeleted" mat-row id="table-row-{{i}}" + *matRowDef="let row; columns: displayedColumns; let i = index;"></tr> + </table> + </div> + </div> + <div class="modal-footer"> + <button [hidden]="disableBack" type="submit" class="btn btn-primary" [disabled]='!changedSelectedUser' + (click)="navigateBack()">Back</button> + <button type="submit" class="btn btn-primary" [disabled]='!anyChanges' (click)="updateUserAppsRoles()">Save</button> + + <button type="button" class="btn btn-primary" (click)="activeModal.close('Close')">Close</button> + </div> +</div> diff --git a/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.scss b/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.scss new file mode 100644 index 00000000..b919f362 --- /dev/null +++ b/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.scss @@ -0,0 +1,51 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ +.icon-trash { + cursor: pointer; + font-size: 20px; +} + +.app-item-right-error { + color: red; +} + +::ng-deep .mat-pseudo-checkbox-checked::after { + left: 1px !important; + width: 11px !important; + height: 5px !important; +}
\ No newline at end of file diff --git a/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.spec.ts b/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.spec.ts new file mode 100644 index 00000000..beb998cf --- /dev/null +++ b/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.spec.ts @@ -0,0 +1,62 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NewUserModalComponent } from './new-user-modal.component'; + +describe('NewUserModalComponent', () => { + let component: NewUserModalComponent; + let fixture: ComponentFixture<NewUserModalComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ NewUserModalComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NewUserModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.ts b/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.ts new file mode 100644 index 00000000..76c14f17 --- /dev/null +++ b/portal-FE-os/src/app/pages/users/new-user-modal/new-user-modal.component.ts @@ -0,0 +1,322 @@ +/*- + * ============LICENSE_START========================================== + * ONAP Portal + * =================================================================== + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * + * Unless otherwise specified, all software contained herein is licensed + * under the Apache License, Version 2.0 (the "License"); + * you may not use this software 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. + * + * Unless otherwise specified, all documentation contained herein is licensed + * under the Creative Commons License, Attribution 4.0 Intl. (the "License"); + * you may not use this documentation except in compliance with the License. + * You may obtain a copy of the License at + * + * https://creativecommons.org/licenses/by/4.0/ + * + * Unless required by applicable law or agreed to in writing, documentation + * 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. + * + * ============LICENSE_END============================================ + * + * + */ +import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core'; +import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ApplicationsService, UsersService } from 'src/app/shared/services'; +import { MatTableDataSource, MatRadioChange } from '@angular/material'; +import { UserAdminApps, UserAccessRoles } from 'src/app/shared/model'; +import { ConfirmationModalComponent } from 'src/app/modals/confirmation-modal/confirmation-modal.component'; +import { InformationModalComponent } from 'src/app/modals/information-modal/information-modal.component'; +import { HttpErrorResponse } from '@angular/common/http'; + +@Component({ + selector: 'app-new-user-modal', + templateUrl: './new-user-modal.component.html', + styleUrls: ['./new-user-modal.component.scss'] +}) +export class NewUserModalComponent implements OnInit { + @Input() dialogState: number; + @Input() userTitle: string; + @Input() disableBack: boolean; + @Input() adminModalData: any; + @Input() userModalData: any; + @Output() passBackNewUserPopup: EventEmitter<any> = new EventEmitter(); + changedSelectedUser: any; + showSpinner: boolean; + isGettingAdminApps: boolean; + adminApps: any; + modelSelectedRoles: any; + appRoles: Array<UserAccessRoles>; + numberAppsProcessed: number; + isSystemUserCheck = false; + extRequestValue = false; + searchTitleText = 'Enter First Name, Last Name or Org-ID'; + placeholderText = 'Search'; + ngRepeatDemo = [ + { id: 'userButton', value: 'true', labelValue: 'User' }, + { id: 'systemUserButton', value: 'false', labelValue: 'System' } + ] + selectedvalueradioButtonGroup = { + type: 'true' + } + userRadioSearchButton = this.ngRepeatDemo[0].labelValue; + displayedColumns: string[] = ['applications', 'roles', 'delete']; + userAdminAppsSource = new MatTableDataSource(this.adminApps); + showAppSpinner: boolean; + isError: boolean; + errorMessage: any; + originalSelectedRoles: any; + numberAppsSucceeded: number; + anyChanges: boolean; + orgUserIdValue: string; + constructor(public ngbModal: NgbModal, public activeModal: NgbActiveModal, private applicationsService: ApplicationsService, private usersService: UsersService) { } + + ngOnInit() { + this.appRoles = []; + this.adminApps = []; + this.anyChanges = false; + this.modelSelectedRoles = []; + this.originalSelectedRoles = []; + this.changedSelectedUser = null; + if (this.dialogState === 2) { + this.changedSelectedUser = this.userModalData; + this.orgUserIdValue = this.changedSelectedUser.orgUserId; + this.getUserAppsRoles(); + } + } + + addUserFlag : boolean = false; + + addNewUser(newUserFlag : boolean){ + console.log("New user flag ", newUserFlag); + this.addUserFlag = true; + } + + changeSelectedUser(user: any) { + this.changedSelectedUser = user; + if (this.changedSelectedUser.firstName === undefined || this.changedSelectedUser.lastName === undefined) { + this.userTitle = this.changedSelectedUser; + this.orgUserIdValue = this.changedSelectedUser; + } + else { + this.orgUserIdValue = this.changedSelectedUser.orgUserId; + this.userTitle = `${this.changedSelectedUser.firstName}, ` + ` ${this.changedSelectedUser.lastName} ` + ` (${this.changedSelectedUser.orgUserId})`; + } + } + + searchUserRadioChange($event: MatRadioChange) { + if ($event.value === 'System') { + this.searchTitleText = 'Enter System UserID'; + this.placeholderText = 'xxxxxx@org.com'; + this.isSystemUserCheck = true; + } else { + this.searchTitleText = 'Enter First Name, Last Name or ATTUID'; + this.placeholderText = 'Search'; + this.isSystemUserCheck = false; + } + } + + navigateBack() { + this.anyChanges = false; + this.dialogState = 1; + } + + roleSelectChange(element: any) { // update this.adminApps list when user select roles from dropdown + let existingSelectedRoles = this.modelSelectedRoles; + this.modelSelectedRoles = []; + this.anyChanges = true; + this.adminApps.forEach(_item => { + if (_item.id === element.id) { + _item['isChanged'] = true; + } + let appRoleList = _item.appRoles; + if (appRoleList != undefined) { + appRoleList.forEach(_itemRole => { + if (_itemRole.appId === element.id) { + const index = existingSelectedRoles.indexOf(_itemRole); + if (index != -1) { + _itemRole.isApplied = true; + } else { + _itemRole.isApplied = false; + } + } + if (_itemRole.isApplied) + this.modelSelectedRoles.push(_itemRole); + }); + } + }); + } + + getUserAppsRoles() { + if (this.changedSelectedUser === undefined) { + this.dialogState = 1; + return; + } + this.isGettingAdminApps = true; + this.applicationsService.getAdminApps().subscribe((apps: Array<UserAdminApps>) => { + this.isGettingAdminApps = false; + if (!apps || !apps.length) { + return null; + } + this.adminApps = apps; + this.dialogState = 2; + this.numberAppsProcessed = 0; + this.showSpinner = 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['showSpinner'] = true; + app['isError'] = false; + app['isDeleted'] = false; + app['printNoChanges'] = false; + app['isUpdating'] = false; + app['isErrorUpdating'] = false; + app['isDoneUpdating'] = false; + app['errorMessage'] = ""; + this.usersService.getUserAppRoles(app.id, this.orgUserIdValue, this.extRequestValue, this.isSystemUserCheck).toPromise().then((userAppRolesResult) => { + app['appRoles'] = userAppRolesResult; + app['showSpinner'] = false; + for (var i = 0; i < app['appRoles'].length; i++) { + app['appRoles'][i]['appId'] = app.id; + if (app['appRoles'][i].roleName.indexOf('global_') != -1) { + app['appRoles'][i].roleName = '*' + app['appRoles'][i].roleName; + } + if (app['appRoles'][i].isApplied) + this.modelSelectedRoles.push(app['appRoles'][i]); + } + }).catch((err: HttpErrorResponse) => { + app['isError'] = true; + app['showSpinner'] = false; + if (err.status == 200 || err.message.toLowerCase().includes("rollback")) + app['errorMessage'] = 'Error: ' + 500; + else + app['errorMessage'] = 'Error: ' + err.status; + }).finally(() => { + this.numberAppsProcessed++; + if (this.numberAppsProcessed === this.adminApps.length) { + this.originalSelectedRoles = this.modelSelectedRoles; + this.showSpinner = false; + } + }); + }) + this.userAdminAppsSource = new MatTableDataSource(this.adminApps); + }, (_err) => { + + }) + } + + updateUserAppsRoles() { + this.anyChanges = false; + if (!this.changedSelectedUser || + (this.changedSelectedUser.orgUserId == undefined && !this.isSystemUserCheck) || + !this.adminApps) { + return; + } + this.showSpinner = true; + this.numberAppsProcessed = 0; + this.numberAppsSucceeded = 0; + this.adminApps.forEach(app => { + if (app.isChanged) { + app.isUpdating = true; + for (var i = 0; i < app.appRoles.length; i++) { + if (app.appRoles[i].roleName.indexOf('*global_') != -1) { + app.appRoles[i].roleName = app.appRoles[i].roleName.replace('*', ''); + } + } + var newUserAppRoles = { + orgUserId: this.orgUserIdValue, + appId: app.id, + appRoles: app.appRoles, + appName: app.name, + isSystemUser: this.isSystemUserCheck + }; + this.usersService.updateUserAppRoles(newUserAppRoles).toPromise() + .then((res: any) => { + if (res.status && res.status.toLowerCase() === 'error') { + const modalErrorRef = this.ngbModal.open(ConfirmationModalComponent); + modalErrorRef.componentInstance.message = "Error: " + res.message; + } + app.isUpdating = false; + app.isDoneUpdating = true; + this.numberAppsSucceeded++; + }).catch(err => { + var errorMessage = 'Failed to update the user application roles: ' + err; + if (err.status == 504) { + this.numberAppsSucceeded++; + errorMessage = 'Request is being processed, please check back later!'; + } else { + app.isErrorUpdating = true; + } + const modalErrorRef = this.ngbModal.open(ConfirmationModalComponent); + modalErrorRef.componentInstance.message = errorMessage; + }).finally(() => { + this.numberAppsProcessed++; + if (this.numberAppsProcessed === this.adminApps.length) { + this.showSpinner = false; // hide the spinner + } + if (this.numberAppsSucceeded === this.adminApps.length) { + this.passBackNewUserPopup.emit(); + this.activeModal.close('Close');//close and resolve dialog promise with true (to update the table) + } + }) + } else { + 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.showSpinner = false; // hide the spinner + } + if (this.numberAppsSucceeded === this.adminApps.length) { + this.activeModal.close('Close'); + } + } + }); + } + + deleteApp(app) { + let appMessage = this.changedSelectedUser.firstName + ' ' + this.changedSelectedUser.lastName; + const ngbModalConfirm = this.ngbModal.open(InformationModalComponent); + ngbModalConfirm.componentInstance.title = 'Confirmation'; + ngbModalConfirm.componentInstance.message = 'Are you sure you want to delete ' + appMessage; + ngbModalConfirm.result.then((_res) => { + if (_res === 'Ok') { + this.anyChanges = true; + app.isChanged = true; + app.isDeleted = true; // use this to hide the app in the display + app.appRoles.forEach((role) => { + role.isApplied = false; + }); + // remove app roles if user app delete option is selected + this.modelSelectedRoles.forEach((_role, index) => { + if (_role.appId === app.id) this.modelSelectedRoles.splice(index, 1); + }) + this.roleSelectChange(app); + } + }).catch(err => { + // $log.error('NewUserModalCtrl::deleteApp error: ',err); + const ngbModalError = this.ngbModal.open(InformationModalComponent); + ngbModalError.componentInstance.title = 'Error'; + ngbModalError.componentInstance.message = 'There was a problem deleting the the applications. ' + + 'Please try again later. Error: ' + err.status; + }); + } + +} diff --git a/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.html b/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.html new file mode 100644 index 00000000..7fc35e44 --- /dev/null +++ b/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.html @@ -0,0 +1,118 @@ +<div class="modal-body"> + <form [formGroup]="addNewUserForm"> + <div class="modal-body"> + <div class="new-users-details-modal"> + <div class="new-users-main"> + + <!-- First Name --> + <div class="new-users-container"> + <div class="form-group"> + <div>*First Name</div> + <input class="form-control" formControlName="firstName" type="text" + [ngClass]="{ 'is-invalid': submitted && formValue.firstName.errors }" /> + <div *ngIf="submitted && formValue.firstName.errors" class="invalid-feedback"> + <div *ngIf="formValue.firstName.errors.required"> + First Name is required + </div> + </div> + </div> + </div> + + <!-- Middle Name --> + <div class="new-users-container"> + <div class="form-group"> + <div>Middle Name</div> + <input class="form-control" formControlName="middleInitial" type="text" /> + </div> + </div> + + <!-- Last Name --> + <div class="new-users-container"> + <div class="form-group"> + <div>*Last Name</div> + <input class="form-control" formControlName="lastName" type="text" + [ngClass]="{ 'is-invalid': submitted && formValue.lastName.errors }" /> + <div *ngIf="submitted && formValue.lastName.errors" class="invalid-feedback"> + <div *ngIf="formValue.lastName.errors.required"> + Last Name is required + </div> + </div> + </div> + </div> + + <!-- Email Address ID --> + <div class="new-users-container"> + <div class="form-group"> + <div>*Email Address ID</div> + <input class="form-control" formControlName="email" type="text" + [ngClass]="{ 'is-invalid': submitted && formValue.email.errors }" /> + <div *ngIf="submitted && formValue.email.errors" class="invalid-feedback"> + <div *ngIf="formValue.email.errors.required"> + Email Address is required + </div> + <div *ngIf="formValue.email.errors.email"> + Email Address is invalid + </div> + </div> + </div> + </div> + + <!-- Login ID --> + <div class="new-users-container"> + <div class="form-group"> + <div>*Login ID</div> + <input class="form-control" formControlName="loginId" type="text" + [ngClass]="{ 'is-invalid': submitted && formValue.loginId.errors }" /> + <div *ngIf="submitted && formValue.loginId.errors" class="invalid-feedback"> + <div *ngIf="formValue.loginId.errors.required"> + Login ID is required + </div> + </div> + </div> + </div> + + <!-- Login Password --> + <div class="new-users-container"> + <div class="form-group"> + <div>*Login Password</div> + <input class="form-control" formControlName="loginPwd" type="password" + [ngClass]="{ 'is-invalid': submitted && formValue.loginPwd.errors }" /> + <div *ngIf="submitted && formValue.loginPwd.errors" class="invalid-feedback"> + <div *ngIf="formValue.loginPwd.errors.required"> + Password is required + </div> + <div *ngIf="formValue.loginPwd.errors.minlength"> + Password must be at least 8 characters + </div> + </div> + </div> + </div> + + <!-- Confirm Login Password --> + <div class="new-users-container"> + <div class="form-group"> + <div>*Confirm Password</div> + <input class="form-control" formControlName="confirmPassword" type="password" + [ngClass]="{ 'is-invalid': submitted && formValue.confirmPassword.errors }" + appEqualValidator="password" /> + <div *ngIf="submitted && formValue.confirmPassword.errors" class="invalid-feedback"> + <div *ngIf="formValue.confirmPassword.errors.required"> + Confirm Login password + </div> + <div *ngIf="formValue.confirmPassword.errors.mustMatch"> + Password Must Match + </div> + </div> + </div> + </div> + + + </div> + </div> + </div> + </form> +</div> +<div class="modal-footer"> + <button type="submit" class="btn btn-primary" (click)="addUser()" [disabled]="addNewUserForm.invalid">Next</button> + <button type="button" class="btn btn-primary" aria-label="Close" (click)="activeModal.dismiss('Cross')">Close</button> +</div> diff --git a/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.scss b/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.scss new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.scss diff --git a/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.spec.ts b/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.spec.ts new file mode 100644 index 00000000..3262174f --- /dev/null +++ b/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UserDetailsFormComponent } from './user-details-form.component'; + +describe('UserDetailsFormComponent', () => { + let component: UserDetailsFormComponent; + let fixture: ComponentFixture<UserDetailsFormComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ UserDetailsFormComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UserDetailsFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.ts b/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.ts new file mode 100644 index 00000000..bea0fcdb --- /dev/null +++ b/portal-FE-os/src/app/pages/users/user-details-form/user-details-form.component.ts @@ -0,0 +1,51 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MustMatch } from 'src/app/shared/helpers/must-match-validator'; +import { UsersService } from 'src/app/shared/services'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'app-user-details-form', + templateUrl: './user-details-form.component.html', + styleUrls: ['./user-details-form.component.scss'] +}) +export class UserDetailsFormComponent implements OnInit { + addNewUserForm: FormGroup; + submitted = false; + + constructor(private formBuilder: FormBuilder, + private usersService: UsersService, + public activeModal: NgbActiveModal) { } + + ngOnInit() { + this.addNewUserForm = this.formBuilder.group({ + firstName: ['', Validators.required], + middleInitial: [''], + lastName: ['', Validators.required], + email: ['', [Validators.required, Validators.email]], + loginId: ['', Validators.required], + loginPwd: ['', [Validators.required, Validators.minLength(6)]], + confirmPassword: ['', Validators.required] + }, { + validator: MustMatch('loginPwd', 'confirmPassword') + }); + } + + get formValue() { + return this.addNewUserForm.controls; + } + + addUser() { + this.submitted = true; + if (this.addNewUserForm.invalid) { + console.log("Invalid form!!"); + return; + } + console.log("New user Json : " + JSON.stringify(this.addNewUserForm.value)); + console.log("Get Raw value : " + this.addNewUserForm.getRawValue()); + let newUserFormData = JSON.stringify(this.addNewUserForm.getRawValue()); + this.usersService.addNewUser(newUserFormData); + this.activeModal.close(); + } + +} |