Adding new components with portal-FE-common 73/100673/10
authorSudarshan Kumar <sudarshan.kumar@att.com>
Thu, 23 Jan 2020 11:36:22 +0000 (17:06 +0530)
committerSudarshan Kumar <sudarshan.kumar@att.com>
Tue, 28 Jan 2020 07:29:04 +0000 (07:29 +0000)
added layout, admins, functional-menu, portal-admins, plugins components along with helpers class, models, pipes and dependent angular services.

Issue-ID: PORTAL-795
Change-Id: I8e9771fcee5d0e58c4a48711c943761f29ae48b8
Signed-off-by: Sudarshan Kumar <sudarshan.kumar@att.com>
88 files changed:
portal-FE-common/src/app/layout/components/footer/footer.component.scss [new file with mode: 0644]
portal-FE-common/src/app/layout/components/footer/footer.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/footer/footer.component.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/header-menu/header-menu.component.html [new file with mode: 0644]
portal-FE-common/src/app/layout/components/header-menu/header-menu.component.scss [new file with mode: 0644]
portal-FE-common/src/app/layout/components/header-menu/header-menu.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/header-menu/header-menu.component.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/header/header.component.html [new file with mode: 0644]
portal-FE-common/src/app/layout/components/header/header.component.scss [new file with mode: 0644]
portal-FE-common/src/app/layout/components/header/header.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/header/header.component.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/sidebar/sidebar.component.html [new file with mode: 0644]
portal-FE-common/src/app/layout/components/sidebar/sidebar.component.scss [new file with mode: 0644]
portal-FE-common/src/app/layout/components/sidebar/sidebar.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/sidebar/sidebar.component.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/tabbar/tab.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/tabbar/tabbar.component.html [new file with mode: 0644]
portal-FE-common/src/app/layout/components/tabbar/tabbar.component.scss [new file with mode: 0644]
portal-FE-common/src/app/layout/components/tabbar/tabbar.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/tabbar/tabbar.component.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/userbar/userbar.component.html [new file with mode: 0644]
portal-FE-common/src/app/layout/components/userbar/userbar.component.scss [new file with mode: 0644]
portal-FE-common/src/app/layout/components/userbar/userbar.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/components/userbar/userbar.component.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/layout-routing.module.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/layout.component.html [new file with mode: 0644]
portal-FE-common/src/app/layout/layout.component.scss [new file with mode: 0644]
portal-FE-common/src/app/layout/layout.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/layout.component.ts [new file with mode: 0644]
portal-FE-common/src/app/layout/layout.module.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/admins/admins.component.html [new file with mode: 0644]
portal-FE-common/src/app/pages/admins/admins.component.scss [new file with mode: 0644]
portal-FE-common/src/app/pages/admins/admins.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/admins/admins.component.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.html [new file with mode: 0644]
portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.scss [new file with mode: 0644]
portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.html [new file with mode: 0644]
portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.scss [new file with mode: 0644]
portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/functional-menu/functional-menu.component.html [new file with mode: 0644]
portal-FE-common/src/app/pages/functional-menu/functional-menu.component.scss [new file with mode: 0644]
portal-FE-common/src/app/pages/functional-menu/functional-menu.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/functional-menu/functional-menu.component.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/functional-menu/jqTreeContextMenu.js [new file with mode: 0644]
portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.html [new file with mode: 0644]
portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.scss [new file with mode: 0644]
portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/portal-admins/portal-admins.component.html [new file with mode: 0644]
portal-FE-common/src/app/pages/portal-admins/portal-admins.component.scss [new file with mode: 0644]
portal-FE-common/src/app/pages/portal-admins/portal-admins.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/pages/portal-admins/portal-admins.component.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/helpers/must-match-validator.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/Admins.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/AllApps.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/PortalAdmin.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/Role.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/RoleFunction.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/UserAccessRoles.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/UserAdminApps.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/application-catalog.model.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/applications-onboarding/applications.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/dynamic-component-manifest/dynamic-component-manifest.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/global-search-item.model.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/index.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/widget-catalog.model.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/model/widget-onboarding/widget.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/pipes/application-pipes.module.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/pipes/elipsis/elipsis.pipe.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/pipes/elipsis/elipsis.pipe.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/dynamic-widget/dynamic-widget.module.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.html [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.scss [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/plugin-loader/client-plugin-loader.service.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/plugin-loader/plugin-externals.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/plugin-loader/plugin-loader.service.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/plugin.component.html [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/plugin.component.scss [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/plugin.component.spec.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/plugin.component.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/plugin.module.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/plugins-config.provider.ts [new file with mode: 0644]
portal-FE-common/src/app/shared/plugin/transfer-state.service.ts [new file with mode: 0644]

diff --git a/portal-FE-common/src/app/layout/components/footer/footer.component.scss b/portal-FE-common/src/app/layout/components/footer/footer.component.scss
new file mode 100644 (file)
index 0000000..82c81d7
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * ============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============================================
+ *
+ * 
+ */
+$topnav-background-color: #222;
+
+.footerText {
+  background-color: $topnav-background-color;
+}
+
+.copyright-text {
+  background-color: $topnav-background-color;
+  color: #fff;
+  font-size: 11px;
+  margin-bottom: 0;
+  line-height: 3rem;
+  margin-top: 20px;
+  text-align: center;
+}
diff --git a/portal-FE-common/src/app/layout/components/footer/footer.component.spec.ts b/portal-FE-common/src/app/layout/components/footer/footer.component.spec.ts
new file mode 100644 (file)
index 0000000..b834022
--- /dev/null
@@ -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 { FooterComponent } from './footer.component';
+
+describe('FooterComponent', () => {
+  let component: FooterComponent;
+  let fixture: ComponentFixture<FooterComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ FooterComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(FooterComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/layout/components/footer/footer.component.ts b/portal-FE-common/src/app/layout/components/footer/footer.component.ts
new file mode 100644 (file)
index 0000000..2561a26
--- /dev/null
@@ -0,0 +1,65 @@
+/*-
+ * ============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';
+import { ManifestService } from 'src/app/shared/services';
+
+
+@Component({
+  selector: 'app-footer',
+  templateUrl: './footer.component.html',
+  styleUrls: ['./footer.component.scss']
+})
+export class FooterComponent implements OnInit {
+
+  buildVersion: string;
+  constructor(private manifest: ManifestService) { }
+
+  ngOnInit() {
+    this.buildVersion =  '';
+    this.manifestDetails();
+  }
+
+  manifestDetails() {
+    this.manifest.getManifest().subscribe((_res: any) => {
+      this.buildVersion = _res.manifest['Build-Number'];
+    }, (_err) => {
+
+    });
+  }
+}
diff --git a/portal-FE-common/src/app/layout/components/header-menu/header-menu.component.html b/portal-FE-common/src/app/layout/components/header-menu/header-menu.component.html
new file mode 100644 (file)
index 0000000..45b4e9f
--- /dev/null
@@ -0,0 +1,130 @@
+<!--
+  ============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 style="bottom: tabBottom; display: flex; height: 100%; overflow: hidden">
+    <nav class="navbar navbar-expand-lg fixed-top">
+        <a class="navbar-brand" href=""> <img src="assets/images/global.logo" style="width:14%"/> &nbsp; ONAP Portal</a>
+        <div class="header-menu-display">
+            <app-header-menu></app-header-menu>
+        </div>
+        <button class="navbar-toggler" type="button" (click)="toggleSidebar()">
+            <i class="icon ion-md-menu" style="color:white;"></i>
+        </button>
+        <div class="collapse navbar-collapse">
+            <ul class="navbar-nav ml-auto">
+                <app-global-search></app-global-search>
+                <li class="nav-item dropdown" ngbDropdown>
+                    <a href="javascript:void(0)" class="nav-link" ngbDropdownToggle>
+                        <i class="icon ion-md-bulb"></i> <b class="caret"></b><span class="sr-only"></span>
+                    </a>
+                    <div class="custom-dropdown-item" ngbDropdownMenu>
+
+                <li class="dropdown-divider"></li>
+        </div>
+        </li>
+        <li class="nav-item dropdown" ngbDropdown>
+            <a href="javascript:void(0)" class="nav-link" ngbDropdownToggle>
+                <i class="icon ion-md-flag"></i> <b class="caret"></b><span class="sr-only"></span>
+            </a>
+            <div class="dropdown-menu-right" ngbDropdownMenu>
+                <a style="margin-left: 8%;" id="application-role" [routerLink]="['/recentNotifications']"
+                    href="javascript:void(0);">
+                    {{ 'View All Recent Notifications' }} </a>
+        <li class="dropdown-divider"></li>
+</div>
+</li>
+<li class="nav-item dropdown" ngbDropdown>
+    <a href="javascript:void(0)" class="nav-link" ngbDropdownToggle>
+        <i class="icon ion-md-person"></i> {{firstName}} <b class="caret"></b>
+    </a>
+    <div class="dropdown-menu-right" ngbDropdownMenu>
+<li class="dropdown-item" style="font-weight: lighter">
+    {{firstName}}, {{lastName}}
+</li>
+<li class="dropdown-item">
+    <span class="dropdown-item-name"> {{ 'Email'}}: </span>
+    <div>
+        <span class="dropdown-item-value">
+            {{loginSnippetEmail}}
+        </span>
+    </div>
+</li>
+<li class="dropdown-item">
+    <span class="dropdown-item-name"> {{ 'User Id' }}: </span>
+    <div>
+        <span class="dropdown-item-value">
+            {{loginSnippetUserid}}
+        </span>
+    </div>
+</li>
+<li class="dropdown-item">
+    <span class="dropdown-item-name"> {{ 'Last login' }}: </span>
+    <div>
+        <span class="dropdown-item-value">
+            {{lastLogin | date:'medium'}}
+        </span>
+    </div>
+</li>
+<li class="custom-display-item">
+    <a (click)="getUserApplicationRoles()" href="javascript:void(0);"><span><i class="icon ion-md-add-circle-outline"
+                [ngClass]="{true: 'icon ion-md-add-circle-outline', false: 'icon ion-md-remove-circle-outline'}[ !displayUserAppRoles]"></i>
+            {{ 'Applications and Roles' }} </span></a>
+    <span class="onap-spinner" *ngIf="isLoading"></span>
+</li> <br>
+<div class="custom-display-item approles" [hidden]="!displayUserAppRoles">
+    <div *ngFor="let ua of userapproles ; index as i">
+        <div class="reg-userApp-value">
+            <span class="dropdown-item-name">{{ua.App}}:</span>
+        </div>
+        <div *ngFor="let role of ua.Roles ; index as i" class="reg-userAppRoles-value">
+            <span *ngIf="role.indexOf('global_')!=-1" id="required" style="color: Red;" visible="false">
+                *</span> <span class="dropdown-item-value">{{role}}</span>
+        </div>
+        <br>
+    </div>
+</div>
+<hr>
+<div id="reg-logout-div" >
+<button type="button" class="btn btn-primary"  (click)="allAppsLogout()">
+    <i class="icon ion-md-log-out"></i> {{ 'Log Out' }} </button></div>
+</div>
+</li>
+</ul>
+</div>
+</nav>
+</div>
\ No newline at end of file
diff --git a/portal-FE-common/src/app/layout/components/header-menu/header-menu.component.scss b/portal-FE-common/src/app/layout/components/header-menu/header-menu.component.scss
new file mode 100644 (file)
index 0000000..d69b858
--- /dev/null
@@ -0,0 +1,150 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
+ .header-menu-item-div
+ {
+     float: left; 
+     margin-right: 5%;
+ }
+ .header-menu-item-li
+ {
+     float: left; 
+     margin-right: 2%;
+     
+ }
+ .header-menu-item-link
+ {
+     font-family:"Open Sans", Arial;
+     font-size:16px;
+     color:#999;
+     text-decoration:none;
+     
+ }
+ #parentmenu-tabs:hover {
+     color: #fff;
+ }
+ .third-level-menu{
+ column-count: 4;
+     line-height: 12px;
+     overflow-x: hidden;
+     overflow-y: hidden;
+     column-gap: 13px;
+     column-rule: 1px outset #d2d2d2;
+     margin-left:20px;
+ }
+ .third-level-menu a{
+  color:black;
+ }
+ .third-level-menu li a {
+     color: #333;
+     display: inline-flex;
+     padding: 7px 15px;
+     font-family:"Omnes-ECOMP-W02", Arial;
+     margin-top:5px;
+   margin-bottom:5px;
+ }
+ .third-level-menu li{
+     width:100%;
+     border-bottom: 1px solid #d2d2d2;
+ }
+ .b2b-header-tabs .header-secondary .header-subitem a.menu__item{
+   font-size:16px;
+ }
+ .third-level-title{
+   font-size:15px;
+   font-weight: 700;
+ }
+ .header-secondlevel-menu 
+   {
+     background-color: #fff;
+     position: fixed;
+     right:1%;
+     width: -webkit-fill-available;
+     
+   }
+   .header-thirdlevel-menu 
+   {
+     background-color: #fff;
+     position: fixed;
+     left:0;
+     height: 70%;    
+     width: 100%;
+   }
+   
+   a.menu__item{
+    font-size:16px;
+    font-family:"Open Sans", Arial;
+    color:#333;
+  }
\ No newline at end of file
diff --git a/portal-FE-common/src/app/layout/components/header-menu/header-menu.component.spec.ts b/portal-FE-common/src/app/layout/components/header-menu/header-menu.component.spec.ts
new file mode 100644 (file)
index 0000000..889f499
--- /dev/null
@@ -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 { HeaderMenuComponent } from './header-menu.component';
+
+describe('HeaderMenuComponent', () => {
+  let component: HeaderMenuComponent;
+  let fixture: ComponentFixture<HeaderMenuComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ HeaderMenuComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(HeaderMenuComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/layout/components/header-menu/header-menu.component.ts b/portal-FE-common/src/app/layout/components/header-menu/header-menu.component.ts
new file mode 100644 (file)
index 0000000..eb8f747
--- /dev/null
@@ -0,0 +1,231 @@
+/*-
+ * ============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 * as _ from 'underscore';
+import { Component, OnInit } from '@angular/core';
+import { MenusService } from 'src/app/shared/services/menus/menus.service';
+import { AddTabFunctionService } from 'src/app/shared/services/tab/add-tab-function.service';
+import { Router } from '@angular/router';
+
+@Component({
+  selector: 'app-header-menu',
+  templateUrl: './header-menu.component.html',
+  styleUrls: ['./header-menu.component.scss']
+})
+export class HeaderMenuComponent implements OnInit {
+  hideMenus: boolean[] = [];
+  hideSecondLevelMenus: boolean[][] = [];
+  hideThirdLevelMenus: boolean[] = [];
+  megaMenuDataObject: any[];
+  favoritesMenuItems: any[];
+  favoritesWindow: boolean;
+  showFavorites: boolean;
+  emptyFavorites: boolean;
+  favoriteItemsCount: number;
+
+  constructor(public router: Router, private menusService: MenusService, private addTabFuntionService: AddTabFunctionService) { }
+
+  ngOnInit() {
+    //this.hideMenus[0] = false;
+    this.getFunctionalMenuForUser();
+
+  }
+
+  unflatten(array: any, parent?: any, tree?: any) {
+    tree = typeof tree !== 'undefined' ? tree : [];
+    parent = typeof parent !== 'undefined' ? parent : { menuId: null };
+    var children = _.filter(array, function (child: any) {
+      return child.parentMenuId == parent.menuId;
+    });
+    if (!_.isEmpty(children)) {
+      if (parent.menuId === null) {
+        tree = children;
+      } else {
+        parent['children'] = children
+      }
+      _.each(children, function (child: any) {
+        this.unflatten(array, child)
+      }, this);
+    }
+
+    return tree;
+  }
+  getFunctionalMenuForUser() {
+    this.menusService.getFunctionalMenuForUser().subscribe((jsonHeaderMenu: any) => {
+      this.megaMenuDataObject = this.unflatten(jsonHeaderMenu);
+      // for (let entry of this.megaMenuDataObject) {
+      //   console.log('First level '+entry.text);
+      //   for (let secondLevel of entry.children)
+      //   {
+      //     if(secondLevel)
+      //   {
+      //     console.log('Second level '+secondLevel.text);
+      //     for (let thirdLevel of secondLevel.children)
+      //   {
+      //     console.log('Third level '+thirdLevel.text);
+      //   }
+      //   }
+
+      //   }
+
+      // }
+
+
+    }, (err) => {
+      console.log('HeaderCtrl::GetFunctionalMenuForUser: HeaderCtrl json returned: ' + err);
+    });
+
+  }
+  getFavoriteItems() {
+    this.menusService.getFavoriteItems().toPromise().then((jsonFavourites: any) => {
+      this.favoritesMenuItems = jsonFavourites;
+      if (this.favoritesMenuItems) {
+        this.favoriteItemsCount = this.favoritesMenuItems.length;
+      }
+    }, (err) => {
+      console.log('HeaderCtrl::getFavoriteItems: HeaderCtrl json returned: ' + err);
+    });
+  }
+  loadFirstLevel(index: any) {
+    this.hideMenus = [];
+    this.hideSecondLevelMenus = [];
+    for (let firstLevelIndex = 0; firstLevelIndex < this.megaMenuDataObject.length; firstLevelIndex++) {
+      this.hideMenus.push(false);
+      this.hideSecondLevelMenus.push(new Array(this.megaMenuDataObject[firstLevelIndex].length).fill(false));
+    }
+    this.hideMenus[index] = true;
+    if (!this.favoritesMenuItems) {
+      this.getFavoriteItems();
+    }
+
+  }
+  isUrlFavorite(menuId: any) {
+    if (this.favoritesMenuItems) {
+      var jsonMenu = JSON.stringify(this.favoritesMenuItems);
+      var isMenuFavorite = jsonMenu.indexOf('menuId\":' + menuId);
+      if (isMenuFavorite == -1) {
+        return false;
+      } else {
+        return true;
+      }
+    }
+    else {
+      return false;
+    }
+  }
+  submenuLevelAction(index: any, column: any) {
+    //console.log('index and column' + index + column);
+    if (index == 'Favorites' && this.favoriteItemsCount != 0) {
+      this.favoritesWindow = true;
+      this.showFavorites = true;
+      this.emptyFavorites = false;
+    }
+    if (index == 'Favorites' && this.favoriteItemsCount == 0) {
+      this.favoritesWindow = true;
+      this.showFavorites = false;
+      this.emptyFavorites = true;
+    }
+    if (index != 'Favorites') {
+      this.favoritesWindow = false;
+      this.showFavorites = false;
+      this.emptyFavorites = false;
+    }
+  }
+  hideFavoritesWindow() {
+    this.showFavorites = false;
+    this.emptyFavorites = false;
+  }
+  removeAsFavoriteItem(event: any, menuId: any) {
+    this.menusService.removeFavoriteItem(menuId).subscribe(() => {
+      //angular.element('#' + event.target.id).css('color', '#666666');
+      this.getFavoriteItems();
+    }, (err) => {
+      console.error('HeaderCtrl::removeAsFavoriteItem: API removeFavoriteItem error: ' + err);
+    });
+  }
+  hideThirdLevelMenu(firstLevelIndex: any, secondLevelIndex: any) {
+    this.hideSecondLevelMenus = [];
+    for (let firstLevelIndex = 0; firstLevelIndex < this.megaMenuDataObject.length; firstLevelIndex++) {
+      this.hideSecondLevelMenus.push(new Array(this.megaMenuDataObject[firstLevelIndex].length).fill(false));
+    }
+    this.hideSecondLevelMenus[firstLevelIndex][secondLevelIndex] = true;
+  }
+
+  clickOutSide(event: any) {
+    this.hideMenus = [];
+    this.hideSecondLevelMenus = [];
+    for (let firstLevelIndex = 0; firstLevelIndex < this.megaMenuDataObject.length; firstLevelIndex++) {
+      this.hideMenus.push(false);
+      this.hideSecondLevelMenus.push(new Array(this.megaMenuDataObject[firstLevelIndex].length).fill(false));
+    }
+
+  }
+  setAsFavoriteItem(event: any, menuId: any) {
+
+  }
+  goToUrl(item: any) {
+    //console.log('Get into URL function' + item.url);
+    let url = item.url;
+    let restrictedApp = item.restrictedApp;
+    if (!url) {
+      console.log('HeaderCtrl::goToUrl: No url found for this application, doing nothing..');
+      return;
+    }
+    if (restrictedApp) {
+      window.open(url, '_blank');
+    } else {
+      if (item.url == "getAccess" || item.url == "contactUs") {
+
+        this.router.navigate(['/' + item.url]);
+
+      } else {
+        var tabContent = {
+          id: new Date(),
+          title: item.text,
+          url: item.url,
+          appId: item.appid
+        };
+        this.addTabFuntionService.filter(tabContent);
+      }
+    }
+
+  }
+  auditLog(link: any, action: any) {
+
+  }
+
+}
diff --git a/portal-FE-common/src/app/layout/components/header/header.component.html b/portal-FE-common/src/app/layout/components/header/header.component.html
new file mode 100644 (file)
index 0000000..45b4e9f
--- /dev/null
@@ -0,0 +1,130 @@
+<!--
+  ============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 style="bottom: tabBottom; display: flex; height: 100%; overflow: hidden">
+    <nav class="navbar navbar-expand-lg fixed-top">
+        <a class="navbar-brand" href=""> <img src="assets/images/global.logo" style="width:14%"/> &nbsp; ONAP Portal</a>
+        <div class="header-menu-display">
+            <app-header-menu></app-header-menu>
+        </div>
+        <button class="navbar-toggler" type="button" (click)="toggleSidebar()">
+            <i class="icon ion-md-menu" style="color:white;"></i>
+        </button>
+        <div class="collapse navbar-collapse">
+            <ul class="navbar-nav ml-auto">
+                <app-global-search></app-global-search>
+                <li class="nav-item dropdown" ngbDropdown>
+                    <a href="javascript:void(0)" class="nav-link" ngbDropdownToggle>
+                        <i class="icon ion-md-bulb"></i> <b class="caret"></b><span class="sr-only"></span>
+                    </a>
+                    <div class="custom-dropdown-item" ngbDropdownMenu>
+
+                <li class="dropdown-divider"></li>
+        </div>
+        </li>
+        <li class="nav-item dropdown" ngbDropdown>
+            <a href="javascript:void(0)" class="nav-link" ngbDropdownToggle>
+                <i class="icon ion-md-flag"></i> <b class="caret"></b><span class="sr-only"></span>
+            </a>
+            <div class="dropdown-menu-right" ngbDropdownMenu>
+                <a style="margin-left: 8%;" id="application-role" [routerLink]="['/recentNotifications']"
+                    href="javascript:void(0);">
+                    {{ 'View All Recent Notifications' }} </a>
+        <li class="dropdown-divider"></li>
+</div>
+</li>
+<li class="nav-item dropdown" ngbDropdown>
+    <a href="javascript:void(0)" class="nav-link" ngbDropdownToggle>
+        <i class="icon ion-md-person"></i> {{firstName}} <b class="caret"></b>
+    </a>
+    <div class="dropdown-menu-right" ngbDropdownMenu>
+<li class="dropdown-item" style="font-weight: lighter">
+    {{firstName}}, {{lastName}}
+</li>
+<li class="dropdown-item">
+    <span class="dropdown-item-name"> {{ 'Email'}}: </span>
+    <div>
+        <span class="dropdown-item-value">
+            {{loginSnippetEmail}}
+        </span>
+    </div>
+</li>
+<li class="dropdown-item">
+    <span class="dropdown-item-name"> {{ 'User Id' }}: </span>
+    <div>
+        <span class="dropdown-item-value">
+            {{loginSnippetUserid}}
+        </span>
+    </div>
+</li>
+<li class="dropdown-item">
+    <span class="dropdown-item-name"> {{ 'Last login' }}: </span>
+    <div>
+        <span class="dropdown-item-value">
+            {{lastLogin | date:'medium'}}
+        </span>
+    </div>
+</li>
+<li class="custom-display-item">
+    <a (click)="getUserApplicationRoles()" href="javascript:void(0);"><span><i class="icon ion-md-add-circle-outline"
+                [ngClass]="{true: 'icon ion-md-add-circle-outline', false: 'icon ion-md-remove-circle-outline'}[ !displayUserAppRoles]"></i>
+            {{ 'Applications and Roles' }} </span></a>
+    <span class="onap-spinner" *ngIf="isLoading"></span>
+</li> <br>
+<div class="custom-display-item approles" [hidden]="!displayUserAppRoles">
+    <div *ngFor="let ua of userapproles ; index as i">
+        <div class="reg-userApp-value">
+            <span class="dropdown-item-name">{{ua.App}}:</span>
+        </div>
+        <div *ngFor="let role of ua.Roles ; index as i" class="reg-userAppRoles-value">
+            <span *ngIf="role.indexOf('global_')!=-1" id="required" style="color: Red;" visible="false">
+                *</span> <span class="dropdown-item-value">{{role}}</span>
+        </div>
+        <br>
+    </div>
+</div>
+<hr>
+<div id="reg-logout-div" >
+<button type="button" class="btn btn-primary"  (click)="allAppsLogout()">
+    <i class="icon ion-md-log-out"></i> {{ 'Log Out' }} </button></div>
+</div>
+</li>
+</ul>
+</div>
+</nav>
+</div>
\ No newline at end of file
diff --git a/portal-FE-common/src/app/layout/components/header/header.component.scss b/portal-FE-common/src/app/layout/components/header/header.component.scss
new file mode 100644 (file)
index 0000000..65a4be0
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * ============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============================================
+ *
+ * 
+ */
+$topnav-background-color: #222;
+:host {
+  .navbar {
+    background-color: $topnav-background-color;
+    .navbar-brand {
+      color: #fff;
+    }
+    .nav-item > a {
+      color: #999;
+      &:hover {
+        color: #fff;
+      }
+    }
+  }
+
+  .dropdown-menu-right.dropdown-menu.show{
+    width: 250px;
+  }
+  .dropdown-item-name {
+    font-weight: bold;
+  }
+
+  .dropdown-item-value {
+    font-weight: lighter;
+  }
+
+  .custom-display-item {
+    display: block;
+    width: 100%;
+    padding: 0.25rem 1.5rem;
+    clear: both;
+    font-weight: 400;
+    color: #212529;
+    text-align: inherit;
+    white-space: nowrap;
+    background-color: transparent;
+    border: 0;
+  }
+
+  .custom-display-item.approles {
+    overflow-y: scroll;
+    height: 250px;
+  }
+  .header-menu-display {
+    width: 250px;
+  }
+}
diff --git a/portal-FE-common/src/app/layout/components/header/header.component.spec.ts b/portal-FE-common/src/app/layout/components/header/header.component.spec.ts
new file mode 100644 (file)
index 0000000..1bcd2ff
--- /dev/null
@@ -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 { HeaderComponent } from './header.component';
+
+describe('HeaderComponent', () => {
+  let component: HeaderComponent;
+  let fixture: ComponentFixture<HeaderComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ HeaderComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(HeaderComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/layout/components/header/header.component.ts b/portal-FE-common/src/app/layout/components/header/header.component.ts
new file mode 100644 (file)
index 0000000..cb6bdd3
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * ============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 { Component, OnInit } from '@angular/core';
+import { Router, NavigationEnd } from '@angular/router';
+import { UserProfileService, MenusService } from 'src/app/shared/services';
+import { CookieService } from 'ngx-cookie-service';
+
+@Component({
+    selector: 'app-header',
+    templateUrl: './header.component.html',
+    styleUrls: ['./header.component.scss']
+})
+export class HeaderComponent implements OnInit {
+    public pushRightClass: string;
+    firstName: string;
+    lastName: string;
+    loginSnippetUserid: any;
+    lastLogin: number;
+    loginSnippetEmail: any;
+    userapproles: any[];
+    displayUserAppRoles: any;
+    isLoading: boolean;
+
+    constructor(public router: Router, private userProfileService: UserProfileService, private menusService: MenusService, private cookieService: CookieService) {
+
+        this.router.events.subscribe(val => {
+            if (
+                val instanceof NavigationEnd &&
+                window.innerWidth <= 992 &&
+                this.isToggled()
+            ) {
+                this.toggleSidebar();
+            }
+        });
+    }
+
+    ngOnInit() {
+        this.pushRightClass = 'push-right';
+        this.getUserInformation();
+    }
+
+    getUserInformation() {
+        this.userProfileService.getFunctionalMenuStaticInfo().toPromise().then((res: any) => {
+            if (res == null || res.firstName == null || res.firstName == '' || res.lastName == null || res.lastName == '') {
+                // $log.info('HeaderCtrl: failed to get all required data, trying user profile');
+                this.userProfileService.getUserProfile().toPromise().then((profile: any) => {
+                    this.firstName = profile.firstName;
+                    this.lastName = profile.lastName;
+                }, (err) => {
+                    // $log.error('Header Controller:: getUserProfile() failed: ' + err);
+                });
+            } else {
+                this.firstName = res.firstName;
+                this.lastName = res.lastName;
+                this.loginSnippetEmail = res.email;
+                this.loginSnippetUserid = res.userId;
+                this.lastLogin = Date.parse(res.last_login);
+            }
+            sessionStorage.userId = res.userId;
+            this.menusService.getFunctionalMenuForUser().toPromise().then((jsonHeaderMenu: any) => {
+                // $scope.menuItems = unflatten(jsonHeaderMenu);
+                // $scope.megaMenuDataObject = $scope.menuItems;
+            }, (err) => {
+                // $log.error('HeaderCtrl::GetFunctionalMenuForUser: HeaderCtrl json returned: ' + err);
+            });
+
+        }, (err) => {
+            // $log.error('HeaderCtrl::getFunctionalMenuStaticInfo failed: ' + err);
+        })
+    }
+
+    //     unflatten = function( array, parent, tree ){
+
+    //     tree = typeof tree !== 'undefined' ? tree : [];
+    //     parent = typeof parent !== 'undefined' ? parent : { menuId: null };
+    //     var children = _.filter( array, function(child){ return child.parentMenuId == parent.menuId; });
+
+    //     if( !_.isEmpty( children )  ){
+    //       if( parent.menuId === null ){
+    //         tree = children;
+    //       }else{
+    //         parent['children'] = children
+    //       }
+    //       _.each( children, function( child ){ unflatten( array, child ) } );
+    //     }
+
+    //     return tree;
+    // }
+
+    getUserApplicationRoles() {
+        this.userapproles = [];
+        if (this.displayUserAppRoles) {
+            this.displayUserAppRoles = false;
+        } else {
+            this.displayUserAppRoles = true;
+            this.isLoading = true;
+            this.userProfileService.getUserAppRoles(this.loginSnippetUserid)
+            .subscribe((res: any) => {
+                this.isLoading = false;
+                for (var i = 0; i < res.length; i++) {
+                    var userapprole = {
+                        App: res[i].appName,
+                        Roles: res[i].roleNames,
+                    };
+                    this.userapproles.push(userapprole);
+                }
+            }, (err) => {
+                this.isLoading = false;
+            });
+        }
+    }
+
+    allAppsLogout() {
+        this.firstName="";
+        this.lastName="";
+        this.displayUserAppRoles=false;        
+        var cookieTabs = this.cookieService.get("visInVisCookieTabs").toString;
+         if(cookieTabs!=null){
+             for(var t in cookieTabs){
+             
+                 var url = cookieTabs[t].content;
+                 if(url != "") {
+                     this.menusService.logout(url);
+                   }
+             }
+         }
+         // wait for individual applications to log out before the portal logout
+         setTimeout(function() {
+             window.location.href = "logout.htm";
+         }, 2000);
+    }
+
+    isToggled(): boolean {
+        const dom: Element = document.querySelector('body');
+        return dom.classList.contains(this.pushRightClass);
+    }
+
+    toggleSidebar() {
+        const dom: any = document.querySelector('body');
+        dom.classList.toggle(this.pushRightClass);
+    }
+
+    onLoggedout() {
+        localStorage.removeItem('isLoggedin');
+    }
+}
diff --git a/portal-FE-common/src/app/layout/components/sidebar/sidebar.component.html b/portal-FE-common/src/app/layout/components/sidebar/sidebar.component.html
new file mode 100644 (file)
index 0000000..e2f4f3a
--- /dev/null
@@ -0,0 +1,72 @@
+<!--
+  ============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============================================
+  
+  -->
+  
+<nav class="sidebar" [ngClass]="{sidebarPushRight: isActive, collapsed: collapsed}">
+    <div class="toggle-button" [ngClass]="{collapsed: collapsed}" (click)="toggleCollapsed()">
+        <i class="icon ion-md-arrow-{{collapsed?'dropright':'dropleft'}}" style="float:right"></i> <i
+            class="icon ion-md-arrow-{{collapsed?'dropright':'dropleft'}}" style="float:right"></i>&nbsp;
+    </div>
+    <div class="list-group" *ngFor="let menu of menuData ; index as item">
+        <a href="{{menu.href}}" *ngIf="!showOnlyParentMenu" [routerLinkActive]="['router-link-active']"
+            class="list-group-item">
+            <i class="icon ion-md-{{menu.imageSrc}}"></i>&nbsp;
+            <span>{{menu.name}}</span>
+        </a>
+        <a [routerLink]="menu.state" *ngIf="showOnlyParentMenu" [routerLinkActive]="['router-link-active']"
+            class="list-group-item">
+            <i class="icon ion-md-{{menu.imageSrc}}"></i>&nbsp;
+            <span>{{menu.name}}</span>
+        </a>
+        <div class="nested-menu" *ngIf="menu.menuItems.length > 0">
+            <a href="javascript:void(0)" class="list-group-item" (click)="addExpandClass(menu.name)">
+                <i class="fa fa-plus"></i>&nbsp;
+                <span>{{menu.name}}</span>
+            </a>
+            <li class="nested" [class.expand]="showMenu === menu.name">
+                <ul class="submenu">
+                    <li *ngFor="let menuItems of menu.menuItems">
+                        <a href="{{menuItems.href}}">
+                            <i class="fa fa-monument"></i>&nbsp;
+                            <span>{{ menuItems.name }}</span>
+                        </a>
+                    </li>
+                </ul>
+            </li>
+        </div>
+    </div>
+</nav>
\ No newline at end of file
diff --git a/portal-FE-common/src/app/layout/components/sidebar/sidebar.component.scss b/portal-FE-common/src/app/layout/components/sidebar/sidebar.component.scss
new file mode 100644 (file)
index 0000000..8d3c51f
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * ============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============================================
+ *
+ *
+ */
+$topnav-background-color: #fff;
+.sidebar {
+    border-radius: 0;
+    position: relative;
+    z-index: 1000;
+    //top: 56px;
+    left: 270px;
+    width: 270px;
+    margin-left: -270px;
+    margin-bottom: 48px;
+    border: none;
+    border-radius: 0;
+    overflow-y: auto;
+    background-color: $topnav-background-color;
+    bottom: 0;
+    overflow-x: hidden;
+    padding-bottom: 40px;
+    white-space: nowrap;
+    -webkit-transition: all 0.2s ease-in-out;
+    -moz-transition: all 0.2s ease-in-out;
+    -ms-transition: all 0.2s ease-in-out;
+    -o-transition: all 0.2s ease-in-out;
+    transition: all 0.2s ease-in-out;
+    .list-group {
+        a.list-group-item {
+            background: $topnav-background-color;
+            border: 0;
+            border-top: 1px solid #999;
+            border-radius: 0;
+            color: #0568ae;
+            text-decoration: none;
+            .icon {
+                margin-right: 10px;
+                color: #000;
+            }
+        }
+        a:hover {
+            background: darken($topnav-background-color, 2%);
+            color: #000;
+        }
+        a.router-link-active {
+            background: darken($topnav-background-color, 5%);
+            color: #000;
+        }
+        .header-fields {
+            padding-top: 10px;
+
+            > .list-group-item:first-child {
+                border-top: 1px solid rgba(255, 255, 255, 0.2);
+            }
+        }
+    }
+    .sidebar-dropdown {
+        *:focus {
+            border-radius: none;
+            border: none;
+        }
+        .panel-title {
+            font-size: 1rem;
+            height: 50px;
+            margin-bottom: 0;
+            a {
+                color: #999;
+                text-decoration: none;
+                font-weight: 400;
+                background: $topnav-background-color;
+                span {
+                    position: relative;
+                    display: block;
+                    padding: 0.75rem 1.5rem;
+                    padding-top: 1rem;
+                }
+            }
+            a:hover,
+            a:focus {
+                color: #fff;
+                outline: none;
+                outline-offset: -2px;
+            }
+        }
+        .panel-title:hover {
+            background: darken($topnav-background-color, 5%);
+        }
+        .panel-collapse {
+            border-radious: 0;
+            border: none;
+            .panel-body {
+                .list-group-item {
+                    border-radius: 0;
+                    background-color: $topnav-background-color;
+                    border: 0 solid transparent;
+                    a {
+                        color: #999;
+                    }
+                    a:hover {
+                        color: #fff;
+                    }
+                }
+                .list-group-item:hover {
+                    background: darken($topnav-background-color, 5%);
+                }
+            }
+        }
+    }
+}
+
+.nested-menu {
+    .list-group-item {
+        cursor: pointer;
+    }
+    .nested {
+        list-style-type: none;
+    }
+    ul.submenu {
+        display: none;
+        height: 0;
+    }
+    & .expand {
+        ul.submenu {
+            display: block;
+            list-style-type: none;
+            height: auto;
+            li {
+                a {
+                    color: #0568ae;
+                    padding: 10px;
+                    display: block;
+                }
+            }
+        }
+    }
+}
+@media screen and (max-width: 992px) {
+    .sidebar {
+        top: 54px;
+        left: 0px;
+    }
+}
+@media print {
+    .sidebar {
+        display: none !important;
+    }
+}
+@media (min-width: 992px) {
+    .header-fields {
+        display: none;
+    }
+}
+
+::-webkit-scrollbar {
+    width: 8px;
+}
+
+::-webkit-scrollbar-track {
+    -webkit-box-shadow: inset 0 0 0px rgba(255, 255, 255, 1);
+    border-radius: 3px;
+}
+
+::-webkit-scrollbar-thumb {
+    border-radius: 3px;
+    -webkit-box-shadow: inset 0 0 3px rgba(255, 255, 255, 1);
+}
+
+.toggle-button {
+    width: 270px;
+    cursor: pointer;
+    padding: 12px;
+    bottom: 0;
+    color: #0568ae;
+    background: #fff;
+    i {
+        font-size: 23px;
+    }
+    &:hover {
+        background: darken($topnav-background-color, 2%);
+        color: #000;
+    }
+    border-top: 1px solid #999;
+    -webkit-transition: all 0.2s ease-in-out;
+    -moz-transition: all 0.2s ease-in-out;
+    -ms-transition: all 0.2s ease-in-out;
+    -o-transition: all 0.2s ease-in-out;
+    transition: all 0.2s ease-in-out;
+}
+
+.collapsed {
+    width: 60px;
+    span {
+        display: none;
+    }
+}
diff --git a/portal-FE-common/src/app/layout/components/sidebar/sidebar.component.spec.ts b/portal-FE-common/src/app/layout/components/sidebar/sidebar.component.spec.ts
new file mode 100644 (file)
index 0000000..92caeb4
--- /dev/null
@@ -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 { SidebarComponent } from './sidebar.component';
+
+describe('SidebarComponent', () => {
+  let component: SidebarComponent;
+  let fixture: ComponentFixture<SidebarComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ SidebarComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(SidebarComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/layout/components/sidebar/sidebar.component.ts b/portal-FE-common/src/app/layout/components/sidebar/sidebar.component.ts
new file mode 100644 (file)
index 0000000..1c689e1
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * ============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 { Component, Output, EventEmitter, OnInit, Input } from '@angular/core';
+import { Router, NavigationEnd } from '@angular/router';
+import { SidebarService } from '../../../shared/services/index'
+
+@Component({
+    selector: 'app-sidebar',
+    templateUrl: './sidebar.component.html',
+    styleUrls: ['./sidebar.component.scss']
+})
+export class SidebarComponent implements OnInit {
+    @Input() labelName: string;
+    isActive: boolean;
+    collapsed: boolean;
+    showMenu: string;
+    pushRightClass: string;
+    result: any;
+    showOnlyParentMenu: boolean;
+    leftParentData: any;
+    leftChildData: any;
+    menuData: Array<object> = [];
+    page: any;
+
+    @Output() collapsedEvent = new EventEmitter<boolean>();
+
+    constructor(public router: Router, public sidebarService: SidebarService) {
+        this.router.events.subscribe(val => {
+            if (
+                val instanceof NavigationEnd &&
+                window.innerWidth <= 992 &&
+                this.isToggled()
+            ) {
+                this.toggleSidebar();
+            }
+        });
+    }
+
+    ngOnInit() {
+        this.isActive = false;
+        this.collapsed = false;
+        this.showMenu = '';
+        this.pushRightClass = 'push-right';
+        this.sidebarService.getLeftMenu()
+            .subscribe(data => {
+                this.result = data;
+                if (this.result.data && this.result.data2) {
+                    this.leftParentData = JSON.parse(this.result.data);
+                    this.leftChildData = JSON.parse(this.result.data2);
+                } else {
+                    this.labelName = this.result.label;
+                    this.leftParentData = this.result.navItems;
+                    this.showOnlyParentMenu = true;
+                }
+
+                for (var i = 0; i < this.leftParentData.length; i++) {
+                    var parentItem = {
+                        name: null,
+                        imageSrc: null,
+                        href: null,
+                        menuItems: [],
+                        state: null
+                    }
+                    if (this.showOnlyParentMenu) {
+                        parentItem.name = this.leftParentData[i].name;
+                        parentItem.imageSrc = this.leftParentData[i].imageSrc;
+                        parentItem.state = '/'+this.leftParentData[i].state;
+                    } else {
+                        parentItem.name = this.leftParentData[i].label;
+                        parentItem.imageSrc = this.leftParentData[i].imageSrc;
+                    }
+                    // Add link to items with no subitems
+                    if (!this.showOnlyParentMenu) {
+                        if (this.leftChildData[i].length == 0)
+                            parentItem.href = this.leftParentData[i].action;
+
+                        for (var j = 0; j < this.leftChildData[i].length; j++) {
+
+                            var childItem = {
+                                name: null,
+                                href: null
+                            };
+                            if (this.leftChildData[i][j].label != null && this.leftChildData[i][j].label.length > 0) {
+
+                                childItem.name = this.leftChildData[i][j].label;
+                                childItem.href = this.leftChildData[i][j].action;
+                                parentItem.menuItems.push(childItem);
+                            }
+                        }
+                    }
+                    this.menuData.push(parentItem);
+                }
+
+            });
+
+    }
+    eventCalled() {
+        this.isActive = !this.isActive;
+    }
+
+    addExpandClass(element: any) {
+        if (element === this.showMenu) {
+            this.showMenu = '0';
+        } else {
+            this.showMenu = element;
+        }
+    }
+
+    toggleCollapsed() {
+        this.collapsed = !this.collapsed;
+        this.collapsedEvent.emit(this.collapsed);
+    }
+
+    isToggled(): boolean {
+        const dom: Element = document.querySelector('body');
+        return dom.classList.contains(this.pushRightClass);
+    }
+
+    toggleSidebar() {
+        const dom: any = document.querySelector('body');
+        dom.classList.toggle(this.pushRightClass);
+    }
+}
diff --git a/portal-FE-common/src/app/layout/components/tabbar/tab.ts b/portal-FE-common/src/app/layout/components/tabbar/tab.ts
new file mode 100644 (file)
index 0000000..bdccdc1
--- /dev/null
@@ -0,0 +1,50 @@
+/*-
+ * ============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 { SafeUrl } from '@angular/platform-browser';
+
+export class Tab {
+  label: string;
+  url: SafeUrl;
+  active: boolean;
+
+  constructor(label: string) {
+      this.label = label;
+  }
+
+}
diff --git a/portal-FE-common/src/app/layout/components/tabbar/tabbar.component.html b/portal-FE-common/src/app/layout/components/tabbar/tabbar.component.html
new file mode 100644 (file)
index 0000000..31bb197
--- /dev/null
@@ -0,0 +1,97 @@
+<!--
+  ============LICENSE_START==========================================
+  ONAP Portal
+  ===================================================================
+  Copyright (C) 2017-2018 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 style="display: flex; flex-direction:column">
+
+      <mat-tab-group [selectedIndex]="selected.value" (selectedIndexChange)="selected.setValue($event)"
+        (selectedTabChange)="tabChanged($event)">
+        <mat-tab [label]="mainTab">
+          <!--
+            <mat-grid-list cols="5">
+              <mat-grid-tile [colspan]="1" [rowspan]="3">
+                <app-sidebar (collapsedEvent)="receiveCollapsed($event)"></app-sidebar>
+                <app-userbar></app-userbar>
+              </mat-grid-tile>
+              <mat-grid-tile [colspan]="4"  style="height: calc(100vh - 24px);overflow-y: hidden">
+                <div class="container">
+                  <router-outlet></router-outlet>
+                </div>
+              </mat-grid-tile>
+            </mat-grid-list>
+          -->
+
+
+          <div style="display: flex; flex-direction:column; overflow-y: scroll; height: calc(100vh - 100px)">
+            <div style="display: flex; flex-direction:row;">
+              <app-sidebar (collapsedEvent)="receiveCollapsed($event)"></app-sidebar>
+              <app-userbar></app-userbar>
+              <div class="container" style="margin-left: 370px;">
+                <router-outlet></router-outlet>
+              </div>
+            </div>
+            <app-footer></app-footer>
+          </div>
+
+        </mat-tab>
+
+        <mat-tab *ngFor="let tab of tabs; let index = index">
+          <ng-template mat-tab-label>
+            {{tab.label | elipsis: 13}} &nbsp;
+            <i class="icon ion-md-close-circle" (click)="removeTab(index)"></i>
+          </ng-template>
+
+
+        </mat-tab>
+
+      </mat-tab-group>
+
+
+
+
+
+        <div *ngFor="let tab of tabs; let index = index"
+          [style.display]='tab.active? "inline" : "none"'
+          [style.position]='tab.active? "static" : "absolute"'
+          [style.height]='tab.active? "calc(100vh)" : "calc(0vh)"'>
+
+          <iframe id="tabframe-{{tab.label.split(' ').join('-')}}" scrolling='yes' frameBorder='0' width='100%'
+            scrolling='yes' frameBorder='0' width='100%' height='90%' [src]='tab.url'></iframe>
+
+        </div>
+
+
+    </div>
diff --git a/portal-FE-common/src/app/layout/components/tabbar/tabbar.component.scss b/portal-FE-common/src/app/layout/components/tabbar/tabbar.component.scss
new file mode 100644 (file)
index 0000000..807e2d5
--- /dev/null
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2017 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============================================
+ *
+ *
+ */
+
+ .input-label,
+ .add-tab-button,
+ .delete-tab-button {
+   margin: 8px;
+ }
+ .search-bar {
+   position: absolute;
+   right: 10%;
+ }
+ #mat-tab-label-0-1 {
+   position: fixed;
+   right: 1em;
+ }
+ .mat-tab-group{
+   margin-top: 55px;
+ }
+
+ ::ng-deep .mat-tab-label {
+   font-size: 13px !important;
+   line-height: 30px !important;
+   margin: 5px 0px 0 !important;
+   border-top-left-radius: 88px 205px !important;
+   border-top-right-radius: 88px 205px !important;
+   padding: 0 30px 0 25px !important;
+   height: 35px !important;
+   background: #d2d2d2 !important;
+   position: relative !important;
+   box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5) !important;
+   width: 180px !important;
+   max-width: 200px !important;
+   min-width: 20px !important;
+   border: 1px solid #aaa !important;
+   text-transform: capitalize !important;
+   text-align: left !important;
+ }
+ ::ng-deep .mat-tab-label.mat-ripple.ng-star-inserted.mat-tab-label-active {
+   opacity: 1;
+ }
\ No newline at end of file
diff --git a/portal-FE-common/src/app/layout/components/tabbar/tabbar.component.spec.ts b/portal-FE-common/src/app/layout/components/tabbar/tabbar.component.spec.ts
new file mode 100644 (file)
index 0000000..94866e4
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2017-2018 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 { TabbarComponent } from './tabbar.component';
+
+describe('TabbarComponent', () => {
+  let component: TabbarComponent;
+  let fixture: ComponentFixture<TabbarComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ TabbarComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TabbarComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/layout/components/tabbar/tabbar.component.ts b/portal-FE-common/src/app/layout/components/tabbar/tabbar.component.ts
new file mode 100644 (file)
index 0000000..7a10e39
--- /dev/null
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2017-2018 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';
+import { FormControl } from '@angular/forms';
+import { DomSanitizer } from '@angular/platform-browser';
+import { Tab } from './tab';
+import { AddTabFunctionService } from 'src/app/shared/services/tab/add-tab-function.service';
+
+@Component({
+  selector: 'app-tabbar',
+  templateUrl: './tabbar.component.html',
+  styleUrls: ['./tabbar.component.scss']
+})
+export class TabbarComponent implements OnInit {
+
+  tabs = [];
+  mainTab = 'Home';
+  selected = new FormControl(0);
+  collapedSideBar: boolean;
+
+  constructor(private sanitizer: DomSanitizer, private addTabFuntionService: AddTabFunctionService) {
+
+  }
+
+  ngOnInit(): void {
+
+    this.addTabFuntionService.listen().subscribe((m: any) => {
+      console.log(m);
+      this.addTab(true, m.title, m.url);
+    })
+  }
+
+  addTab(selectAfterAdding: boolean, label: string, url: string) {
+    const tab = new Tab(label);
+    tab.url = this.sanitizer.bypassSecurityTrustResourceUrl(url);
+    tab.active = true;
+    this.tabs.push(tab);
+
+    if (selectAfterAdding) {
+      this.selected.setValue(this.tabs.length);
+    }
+  }
+
+  removeTab(index: number) {
+    this.tabs.splice(index, 1);
+  }
+
+  receiveCollapsed($event) {
+    this.collapedSideBar = $event;
+  }
+
+  tabChanged($event) {
+
+    for (const ttab of this.tabs) {
+      ttab.active = false;
+    }
+    if(this.tabs.length != 0 && $event.index != 0)
+      this.tabs[$event.index - 1].active = true;
+  }
+}
diff --git a/portal-FE-common/src/app/layout/components/userbar/userbar.component.html b/portal-FE-common/src/app/layout/components/userbar/userbar.component.html
new file mode 100644 (file)
index 0000000..0ff5a55
--- /dev/null
@@ -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============================================
+  
+  -->
+  
+<button type="button" class="btn btn-primary" href="javascript:void(0)"
+[ngStyle]="{'right': isOpen ? '65px' : '-18px' }" (click)="toggleSidebar()">
+  <span id="user-chevron-down" class="icon-controls-down" [hidden]="!isOpen">Close</span>
+  <span id="user-chevron-up" class="icon-controls-upPRIMARY" [hidden]="isOpen"><span class="right-menu-button"><i
+        class="icon ion-md-arrow-dropup"></i> Users</span></span>
+</button>
+<nav [ngStyle]="{'right': isOpen ? '18px' : '-75px' }" class="usb-item usb-item-vertical usb-item-right" id="usb-item-s2">
+  <h3>Online Users</h3>
+  <div *ngFor="let user of userList" style="font-size: 10px;">
+    <a [href]="user.linkQ"><img class="activeUserIcon" [src]="user.linkPic" alt="User Link"></a>
+    <div class="userId-txt">{{user.userId}}</div>
+  </div>
+</nav>
\ No newline at end of file
diff --git a/portal-FE-common/src/app/layout/components/userbar/userbar.component.scss b/portal-FE-common/src/app/layout/components/userbar/userbar.component.scss
new file mode 100644 (file)
index 0000000..8180110
--- /dev/null
@@ -0,0 +1,114 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
+.usb-item {
+  background: #fff;
+  position: fixed;
+}
+
+.usb-item h3 {
+  color: #ef6f00;
+  font-size: 14px;
+  padding: 20px;
+  margin: 0;
+  text-align: center;
+  font-weight: 300;
+  background: #f8f9fa;
+}
+
+.usb-item a {
+  display: block;
+  color: #fff;
+  font-size: 1.1em;
+  font-weight: 300;
+  transition: all 0.2s ease-in-out;
+  -ms-transition: all 0.2s ease-in-out; /* IE 9 */
+  -webkit-transition: all 0.2s ease-in-out; /* Safari 3-8 */
+}
+
+.usb-item a:active {
+  background: #afdefa;
+  color: #47a3da;
+}
+
+.usb-item-right {
+  transition: all 0.5s ease-in-out;
+  -ms-transition: all 0.5s ease-in-out; /* IE 9 */
+  -webkit-transition: all 0.5s ease-in-out; /* Safari 3-8 */
+}
+
+.usb-item a:hover {
+  -ms-transform: scale(1.5); /* IE 9 */
+  -webkit-transform: scale(1.5); /* Safari 3-8 */
+  transform: scale(1.5);
+}
+
+.usb-item-vertical {
+  margin-top: 105px;
+  text-align: center;
+  width: 75px;
+  height: 79%;
+  top: 0;
+  z-index: 1000;
+  box-shadow: 0 4px 5px rgba(0, 0, 0, 0.2);
+}
+
+.usb-item-vertical a {
+  padding: 0.5em;
+}
+
+button {
+  transition: all 0.5s ease-in-out;
+  -ms-transition: all 0.5s ease-in-out; /* IE 9 */
+  -webkit-transition: all 0.5s ease-in-out; /* Safari 3-8 */
+  z-index: 9999;
+  top: 450px;
+  -ms-transform: rotate(-90deg); /* IE 9 */
+  -webkit-transform: rotate(-90deg); /* Safari 3-8 */
+  transform: rotate(-90deg);
+  position: fixed;
+}
+
+.activeUserIcon {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+  height: 55px;
+  width: 55px;
+  border-radius: 50%;
+}
diff --git a/portal-FE-common/src/app/layout/components/userbar/userbar.component.spec.ts b/portal-FE-common/src/app/layout/components/userbar/userbar.component.spec.ts
new file mode 100644 (file)
index 0000000..62b9c54
--- /dev/null
@@ -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 { UserbarComponent } from './userbar.component';
+
+describe('UserbarComponent', () => {
+  let component: UserbarComponent;
+  let fixture: ComponentFixture<UserbarComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ UserbarComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(UserbarComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/layout/components/userbar/userbar.component.ts b/portal-FE-common/src/app/layout/components/userbar/userbar.component.ts
new file mode 100644 (file)
index 0000000..661317b
--- /dev/null
@@ -0,0 +1,133 @@
+/*-
+ * ============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';
+import { UserbarService, UserProfileService } from 'src/app/shared/services';
+import { DomSanitizer } from '@angular/platform-browser';
+import { environment } from 'src/environments/environment';
+
+@Component({
+  selector: 'app-userbar',
+  templateUrl: './userbar.component.html',
+  styleUrls: ['./userbar.component.scss']
+})
+export class UserbarComponent implements OnInit {
+
+  userList;
+  isOpen: boolean;
+  intervalPromise = null;
+  updateRate: number;
+  myservice: UserbarService;
+  api = environment.api;
+  constructor(private sanitizer: DomSanitizer, private userbarService: UserbarService, private userProfileService: UserProfileService) { }
+
+  ngOnInit() {
+    this.userList = [];
+    this.myservice = this.userbarService;
+    this.isOpen = true;
+    // this.userbarService.getOnlineUserUpdateRate().subscribe((_res: any) => {
+    //   if (_res != null) {
+    //     var rate = parseInt(_res.onlineUserUpdateRate);
+    //     var duration = parseInt(_res.onlineUserUpdateDuration);
+    //     this.userbarService.setMaxRefreshCount((duration / rate) + 1);
+    //     this.userbarService.setRefreshCount(this.userbarService.maxCount);
+    //     if (rate != NaN && duration != NaN) {
+    //       // $log.debug('UserbarCtlr: scheduling function at interval ' + millis);
+    //       this.updateRate = rate;
+    //       this.start(this.updateRate);
+    //     }
+    //   }
+    // })
+    this.updateActiveUsers();
+  }
+
+  updateActiveUsers() {
+    // this.userbarService.decrementRefreshCount();
+    this.userProfileService.getActiveUser().subscribe((_res: any) => {
+      if (_res == null) {
+        // $log.error('UserbarCtrl::updateActiveUsers: failed to get active user');
+        this.stop();
+      } else {
+        var maxItems = 25;
+        if (_res.length < maxItems)
+          maxItems = _res.length;
+        for (var i = 0; i < maxItems; i++) {
+          var data = {
+            userId: _res[i],
+            linkQ: this.api.linkQ,
+            linkPic: this.api.linkPic
+          }
+          this.userList.push(data);
+        }
+      }
+
+    }, (err) => {
+      this.userList = [];
+      this.stop();
+    })
+
+    // .add(() => {
+    //   var footerOff = $('#online-userbar').offset().top;
+    //   var headOff = $('#footer').offset().top;
+    //   var defaultOffSet = 45;
+    //   $(".online-user-container").css({
+    //     "height": headOff - footerOff - defaultOffSet
+    //   });
+    // })
+
+  }
+
+  toggleSidebar() {
+    this.isOpen = !this.isOpen;
+  }
+
+  start(rate) {
+    // stops any running interval to avoid two intervals running at the same time
+    this.stop();
+    // store the interval promise
+    this.intervalPromise = setInterval(this.updateActiveUsers, rate);
+  }
+
+
+  stop() {
+    if (this.intervalPromise != null) {
+      clearInterval(this.intervalPromise);
+      this.intervalPromise = null;
+    }
+  }
+
+}
diff --git a/portal-FE-common/src/app/layout/layout-routing.module.ts b/portal-FE-common/src/app/layout/layout-routing.module.ts
new file mode 100644 (file)
index 0000000..5fa77e3
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * ============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 { LayoutComponent } from './layout.component';
+
+const routes: Routes = [
+    {
+        path: '',
+        component: LayoutComponent,
+        children: [
+            //redirecting to pages module
+            { path: '', redirectTo: 'app', },
+            { path: 'app', loadChildren: () => import('../pages/pages.module').then(m => m.PagesModule) },        ]
+    }
+];
+
+@NgModule({
+    imports: [RouterModule.forChild(routes)],
+    exports: [RouterModule]
+})
+export class LayoutRoutingModule {}
diff --git a/portal-FE-common/src/app/layout/layout.component.html b/portal-FE-common/src/app/layout/layout.component.html
new file mode 100644 (file)
index 0000000..fa7ada0
--- /dev/null
@@ -0,0 +1,44 @@
+<!--
+  ============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============================================
+
+
+  -->
+
+  <app-header></app-header>
+  <app-tabbar></app-tabbar>
+
+
+
+
diff --git a/portal-FE-common/src/app/layout/layout.component.scss b/portal-FE-common/src/app/layout/layout.component.scss
new file mode 100644 (file)
index 0000000..a8adf26
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * ============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============================================
+ *
+ * 
+ */
+* {
+    -webkit-transition: margin-left 0.2s ease-in-out;
+    -moz-transition: margin-left 0.2s ease-in-out;
+    -ms-transition: margin-left 0.2s ease-in-out;
+    -o-transition: margin-left 0.2s ease-in-out;
+    transition: margin-left 0.2s ease-in-out;
+}
+.main-container {
+    margin-top: 56px;
+    margin-left: 270px;
+    padding: 15px;
+    -ms-overflow-x: hidden;
+    overflow-x: hidden;
+    overflow-y: scroll;
+    position: relative;
+    overflow: hidden;
+}
+.collapsed {
+    margin-left: 60px;
+}
+@media screen and (max-width: 992px) {
+    .main-container {
+        margin-left: 0px !important;
+    }
+}
+@media print {
+    .main-container {
+        margin-top: 0px !important;
+        margin-left: 0px !important;
+    }
+}
diff --git a/portal-FE-common/src/app/layout/layout.component.spec.ts b/portal-FE-common/src/app/layout/layout.component.spec.ts
new file mode 100644 (file)
index 0000000..5184fe4
--- /dev/null
@@ -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 { LayoutComponent } from './layout.component';
+
+describe('LayoutComponent', () => {
+  let component: LayoutComponent;
+  let fixture: ComponentFixture<LayoutComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ LayoutComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LayoutComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/layout/layout.component.ts b/portal-FE-common/src/app/layout/layout.component.ts
new file mode 100644 (file)
index 0000000..b512988
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * ============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 { Component, OnInit } from '@angular/core';
+
+@Component({
+    selector: 'app-layout',
+    templateUrl: './layout.component.html',
+    styleUrls: ['./layout.component.scss']
+})
+export class LayoutComponent implements OnInit {
+
+    collapedSideBar: boolean;
+
+    constructor() {}
+
+    ngOnInit() {}
+
+    receiveCollapsed($event) {
+        this.collapedSideBar = $event;
+    }
+}
diff --git a/portal-FE-common/src/app/layout/layout.module.ts b/portal-FE-common/src/app/layout/layout.module.ts
new file mode 100644 (file)
index 0000000..63a99d8
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * ============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 { CommonModule } from '@angular/common';
+import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgMaterialModule } from '../ng-material-module';
+import { LayoutRoutingModule } from './layout-routing.module';
+import { LayoutComponent } from './layout.component';
+import { SidebarComponent } from './components/sidebar/sidebar.component';
+import { HeaderComponent } from './components/header/header.component';
+import { GlobalSearchComponent } from './components/global-search/global-search.component';
+import { ClickOutsideModule } from 'ng-click-outside';
+import { TabbarComponent } from './components/tabbar/tabbar.component';
+import { HeaderMenuComponent } from './components/header-menu/header-menu.component';
+import { UserbarComponent } from './components/userbar/userbar.component';
+import { FooterComponent } from './components/footer/footer.component';
+import { ApplicationPipesModule } from '../shared/pipes/application-pipes.module';
+
+@NgModule({
+    imports: [
+        CommonModule,
+        NgMaterialModule,
+        LayoutRoutingModule,
+        ApplicationPipesModule,
+        NgbDropdownModule,
+        ClickOutsideModule
+    ],
+    declarations: [LayoutComponent, SidebarComponent, HeaderComponent,GlobalSearchComponent, TabbarComponent, HeaderMenuComponent, UserbarComponent, FooterComponent]
+})
+export class LayoutModule {}
diff --git a/portal-FE-common/src/app/pages/admins/admins.component.html b/portal-FE-common/src/app/pages/admins/admins.component.html
new file mode 100644 (file)
index 0000000..a47a620
--- /dev/null
@@ -0,0 +1,92 @@
+<!--
+  ============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="onap-main-view-title">
+    <h1 class="heading-page">Admins</h1>
+  </div>
+
+  <mat-form-field>
+    <mat-label>All Applications</mat-label>
+    <mat-select>
+      <mat-option>All Applications</mat-option>
+      <mat-option *ngFor="let app of availableApps" [value]="app.value" (click)="applyFilterByAppName(app.value)">{{app.title}}</mat-option>
+    </mat-select>
+  </mat-form-field>
+  &nbsp;
+  <mat-form-field>
+    <input matInput type="text" (keyup)="applyFilter($event.target.value)" placeholder="Search in entire table">
+  </mat-form-field>
+  <button type="button" class="btn btn-primary" (click)="openAddNewAdminModal()"><i class="icon ion-md-person-add"></i>
+    Add Admin</button>
+  <span class="onap-spinner" *ngIf="showSpinner"></span>
+  <table mat-table [dataSource]="adminsDataSource" matSort>
+    <!-- First Name Column -->
+    <ng-container matColumnDef="firstName">
+      <th id="col1" mat-header-cell *matHeaderCellDef mat-sort-header> First Name </th>
+      <td id="rowheader_t1_{{i}}-firstName" mat-cell *matCellDef="let element; let i = index;"> {{element.firstName}}
+      </td>
+    </ng-container>
+
+    <!-- Last Name Column -->
+    <ng-container matColumnDef="lastName">
+      <th id="col2" mat-header-cell *matHeaderCellDef mat-sort-header> Last Name </th>
+      <td id="rowheader_t1_{{i}}-lastName" mat-cell *matCellDef="let element; let i=index;"> {{element.lastName}}
+      </td>
+    </ng-container>
+
+    <!-- User ID Column -->
+    <ng-container matColumnDef="userId">
+      <th id="col3" mat-header-cell *matHeaderCellDef mat-sort-header> User ID </th>
+      <td id="rowheader_t1_{{i}}-userId" mat-cell *matCellDef="let element; let i=index;"> {{element.orgUserId}}
+      </td>
+    </ng-container>
+
+    <!-- Applications Column -->
+    <ng-container matColumnDef="appName">
+      <th id="col4" mat-header-cell *matHeaderCellDef> Applications </th>
+      <td id="rowheader_t1_{{i}}-applications" mat-cell *matCellDef="let element; let i=index;">
+        <div *ngFor="let element of element.apps; let i=index;"> {{element.appName}} </div>
+      </td>
+    </ng-container>
+
+    <tr [hidden]="adminsData.length === 0" mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+    <tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="openExistingAdminModal(row)"></tr>
+  </table>
+  <mat-paginator [hidden]="adminsData.length === 0" [pageSizeOptions]="[10, 20]" showFirstLastButtons></mat-paginator>
+</div>
\ No newline at end of file
diff --git a/portal-FE-common/src/app/pages/admins/admins.component.scss b/portal-FE-common/src/app/pages/admins/admins.component.scss
new file mode 100644 (file)
index 0000000..a7f4d38
--- /dev/null
@@ -0,0 +1,42 @@
+/*-
+ * ============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 '../pages.component';
+
+.mat-row{
+    cursor: pointer;
+}
diff --git a/portal-FE-common/src/app/pages/admins/admins.component.spec.ts b/portal-FE-common/src/app/pages/admins/admins.component.spec.ts
new file mode 100644 (file)
index 0000000..563f80f
--- /dev/null
@@ -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 { AdminsComponent } from './admins.component';
+
+describe('AdminsComponent', () => {
+  let component: AdminsComponent;
+  let fixture: ComponentFixture<AdminsComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ AdminsComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AdminsComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/pages/admins/admins.component.ts b/portal-FE-common/src/app/pages/admins/admins.component.ts
new file mode 100644 (file)
index 0000000..d7a839b
--- /dev/null
@@ -0,0 +1,133 @@
+/*-
+ * ============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 } from '@angular/core';
+import { AdminsService, ApplicationsService } from 'src/app/shared/services';
+import { Admins, AllApps } from 'src/app/shared/model';
+import { MatTableDataSource, MatSort, MatPaginator } from '@angular/material';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { NewAdminComponent } from './new-admin/new-admin.component';
+
+@Component({
+  selector: 'app-admins',
+  templateUrl: './admins.component.html',
+  styleUrls: ['./admins.component.scss']
+})
+export class AdminsComponent implements OnInit {
+  availableApps: Array<{ index: number, title: string, value: string }> = [];
+
+  constructor(private adminsService: AdminsService, private applicationService: ApplicationsService,
+    public ngModal: NgbModal) { }
+
+  showSpinner = true;
+  displayedColumns: string[] = ['firstName', 'lastName', 'userId', 'appName'];
+  adminsData: Admins[] = [];
+  adminsDataSource = new MatTableDataSource(this.adminsData);
+  @ViewChild(MatSort) sort: MatSort;
+  @ViewChild(MatPaginator) paginator: MatPaginator;
+  ngOnInit() {
+    this.adminsData = [];
+    this.getAccoutAdminsData();
+    this.getAllApps();
+  }
+
+  openAddNewAdminModal() {
+    const modalRef = this.ngModal.open(NewAdminComponent);
+    modalRef.componentInstance.title = 'New Admin';
+    modalRef.componentInstance.dialogState = 1;
+    modalRef.componentInstance.disableBack = false;
+    modalRef.componentInstance.passBackNewAdminPopup.subscribe((_result: any) => {
+      modalRef.close();
+      this.showSpinner = true;
+      this.getAccoutAdminsData();
+    }, (_reason: any) => {
+      return;
+    });
+  }
+
+  openExistingAdminModal(_adminData: Admins) {
+    const modalRef = this.ngModal.open(NewAdminComponent);
+    modalRef.componentInstance.userTitle = `${_adminData.firstName}, ${_adminData.lastName} `+'('+`${_adminData.orgUserId}`+')';
+    modalRef.componentInstance.adminModalData = _adminData;
+    modalRef.componentInstance.dialogState = 2;
+    modalRef.componentInstance.disableBack = true;
+    modalRef.componentInstance.passBackNewAdminPopup.subscribe((_result: any) => {
+      modalRef.close();
+      this.showSpinner = true;
+      this.getAccoutAdminsData();
+    }, (_reason: any) => {
+      return;
+    });
+  }
+
+  applyFilterByAppName(filterValue: string) {
+
+  }
+
+  applyFilter(filterValue: string) {
+    this.adminsDataSource.filter = filterValue.trim().toLowerCase();
+  }
+
+
+  getAccoutAdminsData() {
+    this.adminsService.getAccountAdmins().subscribe((_res: Admins[]) => {
+      this.showSpinner = false;
+      this.adminsData = _res;
+      this.adminsDataSource = new MatTableDataSource(this.adminsData);
+      this.adminsDataSource.sort = this.sort;
+      this.adminsDataSource.paginator = this.paginator;
+    });
+  }
+
+  getAllApps() {
+    this.applicationService.getAvailableApps().subscribe((_res: AllApps[]) => {
+      var realAppIndex = 1;
+      for (let i = 1; i <= _res.length; i++) {
+        if (!_res[i - 1].restrictedApp) {
+          this.availableApps.push({
+            index: realAppIndex,
+            title: _res[i - 1].title,
+            value: _res[i - 1].value
+          });
+          realAppIndex = realAppIndex + 1;
+        } else {
+        }
+      }
+    });
+  }
+
+}
diff --git a/portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.html b/portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.html
new file mode 100644 (file)
index 0000000..c43e4ba
--- /dev/null
@@ -0,0 +1,96 @@
+<!--
+  ============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">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body">
+    <app-search-users [searchTitle]="searchTitleText" [placeHolder]="placeholderText" (passBackSelectedUser)='changeSelectedUser($event)'></app-search-users>
+    <span class="onap-spinner" *ngIf="isLoading"></span>
+  </div>
+  <div class="modal-footer">
+    <button type="submit" class="btn btn-primary" [disabled]='!changedSelectedUser'
+      (click)="getAdminAppsRoles()">Next</button> &nbsp;
+    <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">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body adminApps">
+    <div ngbDropdown class="d-inline-block">
+      <h4>Administrates:</h4>
+      <button class="btn btn-outline-primary" id="dropdownBasic1" ngbDropdownToggle>Select Application</button>
+      <div ngbDropdownMenu aria-labelledby="dropdownBasic1">
+        <button *ngFor="let app of adminDropdownApps" (click)="updateDropdown(app, true)"
+          ngbDropdownItem>{{app.appName}}</button>
+      </div>
+    </div>
+    <!-- User admins list -->
+    <div class="container adminApps" *ngIf="adminAppsRoles.length > 0">
+      <table mat-table [dataSource]="adminsAppsSource">
+        <!-- Search Result Column-->
+        <ng-container matColumnDef="applications">
+          <th id="rowheader-result" mat-header-cell *matHeaderCellDef> Applications <span id="i-delete-application"
+              class="span-remove-title"> Delete </span></th>
+          <td id="table-data-{{i}}" mat-cell *matCellDef="let element; let i = index;">
+            {{element.appName}} <span id="i-delete-application" class="span-remove-admin"
+              (click)="removeAdminApp(element)"><i class="icon ion-md-trash"></i></span>
+          </td>
+        </ng-container>
+        <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
+        <tr 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> &nbsp;
+    <button type="submit" class="btn btn-primary" [disabled]='!newAppSelected'
+      (click)="updateAdminAppsRoles(adminAppsScreen)">Save</button> &nbsp;
+    <button type="button" class="btn btn-primary" (click)="activeModal.close('Close')">Close</button>
+  </div>
+</div>
\ No newline at end of file
diff --git a/portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.scss b/portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.scss
new file mode 100644 (file)
index 0000000..eb6db14
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * ============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.adminApps {
+  overflow-y: auto;
+  height: 250px;
+}
+
+.dropdown-menu.show {
+  overflow-y: auto !important;
+  height: 250px !important;
+}
+.span-remove-title {
+  float: right;
+}
+.span-remove-admin {
+  float: right;
+  cursor: pointer;
+  font-size: 20px;
+}
+
+.onap-spinner{
+  z-index: 9999;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.spec.ts b/portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.spec.ts
new file mode 100644 (file)
index 0000000..4040b0d
--- /dev/null
@@ -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 { NewAdminComponent } from './new-admin.component';
+
+describe('NewAdminComponent', () => {
+  let component: NewAdminComponent;
+  let fixture: ComponentFixture<NewAdminComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ NewAdminComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(NewAdminComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.ts b/portal-FE-common/src/app/pages/admins/new-admin/new-admin.component.ts
new file mode 100644 (file)
index 0000000..8f80138
--- /dev/null
@@ -0,0 +1,228 @@
+/*-
+ * ============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, Output, EventEmitter, Input } from '@angular/core';
+import { AdminsService } from 'src/app/shared/services';
+import { HttpErrorResponse } from '@angular/common/http';
+import { ConfirmationModalComponent } from 'src/app/modals/confirmation-modal/confirmation-modal.component';
+import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { PortalAdmin } from 'src/app/shared/model';
+import { MatTableDataSource } from '@angular/material';
+import { InformationModalComponent } from 'src/app/modals/information-modal/information-modal.component';
+import { Router } from '@angular/router';
+
+@Component({
+  selector: 'app-new-admin',
+  templateUrl: './new-admin.component.html',
+  styleUrls: ['./new-admin.component.scss']
+})
+export class NewAdminComponent implements OnInit {
+
+  @Input() dialogState: number;
+  @Input() userTitle: string;
+  @Input() disableBack: boolean;
+  @Input() adminModalData: any;
+  @Output() passBackNewAdminPopup: EventEmitter<any> = new EventEmitter();
+  searchTitleText = 'Enter First Name, Last Name or Org User Id';
+  placeholderText = 'Search';
+  changedSelectedUser: PortalAdmin;
+  adminAppsRoles: any;
+  adminDropdownApps: any;
+  isLoading: boolean;
+  newAppSelected: boolean;
+  adminAppSelectAndUnselectData: any;
+  displayedColumns: string[] = ['applications'];
+  adminsAppsSource = new MatTableDataSource(this.adminAppsRoles);
+  constructor(public router: Router, private adminsService: AdminsService, public ngModal: NgbModal, public activeModal: NgbActiveModal) { }
+
+  ngOnInit() {
+    this.adminAppsRoles = [];
+    this.changedSelectedUser = null;
+    if (this.disableBack){
+      this.changedSelectedUser = this.adminModalData;
+      this.getAdminAppsRoles();
+    }
+    this.adminDropdownApps = [];
+    this.adminAppSelectAndUnselectData = [];
+  }
+
+  changeSelectedUser(user: PortalAdmin) {
+    this.changedSelectedUser = user;
+    this.userTitle = `${this.changedSelectedUser.firstName}, ` + ` ${this.changedSelectedUser.lastName} ` + ` (${this.changedSelectedUser.orgUserId})`;
+  }
+
+  getAdminAppsRoles() {
+    this.isLoading = true;
+    this.adminsService.getAdminAppsRoles(this.changedSelectedUser.orgUserId).subscribe((_res: any) => {
+      JSON.stringify(_res);
+      if (!_res.appsRoles) {
+        return;
+      }
+      this.adminAppsRoles = [];
+      for (var i = 0; i < _res.appsRoles.length; i++) {
+        if (!_res.appsRoles[i].restrictedApp && _res.appsRoles[i].isAdmin) {
+          this.adminAppsRoles.push({
+            id: _res.appsRoles[i].id,
+            appName: _res.appsRoles[i].appName,
+            isAdmin: _res.appsRoles[i].isAdmin,
+            restrictedApp: _res.appsRoles[i].restrictedApp
+          });
+        } else if (!_res.appsRoles[i].restrictedApp) {
+          this.adminDropdownApps.push({
+            id: _res.appsRoles[i].id,
+            appName: _res.appsRoles[i].appName,
+            isAdmin: _res.appsRoles[i].isAdmin,
+            restrictedApp: _res.appsRoles[i].restrictedApp
+          });
+        }
+      }
+      this.isLoading = false;
+      this.newAppSelected = false;
+      this.dialogState = 2;
+      this.adminsAppsSource = new MatTableDataSource(this.adminAppsRoles);
+    });
+  }
+
+  navigateBack() {
+    this.dialogState = 1;
+  }
+
+  removeAdminApp(app: any) {
+    const modalRef = this.ngModal.open(InformationModalComponent);
+    modalRef.componentInstance.title = "Confirmation";
+    modalRef.componentInstance.message = `Are you sure you want to delete ${app.appName}?`;
+    modalRef.result.then((result) => {
+      if (result === 'Ok') {
+        this.adminAppsRoles.forEach((item: any, index: any) => {
+          if (item === app) this.adminAppsRoles.splice(index, 1);
+        });
+        //call from delete admin app
+        this.updateDropdown(app, false);
+        this.adminsAppsSource = new MatTableDataSource(this.adminAppsRoles);
+      }
+    }, (resut) => {
+      return;
+    })
+  }
+
+  updateDropdown(_newValue: any, isDropdownCall: boolean) {
+    // app is selected from dropdown
+    if (isDropdownCall) {
+      this.adminDropdownApps.forEach((item: any, index: any) => {
+        if (item === _newValue) this.adminDropdownApps.splice(index, 1);
+      });
+      this.getadminAppSelectAndUnselectedData(_newValue);
+      _newValue.isAdmin = true;
+      this.adminAppsRoles.push(_newValue);
+      this.adminsAppsSource = new MatTableDataSource(this.adminAppsRoles);
+    } else {    // app is removed from the admin list
+      this.getadminAppSelectAndUnselectedData(_newValue);
+      _newValue.isAdmin = false;
+      this.adminDropdownApps.push(_newValue);
+    }
+
+    // disable save button if nothing new in the admin list
+    if (this.adminAppSelectAndUnselectData.length > 0)
+      this.newAppSelected = true;
+    else
+      this.newAppSelected = false;
+
+  }
+
+  private getadminAppSelectAndUnselectedData(_newValue: any) {
+    const index: number = this.adminAppSelectAndUnselectData.indexOf(_newValue);
+    if (index !== -1) {
+      this.adminAppSelectAndUnselectData.splice(index, 1); // if found, remove selected app from dropdown in the list
+    }
+    else {
+      this.adminAppSelectAndUnselectData.push(_newValue);
+    }
+  }
+
+  remindToAddUserIfNecessary() {
+    let adminAddedToNewApp = true;
+    if ((this.adminAppsRoles != null) && (this.adminAppsRoles.length > 0)) {
+      for (var i = 0; i < this.adminAppSelectAndUnselectData.length; i++) {
+        var foundApp = false;
+        for (var j = 0; j < this.adminAppsRoles.length; j++) {
+          if (this.adminAppsRoles[j] === this.adminAppSelectAndUnselectData[i]) {
+            foundApp = true;
+          }
+        }
+        if (foundApp === false) {
+          adminAddedToNewApp = true;
+          break;
+        }
+      }
+    } else {
+      adminAddedToNewApp = true;
+    }
+    if (adminAddedToNewApp === true) {
+      const modalRef = this.ngModal.open(InformationModalComponent);
+      modalRef.componentInstance.title = "Confirmation";
+      modalRef.componentInstance.message = 'Add this person as an application user? This allows them to access the application from ONAP Portal. Press OK to go to the Add Users page.';
+      modalRef.result.then((_res) => {
+        if (_res === 'Ok') {
+          this.router.navigate(['/users']);
+        }
+      });
+    }
+  }
+
+  updateAdminAppsRoles() {
+    const modalRef = this.ngModal.open(InformationModalComponent);
+    modalRef.componentInstance.title = "Admin Update";
+    modalRef.componentInstance.message = 'Are you sure you want to make these admin changes?';
+    modalRef.result.then((result) => {
+      if (result === 'Ok') {
+        this.adminsService.updateAdminAppsRoles({ orgUserId: this.changedSelectedUser.orgUserId, appsRoles: this.adminAppsRoles }).subscribe(_data => {
+          this.passBackNewAdminPopup.emit(_data);
+          this.remindToAddUserIfNecessary();
+        }, (_err: HttpErrorResponse) => {
+          this.passBackNewAdminPopup.emit(_err);
+          const modalErrorRef = this.ngModal.open(ConfirmationModalComponent);
+          modalErrorRef.componentInstance.title = "Error";
+          if (_err.status) {
+            modalErrorRef.componentInstance.message = "There was a unknown problem while adding admin to selected application(s)." + "Please try again later. Error Status: " + _err.status;
+          }
+        });
+      }
+    }, (reason) => {
+      return;
+    });
+  }
+}
diff --git a/portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.html b/portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.html
new file mode 100644 (file)
index 0000000..5e10b78
--- /dev/null
@@ -0,0 +1,100 @@
+<!--
+  ============LICENSE_START==========================================
+  ONAP Portal
+  ===================================================================
+  Copyright (C) 2017 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="functionalMenu-details-modal">
+    <!--Modal Headers-->
+    <div class="modal-header">
+      <h4 class="modal-title">Functional Menu</h4>
+      <button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross')">
+        <span aria-hidden="true">&times;</span>
+      </button>
+    </div>
+  
+    <!--Modal Body goes here-->
+    <div class="modal-body">
+        <div class="parent">
+            <div class="item-label">Parent</div>
+            <input id="input-parent" class="functionalMenu-height"
+               [(ngModel)]="nodedetails.menuLocation" [disabled]="isParentDisable"
+              type="text"      name="parent"  readonly="readonly"  />
+        </div>  
+        <div class="title" >
+            <div class="item-label">Title</div>
+            <input id="input-title"  placeholder="Enter text" class="functionalMenu-height" 
+              [(ngModel)]="nodedetails.name" type="text" autocomplete="off" name="title" maxlength="100" [disabled]="isViewMode"/>
+        </div>
+        <div class="url" >
+            <div class="item-label">URL</div>
+            <input id="input-title"  placeholder="http://" class="functionalMenu-height" 
+              [(ngModel)]="nodedetails.url" type="text" autocomplete="off" name="url" maxlength="100" [disabled]="isViewMode"/>
+        </div>
+        <div class="application-select">                                       
+            <mat-form-field>
+              <mat-label>App</mat-label>
+              <mat-select name="functional-menu-application-select" [disabled]="isViewMode"
+                 [(ngModel)]="nodedetails.selectedAppIndex" 
+                 (ngModelChange)="updateSelectedApp(nodedetails.selectedAppIndex)" 
+                 [(value)]="selectedApp"
+              >
+                <mat-option *ngFor="let d of availableApps" [value]="d.index" >{{d.title}}</mat-option>
+              </mat-select>
+            </mat-form-field>
+        </div>
+        <div class="role-select" [hidden]="hideRoleField">                                     
+            <mat-form-field>
+              <mat-label>Role</mat-label>
+              <mat-select name="functional-menu-role-select" [disabled]="isViewMode"
+                 [(ngModel)]="nodedetails.selectedRole" [(value)]="selectedRole"
+              >
+                <mat-option *ngFor="let d of availableRoles" [value]="d.roleId" >{{d.rolename}}</mat-option>
+              </mat-select>
+            </mat-form-field>
+        </div>
+    </div>
+    
+    <!--Modal Footer goes Here-->
+    <div class="modal-footer">
+      <button type="button" class="btn btn-primary" (click)="switchToAddMode()">Add</button> &nbsp;
+      <button type="button" class="btn btn-primary" (click)="switchToEditMode()">Edit</button> &nbsp;
+      <button type="button" class="btn btn-primary" *ngIf="(isEditMode)" (click)="saveChanges()">Save</button> &nbsp;
+      <button type="button" class="btn btn-primary" (click)="deleteMenuItem()">Delete</button> &nbsp;
+      <button type="button" class="btn btn-primary" (click)="activeModal.close('Close')">Cancel</button>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.scss b/portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.scss
new file mode 100644 (file)
index 0000000..ba9a1e3
--- /dev/null
@@ -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============================================
+ *
+ * 
+ */
+
+::ng-deep .modal-dialog { 
+    max-width: 550px;
+    width: 550px;
+    overflow-x: auto;
+    overflow-y: auto;
+}
+
+::ng-deep .mat-form-field-infix {
+    display: block;
+    position: relative;
+    flex: auto;
+    min-width: 0;
+    width: 448px !important;
+}
+
+.container .functionalMenu-details-modal {
+    padding: 16px;
+    height: 537px;
+    overflow: auto;
+}
+
+.functionalMenu-details-modal input[type="text"] {
+    width: 28em;
+    margin-bottom: 10px;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.spec.ts b/portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.spec.ts
new file mode 100644 (file)
index 0000000..de79b9d
--- /dev/null
@@ -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 { FunctionalMenuDialogComponent } from './functional-menu-dialog.component';
+
+describe('FunctionalMenuDialogComponent', () => {
+  let component: FunctionalMenuDialogComponent;
+  let fixture: ComponentFixture<FunctionalMenuDialogComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ FunctionalMenuDialogComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(FunctionalMenuDialogComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.ts b/portal-FE-common/src/app/pages/functional-menu/functional-menu-dialog/functional-menu-dialog.component.ts
new file mode 100644 (file)
index 0000000..a183973
--- /dev/null
@@ -0,0 +1,446 @@
+/*-
+ * ============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 { FunctionalMenuService } 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-functional-menu-dialog',
+  templateUrl: './functional-menu-dialog.component.html',
+  styleUrls: ['./functional-menu-dialog.component.scss']
+})
+export class FunctionalMenuDialogComponent implements OnInit {
+
+  @Input() nodedata: any;
+  @Input() operationName: string;
+  @Output() passEntry: EventEmitter<any> = new EventEmitter();
+  isEditMode: boolean = false;
+  isViewMode: boolean = false;
+  isAddItemMode: boolean = false;
+  selectedItem: any;
+  result: any;
+  availableRoles: any;
+  preSelectedRoles: any;
+  isAllApplications: boolean  = false;
+  availableApps: any
+  selectedRole: any = [];
+  selectedApp={
+    index:null,
+    isDisabled: null
+  };
+  selectedAppIndex: any;
+  menutitle:string;
+  menuLocation:string;
+  nodedetails: any;
+  isParentDisable:boolean =true;
+  hideRoleField:boolean = true;
+  conflictMessages = {};
+  functionalMenuForm = {};
+  
+  constructor(public functionalMenuService : FunctionalMenuService, public ngbModal: NgbModal, public activeModal: NgbActiveModal) { }
+
+  ngOnInit() {
+    //console.log("nodedata in dialog ",this.nodedata);
+    this.nodedetails = Object.assign({}, this.nodedata);
+    this.isViewMode = true;
+    this.selectedItem = this.nodedata;
+    this.selectedRole = [];
+    this.availableRoles = [];
+    if(this.nodedata && (this.isViewMode || this.isEditMode) && this.isLeafMenuItem(this.nodedetails)){
+        this.selectedApp.index = this.nodedetails.appid;
+        this.selectedAppIndex=this.nodedetails.appid;
+        this.getAvailableRoles(this.selectedAppIndex);
+    }
+    
+    if(this.isViewMode || this.isEditMode){
+         this.nodedetails.menutitle = this.nodedetails.name;
+         this.nodedetails.menuLocation = this.isParentMenuItem(this.nodedata) ? this.nodedata.name : this.nodedata.parent.name;
+    }else{
+        this.nodedetails.menutitle = '';
+        this.nodedetails.menuLocation = this.nodedata.name;
+    }
+    this.nodedetails.selectedAppIndex = (this.selectedItem.appid) ? this.selectedItem.appid : 0;
+    this.getAvailableApps();
+    if(this.selectedItem.appid && this.selectedItem.appid >0){
+      this.getAvailableRoles(this.selectedItem.appid);
+    }
+  }
+
+  switchToEditMode(){
+    //console.log("switchToEditMode :: ",this.nodedata);
+    this.isViewMode = false;
+    this.isEditMode = true;
+    this.isParentDisable =true;
+    this.nodedetails.name = this.selectedItem.name;
+    this.nodedetails.url = this.selectedItem.url;
+    this.nodedetails.selectedAppIndex = (this.selectedItem.appid) ? this.selectedItem.appid : 0;
+    this.nodedetails.selectedRole = (this.selectedItem.roles) ? this.selectedItem.roles : 0;
+  }
+
+  switchToAddMode(){
+    //console.log("switchToAddMode :: ",this.nodedata);
+    if(this.selectedItem != null && this.selectedItem.getLevel() >= 4){
+      this.openConfirmationModal("","You are not allowed to have a menu item at a level greater than 4.");
+      return ;
+    }
+    //this.isViewMode = false;
+    this.isViewMode = false;
+    this.isEditMode = true;
+    this.isAddItemMode = true;
+    this.nodedetails.name = "";
+    this.nodedetails.url = "";
+    this.nodedetails.selectedAppIndex = 0;
+    this.nodedetails.selectedRole = 0;
+  }
+
+  /**
+   * deleteMenuItem
+   * @param selectedItem 
+   */
+  deleteMenuItem(){
+    if(this.selectedItem.children!=null && this.selectedItem.children.length>0){
+      const modalRef = this.ngbModal.open(ConfirmationModalComponent);
+      modalRef.componentInstance.title = "";
+      modalRef.componentInstance.message = 'You are not allowed to delete a menu item that has children. You can only delete leaf menu items.';
+      modalRef.result.then((result) => { }, (resut) => {return;});
+    }else{
+      const modalRef = this.ngbModal.open(InformationModalComponent);
+      modalRef.componentInstance.title = "Confirmation";
+      modalRef.componentInstance.message = 'Are you sure you want to delete '+ this.selectedItem.name+' ?';
+      modalRef.result.then((result) => {
+        if (result === 'Ok') {
+          this.functionalMenuService.deleteMenuItem(this.selectedItem.menuId)
+          .subscribe(_data => {
+              this.result = _data
+              this.passEntry.emit(this.result);
+              let successMsg = "Item Deleted Successfully";
+              this.openConfirmationModal("Success",successMsg);
+              this.ngbModal.dismissAll();
+          }, error =>{
+            console.log(error);
+            let deleteErrorMsg  = 'There was an error while deleting the item.'+error.message;
+            this.openConfirmationModal("Error",deleteErrorMsg);
+            return;
+          });
+        }
+      }, (resut) => {
+        
+      })
+    }
+  }
+
+  updateSelectedApp(appid){
+    //console.log("updateSelectedApp called with appId :: ",appid);
+    if (!appid) {
+      return;
+    }
+    this.getAvailableRoles(appid);
+  }
+
+  getAvailableRoles(appid){
+    //console.log("getAvailableRoles called with appId :: ",appid);
+    if (appid != null && appid >0) {
+      this.functionalMenuService.getManagedRolesMenu(appid)
+      .subscribe(rolesObj => {
+        this.availableRoles = rolesObj;
+        if(this.availableRoles && this.availableRoles.length >0){
+          this.hideRoleField = false;
+        }
+        this.preSelectedRoles = {roles:[]};
+        this.preSelectedRoles = {roles:[]};
+
+        if((this.isEditMode) && this.isMidLevelMenuItem(this.nodedata)){
+            // in Edit flow , for Midlevel menu item no need to preSelect.
+            this.preSelectedRoles = {roles:[]};
+        }else if(this.nodedata && this.isEditMode && this.isLeafMenuItem(this.nodedata) && this.nodedata.appid!=appid) {
+            // in Edit flow , for LeafMenuItem, if appid changed then no need to preSelect.
+            this.preSelectedRoles = {roles:[]};
+        }else{
+            if(this.nodedata && this.nodedata.roles){
+                for(var i=0; i< this.nodedata.roles.length; i++){
+                    var role = {"roleId": this.nodedata.roles[i]};
+                    this.preSelectedRoles.roles.push(role);
+                }
+            }
+        }
+        
+        if(this.nodedata.rolesObj){
+          for(var i=0; i< this.nodedata.rolesObj.length;i++){
+            //this.availableRoles[i].isApplied = false;
+            for(var j=0;j<this.preSelectedRoles.roles.length;j++){
+              if(this.preSelectedRoles.roles[j].roleId==this.availableRoles[i].roleId){
+                this.availableRoles[i].isApplied=true;
+                this.nodedetails.selectedRole = (this.preSelectedRoles.roles[j]) ? this.preSelectedRoles.roles[j] : 0;
+                break;
+              }
+          }
+          }
+        }
+      }, error =>{
+        console.log(error);
+        let errorMsg = 'There was an error while gettting available roles. ' + error.message;
+        this.openConfirmationModal("",errorMsg);
+        return;
+      });
+      //console.log("this.availableRoles >>>>>",this.availableRoles);
+    }else{
+      console.log("FunctionalMenuDialogComponent::getAvailableRoles: appid was null or -1");
+    }
+  }
+
+  getAvailableApps(){
+    this.isAllApplications = true;
+    this.functionalMenuService.getAvailableApplications()
+      .subscribe(apps => {
+        this.availableApps = apps;
+        if (this.nodedetails && this.nodedetails.index) {
+          for (var i = 0; i < this.availableApps.length; i++) {
+              if (apps[i].index === this.nodedetails.index) {
+                  //console.log("MenuDetailsModalCtrl::getAvailableApps: found app with index: " + this.nodedetails.index);
+                  //console.log("MenuDetailsModalCtrl::getAvailableApps: setting isDisabled to: " + !apps[i].enabled);
+                  this.nodedetails.isDisabled = !apps[i].enabled;
+                  break;
+              }
+          }
+          //console.log("didn't find index: " + this.nodedetails.index);
+        }
+      }, error =>{
+        console.log(error);
+        this.isAllApplications = false;
+        let errorMsg = 'There was a problem retrieving the Applications. '+error.message;
+        this.openConfirmationModal("Error",errorMsg);
+      });
+  }
+
+  isLeafMenuItem(menu: any){
+      return menu.children.length>0 ? false : true;
+  }
+
+  isMidLevelMenuItem(menu: any){
+      return menu.parentMenuId!=null && menu.children.length>0 ? true : false;
+  }
+
+  isParentMenuItem(menu: any){
+      return menu.parentMenuId!=null ? false : true;
+  };
+
+ isRoleSelected(){
+   var selectedRoleIds=[];
+   for(var i=0;i<this.availableRoles.length;i++){
+       if(this.availableRoles[i].isApplied){
+          selectedRoleIds.push(this.availableRoles[i].roleId);
+          return true;
+       }
+     }
+   return false;
+  }  
+
+  getDialogTitle = (source) => {
+    switch (source) {
+      case 'edit':
+          return "Functional Menu - Edit";
+      case 'view':
+          return "Functional Menu - View";
+      case 'add':
+          return "Functional Menu - Add";
+      default:
+          return "Functional Menu";
+    };
+  }
+
+  saveChanges(){
+    if(!this.nodedetails.menuLocation || !this.nodedetails.name 
+        || !this.nodedetails.url || !this.nodedetails.selectedAppIndex 
+        || !this.nodedetails.selectedAppIndex || !this.nodedetails.selectedRole){
+      this.openConfirmationModal("","All fields are mandatory, please provide inputs for all the fields.");
+      return;
+    }
+    /*
+    if(!!this.nodedetails.url && (!this.selectedApp || this.selectedAppIndex <=0)) {
+      this.openConfirmationModal("","Please select the appropriate app, or remove the url");
+      return;
+    }else if(!this.nodedetails.url && (this.selectedApp) && this.selectedApp.index>0){
+        this.openConfirmationModal("","Please enter url, or select No Application");
+        return;
+    }else if(!this.nodedetails.menutitle){
+        this.openConfirmationModal("","Please enter the Menu title");
+        return;
+    }
+    */
+    
+    if(this.isAddItemMode){
+      if(this.selectedItem != null && this.selectedItem.getLevel() >= 4){
+        this.openConfirmationModal("","You are not allowed to have a menu item at a level greater than 4.");
+        return ;
+      }else{
+        let data = null;
+        let selectedMenuDetails = null;
+        this.functionalMenuService.getFunctionalMenu(this.selectedItem.menuId)
+          .subscribe(_data => {
+              selectedMenuDetails = _data
+              if((this.selectedItem.children===null || this.selectedItem.children.length == 0) && (!!selectedMenuDetails.url
+                 || !!selectedMenuDetails.appid || !!selectedMenuDetails.roles)){
+                let warning_message = 'Warning: the child menu item "' + 
+                              this.selectedItem.name + '" is already configured with an application. You can create a new mid-level menu item.';
+                this.openConfirmationModal("",warning_message);
+                return;
+              }else{
+                if(this.selectedItem){
+                    var selectedRoleIds=[];
+                    //console.log("Selected Role ID ; ",this.nodedetails.selectedRole);
+                    if(this.nodedetails.selectedRole){
+                      selectedRoleIds.push(this.nodedetails.selectedRole);
+                    }
+                    
+                    let applicationid: any = null;
+                    if(!this.nodedata){
+                      applicationid = null;
+                    }else{
+                      applicationid = this.nodedata.appid;
+                    }
+                    var newMenuItem = {
+                        menuId:null, // this is a new menu item
+                        column:this.nodedata.column,
+                        text:this.nodedetails.name,
+                        // We are creating this new menu item under the menu item that was clicked on.
+                        parentMenuId:this.nodedata.menuId,
+                        url:(this.nodedetails.url) ? this.nodedetails.url : null,
+                        appid:(this.nodedetails.selectedAppIndex) ? this.nodedetails.selectedAppIndex: null,
+                        roles:selectedRoleIds
+                    };
+                    //console.log("Add menu Item newMenuItem :: ",newMenuItem)
+                    this.functionalMenuService.saveMenuItem(newMenuItem)
+                    .subscribe(_data => {
+                        this.result = _data
+                        //console.log("add menu item response :: ",_data);
+                        this.passEntry.emit(this.result);
+                        let successMsg = "Item added successfully";
+                        this.openConfirmationModal("Success",successMsg);
+                    }, error =>{
+                      console.log(error);
+                      if(error.status === 409){//Conflict
+                        this.handleConflictErrors(error);
+                      } else {
+                        let errorMsg = "There was a problem saving your menu. Please try again later. Error Status: "+error.status
+                        this.openConfirmationModal("",errorMsg);
+                        return;
+                      }
+                   });
+                }
+              }
+          }, error =>{
+            //console.log(error);
+            let errorMsg = "There was a problem saving your menu. Please try again later. Error Status: "+error.status
+            this.openConfirmationModal("",errorMsg );
+            return;
+        });
+      }
+    }else{
+      //edit mode..
+      //console.log('MenuDetailsModalCtrl::saveChanges: Will be saving an edit menu item');
+      var selectedRoleIds=[];
+      //console.log("Selected Role ID ---->>>> ",this.nodedetails.selectedRole);
+      if(this.nodedetails.selectedRole){
+        selectedRoleIds.push(this.nodedetails.selectedRole);
+      }
+      let activeMenuItem = {
+          menuId:this.nodedata.menuId,
+          column:this.nodedata.column,
+          text:this.nodedetails.name,
+          parentMenuId:this.nodedata.parentMenuId,
+          url:(this.nodedetails.url) ? this.nodedetails.url : null,
+          appid: (this.nodedetails.selectedAppIndex) ? this.nodedetails.selectedAppIndex: null,          
+          roles:selectedRoleIds
+      };
+      if ((activeMenuItem.appid==null && activeMenuItem.url=="") || (activeMenuItem.appid==null && activeMenuItem.url=="undefined")) {
+        activeMenuItem.roles = null;
+      }
+      //console.log("Update menu Item activeMenuItem :: ",activeMenuItem);
+      this.functionalMenuService.saveEditedMenuItem(activeMenuItem)
+      .subscribe(_data => {
+          this.result = _data
+          //console.log("Edit menu item response :: ",_data);
+          this.passEntry.emit(this.result);
+          let successMsg = "Item updated successfully";
+          this.openConfirmationModal("Success",successMsg);
+          //this.ngbModal.dismissAll();
+      }, error =>{
+        //console.log(error);
+        let errorMsg = "There was a problem updating your menu. Please try again later. Error Status: "+error.status;
+        this.openConfirmationModal("",errorMsg);
+        return;
+      });
+    } 
+  }
+
+  //This part handles conflict errors (409)
+  handleConflictErrors(error){
+    if(!error.data){
+      return;
+    }
+    if(!error.data.length){ //support objects
+      error.data = [error.data]
+    }
+    //console.log('MenuDetailsModalCtrl::handleConflictErrors: err.data = ' + JSON.stringify(error.data));
+    if(error.data){
+      error.data.forEach(item => {
+         //set conflict message
+         this.conflictMessages[item.field.name] = item.errorCode;
+         //set field as invalid
+         //console.log('MenuDetailsModalCtrl::handleConflictErrors: fieldName = ' + item.field.name);
+         this.functionalMenuForm[item.field.name].$setValidity('conflict', false);
+      });
+    }
+  }
+
+  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-common/src/app/pages/functional-menu/functional-menu.component.html b/portal-FE-common/src/app/pages/functional-menu/functional-menu.component.html
new file mode 100644 (file)
index 0000000..d898563
--- /dev/null
@@ -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============================================
+   
+  -->
+  
+<div class="container">
+  <div id="title" class="w-onap-main-view-title">
+    <h1 class="heading-page">Edit Functional Menu</h1>
+  </div>
+  <div id="jqTree"></div>
+  <div class="functional-admin-button-container">
+    <button id="regenrate-functionalmenu-btn"
+      class="btn btn-alt btn-small"
+      (click)="regenerateFunctionalMenuAncestors()">Regenerate Menu
+    </button>
+    <div class="regenerate-functionalmenu-btn-txt">
+      <span class="n16r">Click when you are done with your changes.</span>
+    </div>
+  </div>
+</div>
diff --git a/portal-FE-common/src/app/pages/functional-menu/functional-menu.component.scss b/portal-FE-common/src/app/pages/functional-menu/functional-menu.component.scss
new file mode 100644 (file)
index 0000000..c716252
--- /dev/null
@@ -0,0 +1,95 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
+
+.functional-menu-main {
+    background-color: #fff;
+    position: fixed;
+    top: 105px;
+    left: 0;
+    right: 0;
+    bottom: 75px;
+    padding-top: 10px;
+    overflow-y: scroll;
+    padding-left: 0;
+}
+
+.functional-menu-main .functional-menu-container {
+    position: relative;
+    padding-right: 0;
+    padding-left: 0;
+    padding-bottom: 32px;
+    background-color: #fff;
+}
+
+.w-ecomp-main-view-title {
+    color: #191919;
+    font-family: Omnes-ECOMP-W02-Medium,Arial;
+    font-size: 24px;
+    width: 1170px;
+    padding-bottom: 15px;
+    margin: auto;
+}
+
+.functional-menu-main .functional-menu-container .tree {
+    margin: auto;
+    width: 1170px;
+    font-size: 16px;
+}
+
+.functional-admin-button-container {
+    padding-top: 10px;
+    width: 1170px;
+    margin: auto;
+}
+
+.btn-small {
+    padding: 10px 19px 9px 18px;
+    font-size: 1.5rem;
+    border-radius: 8px;
+}
+
+.btn-alt {
+    border-color: #087ac2 transparent #0568ae;
+    background-color: #0568ae;
+    background: linear-gradient(to bottom, #087ac2 0%, #0568ae 100%);
+    color: #ffffff;
+}
+
+.functional-menu-main .regenerate-functionalmenu-btn-txt {
+    color: #000;
+}
diff --git a/portal-FE-common/src/app/pages/functional-menu/functional-menu.component.spec.ts b/portal-FE-common/src/app/pages/functional-menu/functional-menu.component.spec.ts
new file mode 100644 (file)
index 0000000..c5c562e
--- /dev/null
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FunctionalMenuComponent } from './functional-menu.component';
+
+describe('FunctionalMenuComponent', () => {
+  let component: FunctionalMenuComponent;
+  let fixture: ComponentFixture<FunctionalMenuComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ FunctionalMenuComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(FunctionalMenuComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/pages/functional-menu/functional-menu.component.ts b/portal-FE-common/src/app/pages/functional-menu/functional-menu.component.ts
new file mode 100644 (file)
index 0000000..655b4cb
--- /dev/null
@@ -0,0 +1,192 @@
+/*-
+ * ============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';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { FunctionalMenuService } from 'src/app/shared/services';
+import { ConfirmationModalComponent } from 'src/app/modals/confirmation-modal/confirmation-modal.component';
+import { FunctionalMenuDialogComponent } from './functional-menu-dialog/functional-menu-dialog.component';
+
+
+@Component({
+  selector: 'app-functional-menu',
+  templateUrl: './functional-menu.component.html',
+  styleUrls: ['./functional-menu.component.scss']
+})
+export class FunctionalMenuComponent implements OnInit {
+
+  result: any;
+  functionalMenu: any = [];
+  treedata = [];
+  isEditMode: boolean;
+  operationName: string;
+  self: any;
+  constructor(public functionalMenuService : FunctionalMenuService, public ngbModal: NgbModal) { }
+
+  ngOnInit() {
+    this.self = this;
+    this.functionalMenu = [];
+    this.getFunctionalMenu();
+
+  }
+
+  /**
+   * regenerateFunctionalMenuAncestors
+   */
+  regenerateFunctionalMenuAncestors(){
+    this.functionalMenuService.regenerateFunctionalMenuAncestors()
+    .subscribe(_data => {
+        this.result = _data
+        if(this.result){
+          const modalRef = this.ngbModal.open(ConfirmationModalComponent);
+          modalRef.componentInstance.title = "";
+          modalRef.componentInstance.message = 'You have successfully regenerated the menu.';
+          modalRef.result.then((result) => { }, (resut) => {return;});
+          this.getFunctionalMenu();
+        }
+    }, error =>{
+      console.log(error);
+      const modalRef = this.ngbModal.open(ConfirmationModalComponent);
+      modalRef.componentInstance.title = "";
+      modalRef.componentInstance.message = 'There was an error while regenerating the menu.';
+      modalRef.result.then((result) => { }, (resut) => {return;});
+    });
+  }
+
+  /**
+   * getFunctionalMenu
+   */
+  getFunctionalMenu(){
+    let actualData=[];
+    this.functionalMenuService.getManagedFunctionalMenu()
+    .subscribe(_data => {
+        this.result = _data;
+        if(this.result){
+          for(let i = 0; i < this.result.length; i++){
+            this.result[i].children=[];
+            this.result[i].label= this.result[i].text;
+            this.result[i].id= this.result[i].text;
+          }
+          //Adding actual child items to children array in res objects
+          for(let i = 0; i < this.result.length; i++){
+             let parentId=this.result[i].menuId;
+             for(let j = 0; j < this.result.length; j++){
+                let childId=this.result[j].parentMenuId;
+                if(parentId===childId){
+                  this.result[i].children.push(this.result[j]);
+                }
+             }
+          }
+           // Sort the top-level menu items in order based on the column
+          this.result.sort(function(a, b) {
+            return a.column-b.column;
+          });
+
+          // Sort all the children in order based on the column
+          for(let i = 0; i <  this.result.length; i++){
+              this.result[i].children.sort(function(a, b){
+                  return a.column-b.column;
+              });
+          }
+
+          //Forming actual parent items
+          for(let i = 0; i <  this.result.length; i++){
+              let parentId= this.result[i].parentMenuId;
+              if(parentId===null){
+                  actualData.push( this.result[i]);
+              }
+          }
+
+          this.treedata = actualData;
+          //console.log("this.treedata :: ",this.treedata);
+          
+          if(this.treedata){
+            this.buildTree(this.treedata,this.ngbModal, this.self);
+          }
+          
+       }
+    }, error =>{
+      console.log(error);
+    });
+
+  }
+
+  /**
+   * buildTree
+   * @param treedataarray 
+   * @param ngbModal 
+   */
+  buildTree(treedataarray,ngbModal: NgbModal , _self){
+    //console.log("treedataarray>>>>",treedataarray);
+    $(function() {
+        $('#jqTree').tree('loadData', treedataarray);
+        $('#jqTree').tree({
+            data: treedataarray,
+            autoOpen: false,
+            dragAndDrop: true,
+            onCreateLi: function(node, $li) {
+                ////console.log("node >>",node);
+            }
+        }).on(
+          'tree.contextmenu',
+          function(event:any) {
+              // The clicked node is 'event.node'
+              var node = event.node;
+              openMenuDetailsModal(node, "view");
+          }
+        );
+
+        var openMenuDetailsModal = function(node: any, actionName: string ){
+          const modalRef = ngbModal.open(FunctionalMenuDialogComponent, { size: 'lg' });
+          modalRef.componentInstance.title = 'Functional Menu ',actionName;
+          if(node != 'undefined' && node){
+            modalRef.componentInstance.nodedata = node;
+            modalRef.componentInstance.operationName = actionName;
+          }else{
+            modalRef.componentInstance.nodedata  = {};
+          }
+          modalRef.componentInstance.passEntry.subscribe((receivedEntry: any) => {
+            //console.log("receivedEntry>>>>",receivedEntry);
+            ngbModal.dismissAll();
+            if(receivedEntry.httpStatusCode===200){
+              _self.getFunctionalMenu();
+            }
+          });
+        }
+     });
+  }
+}
diff --git a/portal-FE-common/src/app/pages/functional-menu/jqTreeContextMenu.js b/portal-FE-common/src/app/pages/functional-menu/jqTreeContextMenu.js
new file mode 100644 (file)
index 0000000..fa24018
--- /dev/null
@@ -0,0 +1,195 @@
+(function ($) {
+       if (!$.fn.tree) {
+               throw "Error jqTree is not loaded.";
+       }
+
+       $.fn.jqTreeContextMenu = function (menuElement, callbacks) {
+               //
+               // TODO:
+               // * Make sure the useContextMenu option is set in jqTree, either complain or set it automatically
+               // * Make menu fade in/out
+               //
+               var self = this;
+               var $el = this;
+
+               // The jQuery object of the menu div.
+               var $menuEl = menuElement;
+               
+               // This hash holds all menu items that should be disabled for a specific node.
+               var nodeToDisabledMenuItems = {};
+               
+               // Hide the menu div.
+               $menuEl.hide();
+
+               // Disable system context menu from beeing displayed.
+               $el.bind("contextmenu", function (e) { 
+                       e.preventDefault();
+                       return false; 
+               });
+
+               // Handle the contextmenu event sent from jqTree when user clicks right mouse button.
+               $el.bind('tree.contextmenu', function (event) {
+                       var x = event.click_event.pageX;
+                       var y = event.click_event.pageY;
+                       var yPadding = 5;
+                       var xPadding = 5;
+                       var menuHeight = $menuEl.height();
+                       var menuWidth = $menuEl.width();
+                       var windowHeight = $(window).height();
+                       var windowWidth = $(window).width();
+                       
+                       if (menuHeight + y + yPadding > windowHeight) {
+                               // Make sure the whole menu is rendered within the viewport. 
+                               y = y - menuHeight;
+                       }
+                       if (menuWidth + x + xPadding > windowWidth) {
+                               // Make sure the whole menu is rendered within the viewport. 
+                               x = x - menuWidth;
+                       }
+
+                       // Handle disabling and enabling of menu items on specific nodes.
+                       if (Object.keys(nodeToDisabledMenuItems).length > 0) {
+                               if (event.node.name in nodeToDisabledMenuItems) {
+                                       var nodeName = event.node.name;
+                                       var items = nodeToDisabledMenuItems[nodeName];
+                                       if (items.length === 0) {
+                                               $menuEl.find('li').addClass('disabled');
+                                               $menuEl.find('li > a').unbind('click');
+                                       } else {
+                                               $menuEl.find('li > a').each(function () {
+                                                       $(this).closest('li').removeClass('disabled');
+                                                       var hrefValue = $(this).attr('href');
+                                                       var value = hrefValue.slice(hrefValue.indexOf("#") + 1, hrefValue.length)
+                                                       if ($.inArray(value, items) > -1) {
+                                                               $(this).closest('li').addClass('disabled');
+                                                               $(this).unbind('click');
+                                                       }
+                                               });     
+                                       }
+                               } else {
+                                       $menuEl.find('li.disabled').removeClass('disabled');
+                               }
+                       }
+
+                       // Must call show before we set the offset (offset can not be set on display: none elements).
+                       $menuEl.show();
+
+                       $menuEl.offset({ left: x, top: y });
+
+                       var dismissContextMenu = function () {
+                               $(document).unbind('click.jqtreecontextmenu');
+                               $el.unbind('tree.click.jqtreecontextmenu');
+                               $menuEl.hide();
+                       }
+                       // Make it possible to dismiss context menu by clicking somewhere in the document.
+                       $(document).bind('click.jqtreecontextmenu', function () {
+                               dismissContextMenu();
+                       });
+
+                       // Dismiss context menu if another node in the tree is clicked.
+                       $el.bind('tree.click.jqtreecontextmenu', function (e) {
+                               dismissContextMenu();
+                       });
+
+                       // Make selection follow the node that was right clicked on.
+                       var selectedNode = $el.tree('getSelectedNode');
+                       if (selectedNode !== event.node) {
+                               $el.tree('selectNode', event.node);
+                       }
+
+                       // Handle click on menu items, if it's not disabled.
+                       var menuItems = $menuEl.find('li:not(.disabled) a');
+                       if (menuItems.length !== 0) {
+                               menuItems.unbind('click');
+                               menuItems.click(function (e) {
+                                       e.stopImmediatePropagation();
+                                       dismissContextMenu();
+                                       var hrefAnchor = e.currentTarget.attributes.href.nodeValue;
+                                       var funcKey = hrefAnchor.slice(hrefAnchor.indexOf("#") + 1, hrefAnchor.length)
+                                       var callbackFn = callbacks[funcKey];
+                                       if (callbackFn) {
+                                               callbackFn(event.node);
+                                       }
+                                       return false;
+                               });
+                       }
+               });
+               
+               this.disable = function () {
+                       if (arguments.length === 0) {
+                               // Called as: api.disable()
+                               $menuEl.find('li:not(.disabled)').addClass('disabled');
+                               $menuEl.find('li a').unbind('click');
+                               nodeToDisabledMenuItems = {};
+                       } else if (arguments.length === 1) {
+                               // Called as: api.disable(['edit','remove'])
+                               var items = arguments[0];
+                               if (typeof items !== 'object') {
+                                       return;
+                               }
+                               $menuEl.find('li > a').each(function () {
+                                       var hrefValue = $(this).attr('href');
+                                       var value = hrefValue.slice(hrefValue.indexOf("#") + 1, hrefValue.length)
+                                       if ($.inArray(value, items) > -1) {
+                                               $(this).closest('li').addClass('disabled');
+                                               $(this).unbind('click');
+                                       }
+                               });
+                               nodeToDisabledMenuItems = {};
+                       } else if (arguments.length === 2) {
+                               // Called as: api.disable(nodeName, ['edit','remove'])
+                               var nodeName = arguments[0];
+                               var items = arguments[1];
+                               nodeToDisabledMenuItems[nodeName] = items;
+                       }
+               };
+
+               this.enable = function () {
+                       if (arguments.length === 0) {
+                               // Called as: api.enable()
+                               $menuEl.find('li.disabled').removeClass('disabled');
+                               nodeToDisabledMenuItems = {};
+                       } else if (arguments.length === 1) {
+                               // Called as: api.enable(['edit','remove'])
+                               var items = arguments[0];
+                               if (typeof items !== 'object') {
+                                       return;
+                               }
+                               
+                               $menuEl.find('li > a').each(function () {
+                                       var hrefValue = $(this).attr('href');
+                                       var value = hrefValue.slice(hrefValue.indexOf("#") + 1, hrefValue.length)
+                                       if ($.inArray(value, items) > -1) {
+                                               $(this).closest('li').removeClass('disabled');
+                                       }
+                               });
+
+                               nodeToDisabledMenuItems = {};
+                       } else if (arguments.length === 2) {
+                               // Called as: api.enable(nodeName, ['edit','remove'])
+                               var nodeName = arguments[0];
+                               var items = arguments[1];
+                               if (items.length === 0) {
+                                       delete nodeToDisabledMenuItems[nodeName];
+                               } else {
+                                       var disabledItems = nodeToDisabledMenuItems[nodeName];
+                                       for (var i = 0; i < items.length; i++) {
+                                               var idx = disabledItems.indexOf(items[i]);
+                                               if (idx > -1) {
+                                                       disabledItems.splice(idx, 1);
+                                               }
+                                       }
+                                       if (disabledItems.length === 0) {
+                                               delete nodeToDisabledMenuItems[nodeName];
+                                       } else {
+                                               nodeToDisabledMenuItems[nodeName] = disabledItems;      
+                                       }
+                               }
+                               if (Object.keys(nodeToDisabledMenuItems).length === 0) {
+                                       $menuEl.find('li.disabled').removeClass('disabled');
+                               }
+                       }
+               };
+               return this;
+       };
+} (jQuery));
diff --git a/portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.html b/portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.html
new file mode 100644 (file)
index 0000000..bdd9973
--- /dev/null
@@ -0,0 +1,54 @@
+<!--
+  ============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="modal-header">
+        <h4 class="modal-title">{{title}}</h4>
+        <button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross')">
+            <span aria-hidden="true">&times;</span>
+        </button>
+    </div>
+    <div class="modal-body">
+        <app-search-users [searchTitle]="searchTitleText" [placeHolder]="placeholderText" (passBackSelectedUser)='changeSelectedUser($event)'></app-search-users>
+    </div>
+    <div class="modal-footer">
+        <button type="submit" class="btn btn-primary" [disabled]='!changedSelectedUser'
+            (click)="addNewPortalAdmin(changedSelectedUser)">Save</button> &nbsp;
+        <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-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.scss b/portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.scss
new file mode 100644 (file)
index 0000000..7a77339
--- /dev/null
@@ -0,0 +1,37 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
\ No newline at end of file
diff --git a/portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.spec.ts b/portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.spec.ts
new file mode 100644 (file)
index 0000000..cd85fca
--- /dev/null
@@ -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 { NewPortalAdminComponent } from './new-portal-admin.component';
+
+describe('NewPortalAdminComponent', () => {
+  let component: NewPortalAdminComponent;
+  let fixture: ComponentFixture<NewPortalAdminComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ NewPortalAdminComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(NewPortalAdminComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.ts b/portal-FE-common/src/app/pages/portal-admins/new-portal-admin/new-portal-admin.component.ts
new file mode 100644 (file)
index 0000000..d5cde04
--- /dev/null
@@ -0,0 +1,94 @@
+/*-
+ * ============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, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';
+import { PortalAdmin } from 'src/app/shared/model/PortalAdmin';
+import { PortalAdminsService } from 'src/app/shared/services';
+import { HttpErrorResponse } from '@angular/common/http';
+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-new-portal-admin',
+  templateUrl: './new-portal-admin.component.html',
+  styleUrls: ['./new-portal-admin.component.scss']
+})
+export class NewPortalAdminComponent implements OnInit {
+
+  constructor(public activeModal: NgbActiveModal, private portalAdminsService: PortalAdminsService,
+    public ngbModal: NgbModal) { }
+  @Input() title: string;
+  @Input() id: number;
+  changedSelectedUser: PortalAdmin;
+  closeResult: string;
+  searchTitleText = 'Enter First Name, Last Name or Org User Id';
+  placeholderText = 'Search';
+  @Output() passBackNewPortalAdmin: EventEmitter<any> = new EventEmitter();
+
+  ngOnInit() {
+  }
+
+  changeSelectedUser(user: PortalAdmin) {
+    this.changedSelectedUser = user;
+  }
+
+  addNewPortalAdmin(changedSelectedUser: PortalAdmin) {
+    const modalRef = this.ngbModal.open(InformationModalComponent);
+    modalRef.componentInstance.title = "Admin Update";
+    modalRef.componentInstance.message = `Are you sure you want to add ${changedSelectedUser.firstName} ${changedSelectedUser.lastName} as a Portal Admin?`;
+    modalRef.result.then((result) => {
+      if (result === 'Ok') {
+        this.portalAdminsService.addPortalAdmin(this.changedSelectedUser.orgUserId).subscribe(_data => {
+          this.passBackNewPortalAdmin.emit(_data);
+        }, (_err: HttpErrorResponse) => {
+          this.passBackNewPortalAdmin.emit(_err);
+          const modalErrorRef = this.ngbModal.open(ConfirmationModalComponent);
+          modalErrorRef.componentInstance.title = "Error";
+          if (_err.status === 409) {    //Conflict
+            modalErrorRef.componentInstance.message = "This user already exists as a portal admin!";
+          } else {
+            modalErrorRef.componentInstance.message = "There was a unknown problem adding the portal admin." + "Please try again later. Error Status: " + _err.status;
+          }
+        });
+      }
+    }, (reason) => {
+       return;
+    });
+
+  }
+}
diff --git a/portal-FE-common/src/app/pages/portal-admins/portal-admins.component.html b/portal-FE-common/src/app/pages/portal-admins/portal-admins.component.html
new file mode 100644 (file)
index 0000000..26e59e4
--- /dev/null
@@ -0,0 +1,87 @@
+<!--
+  ============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="onap-main-view-title">
+    <h1 class="heading-page">Portal Admins</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" class="btn btn-primary" (click)="addPortalAdminEntry()"><i class="icon ion-md-person-add"></i>
+    Add Portal Admin</button>
+
+  <span class="onap-spinner" *ngIf="showSpinner"></span>
+  <table mat-table [dataSource]="dataSource" matSort>
+    <!-- First Name Column -->
+    <ng-container matColumnDef="firstName">
+      <th id="col1" mat-header-cell *matHeaderCellDef mat-sort-header> First Name </th>
+      <td id="rowheader_t1_{{i}}-firstName" mat-cell *matCellDef="let element; let i = index;"> {{element.firstName}}
+      </td>
+    </ng-container>
+
+    <!-- Last Name Column -->
+    <ng-container matColumnDef="lastName">
+      <th id="col2" mat-header-cell *matHeaderCellDef mat-sort-header> Last Name </th>
+      <td id="rowheader_t1_{{i}}-lastName" mat-cell *matCellDef="let element; let i=index;"> {{element.lastName}}
+      </td>
+    </ng-container>
+
+    <!-- User ID Column -->
+    <ng-container matColumnDef="loginId">
+      <th id="col3" mat-header-cell *matHeaderCellDef mat-sort-header> User ID </th>
+      <td id="rowheader_t1_{{$index}}-loginId" mat-cell *matCellDef="let element; let i=index;"> {{element.loginId}}
+      </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)="removePortalAdmin(element)">
+          <i class="icon ion-md-trash"></i>
+        </span>
+      </td>
+    </ng-container>
+
+    <tr [hidden]="portalAdmins.length === 0" mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
+  </table>
+  <mat-paginator [hidden]="portalAdmins.length === 0" [pageSizeOptions]="[10, 20]" showFirstLastButtons></mat-paginator>
+</div>
\ No newline at end of file
diff --git a/portal-FE-common/src/app/pages/portal-admins/portal-admins.component.scss b/portal-FE-common/src/app/pages/portal-admins/portal-admins.component.scss
new file mode 100644 (file)
index 0000000..4c65595
--- /dev/null
@@ -0,0 +1,43 @@
+/*-
+ * ============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 '../pages.component';
+
+.icon-trash{
+    cursor: pointer;
+    font-size: 20px;
+}
diff --git a/portal-FE-common/src/app/pages/portal-admins/portal-admins.component.spec.ts b/portal-FE-common/src/app/pages/portal-admins/portal-admins.component.spec.ts
new file mode 100644 (file)
index 0000000..0e3c969
--- /dev/null
@@ -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 { PortalAdminsComponent } from './portal-admins.component';
+
+describe('PortalAdminsComponent', () => {
+  let component: PortalAdminsComponent;
+  let fixture: ComponentFixture<PortalAdminsComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ PortalAdminsComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PortalAdminsComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/pages/portal-admins/portal-admins.component.ts b/portal-FE-common/src/app/pages/portal-admins/portal-admins.component.ts
new file mode 100644 (file)
index 0000000..5dfe026
--- /dev/null
@@ -0,0 +1,124 @@
+/*-
+ * ============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 { OnInit, Component, ViewChild, NgModuleRef } from '@angular/core';
+import { MatSort, MatPaginator } from '@angular/material';
+import { MatTableDataSource } from '@angular/material';
+import { PortalAdminsService } from 'src/app/shared/services';
+import { NgbModal, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';
+import { NewPortalAdminComponent } from './new-portal-admin/new-portal-admin.component';
+import { InformationModalComponent } from 'src/app/modals/information-modal/information-modal.component';
+import { PortalAdmin } from 'src/app/shared/model/PortalAdmin';
+import { HttpErrorResponse } from '@angular/common/http';
+import { ConfirmationModalComponent } from 'src/app/modals/confirmation-modal/confirmation-modal.component';
+
+@Component({
+  selector: 'app-portal-admins',
+  templateUrl: './portal-admins.component.html',
+  styleUrls: ['./portal-admins.component.scss']
+})
+export class PortalAdminsComponent implements OnInit {
+  portalAdmins: PortalAdmin[] = [];
+  showSpinner = true;
+  closeResult: string;
+  constructor(private portalAdminsService: PortalAdminsService, private ngbModal: NgbModal) { }
+  displayedColumns: string[] = ['firstName', 'lastName', 'loginId', 'delete'];
+  dataSource = new MatTableDataSource(this.portalAdmins);
+  @ViewChild(MatSort) sort: MatSort;
+  @ViewChild(MatPaginator) paginator: MatPaginator;
+  ngOnInit() {
+    this.getAllPortalAdmins();
+  }
+
+  getAllPortalAdmins() {
+    this.portalAdminsService.getPortalAdmins().subscribe((_data: PortalAdmin[]) => {
+      this.showSpinner = false;
+      // _data is the array of data that you getting from the db.
+      this.portalAdmins = _data;
+      this.dataSource = new MatTableDataSource(this.portalAdmins);
+      this.dataSource.sort = this.sort;
+      this.dataSource.paginator = this.paginator;
+    }, (_err: HttpErrorResponse) =>{
+      const modalErrorRef = this.ngbModal.open(ConfirmationModalComponent);
+      modalErrorRef.componentInstance.title = "Error";
+      if (_err.status) {    //Conflict
+        modalErrorRef.componentInstance.message = 'Error Status: ' + _err.status + ' There was a unknown problem adding the portal admin.' + 'Please try again later.';
+      }
+    })
+  }
+
+  applyFilter(filterValue: string) {
+    this.dataSource.filter = filterValue.trim().toLowerCase();
+  }
+
+  addPortalAdminEntry() {
+    const modalRef = this.ngbModal.open(NewPortalAdminComponent);
+    modalRef.componentInstance.title = 'Add Portal Admin';
+    modalRef.componentInstance.id = 1;
+    modalRef.componentInstance.passBackNewPortalAdmin.subscribe((_result: any) => {
+      modalRef.close();
+      this.showSpinner = true;
+      this.getAllPortalAdmins();
+    })
+  }
+
+  removePortalAdmin(deletePortalAdmin: any) {
+    const modalRef = this.ngbModal.open(InformationModalComponent);
+    modalRef.componentInstance.title = 'Confirmation';
+    modalRef.componentInstance.message = `Are you sure you want to delete ${deletePortalAdmin.firstName} ${deletePortalAdmin.lastName} ?`;
+    modalRef.result.then((result) => {
+      if (result === 'Ok') {
+        this.portalAdminsService.removePortalAdmin(deletePortalAdmin.userId, deletePortalAdmin.loginId).subscribe(_data => {
+          this.showSpinner = true;
+          this.getAllPortalAdmins();
+        })
+      }
+    }, (reason) => {
+      this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
+    });
+  }
+
+  private getDismissReason(reason: any): string {
+    if (reason === ModalDismissReasons.ESC) {
+      return 'by pressing ESC';
+    } else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
+      return 'by clicking on a backdrop';
+    } else {
+      return `with: ${reason}`;
+    }
+  }
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/helpers/must-match-validator.ts b/portal-FE-common/src/app/shared/helpers/must-match-validator.ts
new file mode 100644 (file)
index 0000000..70e9e1a
--- /dev/null
@@ -0,0 +1,21 @@
+import { FormGroup } from '@angular/forms';
+
+// custom validator to check that two fields match
+export function MustMatch(controlName: string, matchingControlName: string) {
+    return (formGroup: FormGroup) => {
+        const control = formGroup.controls[controlName];
+        const matchingControl = formGroup.controls[matchingControlName];
+
+        if (matchingControl.errors && !matchingControl.errors.mustMatch) {
+            // return if another validator has already found an error on the matchingControl
+            return;
+        }
+
+        // set error on matchingControl if validation fails
+        if (control.value !== matchingControl.value) {
+            matchingControl.setErrors({ mustMatch: true });
+        } else {
+            matchingControl.setErrors(null);
+        }
+    }
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/Admins.ts b/portal-FE-common/src/app/shared/model/Admins.ts
new file mode 100644 (file)
index 0000000..258c42a
--- /dev/null
@@ -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============================================
+ *
+ * 
+ */
+export class Admins {
+    firstName: string;
+    lastName: string;
+    user_Id: number;
+    orgUserId: string;
+    apps: Apps[];
+}
+
+export class Apps {
+    appId: number;
+    appName: string;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/AllApps.ts b/portal-FE-common/src/app/shared/model/AllApps.ts
new file mode 100644 (file)
index 0000000..fcf9f7b
--- /dev/null
@@ -0,0 +1,44 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
+export class AllApps {
+    enabled: boolean;
+    index: number;
+    restrictedApp: boolean;
+    title: string;
+    value: string;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/PortalAdmin.ts b/portal-FE-common/src/app/shared/model/PortalAdmin.ts
new file mode 100644 (file)
index 0000000..f826d45
--- /dev/null
@@ -0,0 +1,44 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
+export class PortalAdmin {
+    firstName: string;
+    lastName: string;
+    userId: number;
+    loginId: string;
+    orgUserId: string;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/Role.ts b/portal-FE-common/src/app/shared/model/Role.ts
new file mode 100644 (file)
index 0000000..5badf9e
--- /dev/null
@@ -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============================================
+ *
+ * 
+ */
+import { RoleFunction } from './RoleFunction';
+
+export class Role {
+    name: string;
+    priority: any;
+    childRoles: ChildRoles[];
+    roleFunctions: RoleFunction[];
+}
+
+export class ChildRoles {
+    name: string;
+    priority: any;
+}
+
diff --git a/portal-FE-common/src/app/shared/model/RoleFunction.ts b/portal-FE-common/src/app/shared/model/RoleFunction.ts
new file mode 100644 (file)
index 0000000..68f055c
--- /dev/null
@@ -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============================================
+ *
+ * 
+ */
+export class RoleFunction {
+    type: string;
+    code: string;
+    action: string;
+    name: string;
+
+    //constructor 
+    constructor(type: string, code: string, action: string, name: string) {
+        this.type = type;
+        this.code = code;
+        this.action = action;
+        this.name = name;
+    }
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/UserAccessRoles.ts b/portal-FE-common/src/app/shared/model/UserAccessRoles.ts
new file mode 100644 (file)
index 0000000..ed18088
--- /dev/null
@@ -0,0 +1,44 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
+export class UserAccessRoles {
+    isApplied: boolean;
+    roleId: number;
+    roleName: string;
+    appId: number;
+    appName: string;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/UserAdminApps.ts b/portal-FE-common/src/app/shared/model/UserAdminApps.ts
new file mode 100644 (file)
index 0000000..1617bea
--- /dev/null
@@ -0,0 +1,42 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
+export class UserAdminApps {
+    id: number;
+    name: string;
+    restrictedApp: string;
+}   
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/application-catalog.model.ts b/portal-FE-common/src/app/shared/model/application-catalog.model.ts
new file mode 100644 (file)
index 0000000..a0f368c
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * 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============================================
+ *
+ * 
+ */
+export interface IApplicationCatalog {
+    id: number;
+    name: string;
+    mlAppName: string;
+    imageUrl: string;
+    url: string;
+    restricted: boolean;
+    open: boolean;
+    access: boolean;
+    select: boolean;
+    pending: boolean;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/applications-onboarding/applications.ts b/portal-FE-common/src/app/shared/model/applications-onboarding/applications.ts
new file mode 100644 (file)
index 0000000..a0a93a2
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2017 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============================================
+ *
+ * 
+ */
+
+export interface IApplications {
+    id ?: any;
+    name ?: any;
+    imageUrl ?: any;
+    imageLink ?: any;
+    description ?: any;
+    notes ?: any;
+    url ?: any
+    alternateUrl ?: any;
+    restUrl ?: any;
+    isOpen ?: any;
+    isEnabled ?: any;
+    motsId ?: any;
+    myLoginsAppName ?: any;
+    myLoginsAppOwner ?: any;
+    username ?: any;
+    appPassword ?: any;
+    thumbnail ?: any;
+    uebTopicName ?: any;
+    uebKey ?: any;
+    uebSecret ?: any;
+    restrictedApp ?: any;
+    isCentralAuth ?: any;
+    nameSpace ?: any
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/dynamic-component-manifest/dynamic-component-manifest.ts b/portal-FE-common/src/app/shared/model/dynamic-component-manifest/dynamic-component-manifest.ts
new file mode 100644 (file)
index 0000000..176e16e
--- /dev/null
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2017 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============================================
+ *
+ * 
+ */
+
+export interface IDynamicComponentManifest {
+    componentId: string;
+  path: string;
+  loadChildren: string;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/global-search-item.model.ts b/portal-FE-common/src/app/shared/model/global-search-item.model.ts
new file mode 100644 (file)
index 0000000..626a8d2
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * 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============================================
+ *
+ * 
+ */
+export interface GlobalSearchItem {
+    rowId: number;
+    category: string;
+    name: string;
+    target: string;
+    uuid: boolean;
+    
+}
diff --git a/portal-FE-common/src/app/shared/model/index.ts b/portal-FE-common/src/app/shared/model/index.ts
new file mode 100644 (file)
index 0000000..f8572b8
--- /dev/null
@@ -0,0 +1,44 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
+export * from './Admins';
+export * from './AllApps'
+export * from './PortalAdmin';
+export * from './UserAdminApps';
+export * from './UserAccessRoles';
+export * from './Role';
+export * from './RoleFunction';
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/widget-catalog.model.ts b/portal-FE-common/src/app/shared/model/widget-catalog.model.ts
new file mode 100644 (file)
index 0000000..f30a4e3
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * ============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============================================
+ *
+ * 
+ */
+export interface IWidgetCatalog {
+    widgetId: string;
+    widgetName: String;
+    widgetStatus: string;
+    imageLink: string;
+    select: boolean;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/model/widget-onboarding/widget.ts b/portal-FE-common/src/app/shared/model/widget-onboarding/widget.ts
new file mode 100644 (file)
index 0000000..ba1842f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * ============LICENSE_START==========================================
+ * ONAP Portal SDK
+ * ===================================================================
+ * Copyright Â© 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified?: any; all software contained herein is licensed
+ * under the Apache License?: any; 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?: any; software
+ * distributed under the License is distributed on an "AS IS" BASIS?: any;
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND?: any; either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Unless otherwise specified?: any; all documentation contained herein is licensed
+ * under the Creative Commons License?: any; 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?: any; documentation
+ * distributed under the License is distributed on an "AS IS" BASIS?: any;
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND?: any; either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ============LICENSE_END============================================
+ *
+ * 
+ */
+export interface IWidget {
+    id ?: any;
+    name ?: any;
+    desc ?: any;
+    fileLocation ?: any;
+    allowAllUser ?: any;
+    serviceId ?: any;
+    serviceURL ?: any;
+    sortOrder ?: any;
+    statusCode ?: any;
+    widgetRoles ?: any;
+    appContent ?: any;
+    appName ?: any
+    file  ?: any;
+    allUser ?: boolean;
+    saving ?: any
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/pipes/application-pipes.module.ts b/portal-FE-common/src/app/shared/pipes/application-pipes.module.ts
new file mode 100644 (file)
index 0000000..3e19fa2
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * ============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 { ElipsisPipe } from "./elipsis/elipsis.pipe";
+import { NgModule } from "@angular/core";
+
+// application-pipes.module.ts
+// other imports
+
+
+@NgModule({
+  imports: [
+    // dep modules
+  ],
+  declarations: [ 
+    ElipsisPipe
+  ],
+  exports: [
+    ElipsisPipe
+  ]
+})
+export class ApplicationPipesModule {}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/pipes/elipsis/elipsis.pipe.spec.ts b/portal-FE-common/src/app/shared/pipes/elipsis/elipsis.pipe.spec.ts
new file mode 100644 (file)
index 0000000..22a477d
--- /dev/null
@@ -0,0 +1,45 @@
+/*-
+ * ============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 { ElipsisPipe } from './elipsis.pipe';
+
+describe('ElipsisPipe', () => {
+  it('create an instance', () => {
+    const pipe = new ElipsisPipe();
+    expect(pipe).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/shared/pipes/elipsis/elipsis.pipe.ts b/portal-FE-common/src/app/shared/pipes/elipsis/elipsis.pipe.ts
new file mode 100644 (file)
index 0000000..d749626
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * ============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 { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+  name: 'elipsis'
+})
+export class ElipsisPipe implements PipeTransform {
+
+  transform(value: any, args: string): any {
+    if (!value) {
+      return value;
+    }
+    
+    var noOfChar = parseInt(args);
+
+    if (!noOfChar || value.length <= noOfChar) {
+      return value;
+    }
+
+    value = value.substr(0, noOfChar);
+
+    return value + '...';
+  }
+
+}
diff --git a/portal-FE-common/src/app/shared/plugin/dynamic-widget/dynamic-widget.module.ts b/portal-FE-common/src/app/shared/plugin/dynamic-widget/dynamic-widget.module.ts
new file mode 100644 (file)
index 0000000..baab502
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * ============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 { ListWidgetComponent } from './list-widget/list-widget.component';
+import { HttpClientModule } from '@angular/common/http';
+import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
+
+@NgModule({
+  declarations: [ListWidgetComponent],
+  imports: [
+    CommonModule,
+    HttpClientModule
+  ],
+  exports: [ListWidgetComponent],
+  providers: [HttpClient],
+  entryComponents: [ListWidgetComponent]
+})
+export class DynamicWidgetModule {
+  static entry = ListWidgetComponent;
+}
diff --git a/portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.html b/portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.html
new file mode 100644 (file)
index 0000000..82f4f35
--- /dev/null
@@ -0,0 +1,58 @@
+<!--
+  ============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 id="widget-news" class="widget-news-main">
+  <div onap-gridster-item-body class="information-section-gridsterContent">
+    <div class="resources">
+      <ul *ngIf="newsData && newsData.length!=0">
+        <li *ngFor="let item of newsData"><a href="{{item.href}}" target="_blank">{{item.title}}</a></li>
+      </ul>
+      <div *ngIf="newsData && newsData.length!=0">
+        <div class="activity-error-container"
+          style="background: rgb(255, 255, 255); overflow: hidden !important; width: 100%;">
+          <div class="activity-error-block">
+            <i class="icon-information full-linear-icon-information" style="margin-left: 125px; font-size: 90px"></i>
+            <br>
+            <div class="activity-error-msg1">There's currently no
+              news available.</div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.scss b/portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.scss
new file mode 100644 (file)
index 0000000..7a77339
--- /dev/null
@@ -0,0 +1,37 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.spec.ts b/portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.spec.ts
new file mode 100644 (file)
index 0000000..d799193
--- /dev/null
@@ -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 { ListWidgetComponent } from './list-widget.component';
+
+describe('ListWidgetComponent', () => {
+  let component: ListWidgetComponent;
+  let fixture: ComponentFixture<ListWidgetComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ ListWidgetComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ListWidgetComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.ts b/portal-FE-common/src/app/shared/plugin/dynamic-widget/list-widget/list-widget.component.ts
new file mode 100644 (file)
index 0000000..1d28026
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * ============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';
+import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
+import { Observable } from 'rxjs';
+
+@Component({
+  selector: 'app-list-widget',
+  templateUrl: './list-widget.component.html',
+  styleUrls: ['./list-widget.component.scss']
+})
+export class ListWidgetComponent implements OnInit {
+  newsData: any[];
+
+  constructor(private api: HttpClient) { }
+
+  ngOnInit() {
+    this.getNewsWidgetCatalog();
+  }
+
+  getNewsWidgetCatalog() {
+    //console.log("getNewsWidgetCatalog called");
+  }
+
+}
diff --git a/portal-FE-common/src/app/shared/plugin/plugin-loader/client-plugin-loader.service.ts b/portal-FE-common/src/app/shared/plugin/plugin-loader/client-plugin-loader.service.ts
new file mode 100644 (file)
index 0000000..2666a52
--- /dev/null
@@ -0,0 +1,86 @@
+/*-
+ * ============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 { Injectable, NgModuleFactory } from '@angular/core';
+import { PluginLoaderService } from './plugin-loader.service';
+import { PLUGIN_EXTERNALS_MAP } from './plugin-externals';
+import { PluginsConfigProvider } from '../plugins-config.provider';
+
+const SystemJs = window.System;
+
+@Injectable({
+  providedIn: 'root',
+})
+export class ClientPluginLoaderService extends PluginLoaderService {
+  constructor(private configProvider: PluginsConfigProvider) {
+    super();
+    configProvider.loadConfig()
+    .toPromise()
+    .then(config => {
+      configProvider.config = config;
+      console.log(config);
+    });
+  }
+
+  provideExternals() {
+    Object.keys(PLUGIN_EXTERNALS_MAP).forEach(externalKey =>
+      window.define(externalKey, [], () => PLUGIN_EXTERNALS_MAP[externalKey])
+    );
+  }
+
+  load<T>(pluginName): Promise<NgModuleFactory<T>> {
+
+    const { config } = this.configProvider;
+    if (!config[pluginName]) {
+      throw Error(`Can't find appropriate plugin`);
+    }
+
+    const depsPromises = (config[pluginName].deps || []).map(dep => {
+      return SystemJs.import(window['base'] + config[dep].path).then(m => {
+        window['define'](dep, [], () => m.default);
+      });
+    });
+
+    return Promise.all(depsPromises).then(() => {
+
+      return SystemJs.import(window['base'] + config[pluginName].path).then(
+        module => module.default.default
+      );
+    });
+  }
+}
diff --git a/portal-FE-common/src/app/shared/plugin/plugin-loader/plugin-externals.ts b/portal-FE-common/src/app/shared/plugin/plugin-loader/plugin-externals.ts
new file mode 100644 (file)
index 0000000..3bd5a13
--- /dev/null
@@ -0,0 +1,53 @@
+/*-
+ * ============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 * as core from '@angular/core';
+import * as common from '@angular/common';
+import * as forms from '@angular/forms';
+import * as router from '@angular/router';
+import * as rxjs from 'rxjs';
+import * as tslib from 'tslib';
+
+export const PLUGIN_EXTERNALS_MAP = {
+  'ng.core': core,
+  'ng.common': common,
+  'ng.forms': forms,
+  'ng.router': router,
+  rxjs,
+  tslib
+};
diff --git a/portal-FE-common/src/app/shared/plugin/plugin-loader/plugin-loader.service.ts b/portal-FE-common/src/app/shared/plugin/plugin-loader/plugin-loader.service.ts
new file mode 100644 (file)
index 0000000..1d79b65
--- /dev/null
@@ -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============================================
+ *
+ * 
+ */
+import { NgModuleFactory } from '@angular/core';
+
+export abstract class PluginLoaderService {
+  protected constructor() {
+    this.provideExternals();
+  }
+
+  abstract provideExternals(): void;
+
+  abstract load<T>(pluginName): Promise<NgModuleFactory<T>>;
+}
diff --git a/portal-FE-common/src/app/shared/plugin/plugin.component.html b/portal-FE-common/src/app/shared/plugin/plugin.component.html
new file mode 100644 (file)
index 0000000..e2aeaf1
--- /dev/null
@@ -0,0 +1,48 @@
+<!--
+  ============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============================================
+  
+  -->
+  
+<p> plugin works</p>
+<button (click)="loadPlugin('listWidgetPlugin')">Load News</button>
+<div>
+  <div class="plugins">
+    <ng-template #targetRef></ng-template>
+  </div>
+</div>
+<!--
+<app-list-widget></app-list-widget>
+-->
diff --git a/portal-FE-common/src/app/shared/plugin/plugin.component.scss b/portal-FE-common/src/app/shared/plugin/plugin.component.scss
new file mode 100644 (file)
index 0000000..fa57ecb
--- /dev/null
@@ -0,0 +1,38 @@
+/*-
+ * ============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============================================
+ *
+ * 
+ */
\ No newline at end of file
diff --git a/portal-FE-common/src/app/shared/plugin/plugin.component.spec.ts b/portal-FE-common/src/app/shared/plugin/plugin.component.spec.ts
new file mode 100644 (file)
index 0000000..cc5810c
--- /dev/null
@@ -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 { PluginComponent } from './plugin.component';
+
+describe('PluginComponent', () => {
+  let component: PluginComponent;
+  let fixture: ComponentFixture<PluginComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ PluginComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PluginComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/shared/plugin/plugin.component.ts b/portal-FE-common/src/app/shared/plugin/plugin.component.ts
new file mode 100644 (file)
index 0000000..b64ea7c
--- /dev/null
@@ -0,0 +1,75 @@
+/*-
+ * ============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,
+  Injector,
+  OnInit,
+  ViewChild,
+  ViewContainerRef
+} from '@angular/core';
+import { PluginLoaderService } from './plugin-loader/plugin-loader.service';
+
+@Component({
+  selector: 'app-plugin',
+  templateUrl: './plugin.component.html',
+  styleUrls: ['./plugin.component.scss']
+})
+export class PluginComponent implements OnInit {
+  @ViewChild('targetRef', { read: ViewContainerRef }) vcRef: ViewContainerRef;
+
+  constructor(
+    private injector: Injector,
+    private pluginLoader: PluginLoaderService
+  ) {}
+
+  ngOnInit() {
+    //this.loadPlugin('plugin1');
+  }
+
+  loadPlugin(pluginName: string) {
+    this.pluginLoader.load(pluginName).then(moduleFactory => {
+      const moduleRef = moduleFactory.create(this.injector);
+      const entryComponent = (moduleFactory.moduleType as any).entry;
+      const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(
+        entryComponent
+      );
+      this.vcRef.createComponent(compFactory);
+    });
+  }
+}
diff --git a/portal-FE-common/src/app/shared/plugin/plugin.module.ts b/portal-FE-common/src/app/shared/plugin/plugin.module.ts
new file mode 100644 (file)
index 0000000..de36662
--- /dev/null
@@ -0,0 +1,89 @@
+/*-
+ * ============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 {
+  BrowserModule,
+  BrowserTransferStateModule, TransferState
+} from '@angular/platform-browser';
+import { APP_INITIALIZER, NgModule, APP_BOOTSTRAP_LISTENER } from '@angular/core';
+import { HttpClientModule } from '@angular/common/http';
+import { DynamicWidgetModule } from './dynamic-widget/dynamic-widget.module';
+
+
+
+import { ClientPluginLoaderService } from './plugin-loader/client-plugin-loader.service';
+import { PluginsConfigProvider } from './plugins-config.provider';
+import { TransferStateService } from './transfer-state.service';
+import { PluginLoaderService } from './plugin-loader/plugin-loader.service';
+import { PluginComponent } from './plugin.component';
+import { ListWidgetComponent } from './dynamic-widget/list-widget/list-widget.component';
+
+import { config } from 'rxjs';
+
+@NgModule({
+  declarations: [PluginComponent],
+  imports: [
+    HttpClientModule,
+    //BrowserModule.withServerTransition({ appId: 'serverApp' }),
+    BrowserTransferStateModule
+  ],
+  providers: [
+    { provide: PluginLoaderService, useClass: ClientPluginLoaderService },
+    //PluginsConfigProvider,
+    TransferStateService,
+    {
+      provide: APP_BOOTSTRAP_LISTENER,
+      useFactory: (provider: PluginsConfigProvider) => () =>
+        provider
+          .loadConfig()
+          .toPromise()
+          .then(config => {
+            provider.config = config;
+            console.log(config);
+          }
+          ),
+      multi: true,
+      deps: [PluginsConfigProvider]
+    }
+  ],
+  bootstrap: [PluginComponent],
+  exports: [PluginComponent]
+})
+export class PluginModule {
+  constructor(transferStateService: TransferStateService) {}
+}
diff --git a/portal-FE-common/src/app/shared/plugin/plugins-config.provider.ts b/portal-FE-common/src/app/shared/plugin/plugins-config.provider.ts
new file mode 100644 (file)
index 0000000..8c2a2e7
--- /dev/null
@@ -0,0 +1,75 @@
+/*-
+ * ============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 { Inject, Injectable, Optional, PLATFORM_ID } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { tap } from 'rxjs/operators';
+import { preserveServerState } from './transfer-state.service';
+import { isPlatformBrowser } from '@angular/common';
+
+interface PluginsConfig {
+  [key: string]: {
+    name: string;
+    path: string;
+    deps: string[];
+  };
+}
+
+@Injectable({
+  providedIn: 'root',
+})
+export class PluginsConfigProvider {
+  config: PluginsConfig;
+
+  constructor(
+    private http: HttpClient,
+    @Inject(PLATFORM_ID) private platformId: {},
+    @Inject('APP_BASE_URL') @Optional() private readonly baseUrl: string
+  ) {
+    if (isPlatformBrowser(platformId)) {
+      this.baseUrl = document.location.origin;
+    }
+  }
+
+  @preserveServerState('PLUGIN_CONFIGS')
+  loadConfig() {
+    return this.http.get<PluginsConfig>(
+      `assets/plugins-config.json`
+    );
+  }
+}
diff --git a/portal-FE-common/src/app/shared/plugin/transfer-state.service.ts b/portal-FE-common/src/app/shared/plugin/transfer-state.service.ts
new file mode 100644 (file)
index 0000000..e42abcb
--- /dev/null
@@ -0,0 +1,85 @@
+/*-
+ * ============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 { Inject, Injectable, PLATFORM_ID } from '@angular/core';
+import { makeStateKey, TransferState } from '@angular/platform-browser';
+import { isPlatformBrowser, isPlatformServer } from '@angular/common';
+import { of } from 'rxjs';
+import { tap } from 'rxjs/operators';
+
+let isBrowser: boolean;
+let isServer: boolean;
+let transferState: TransferState;
+
+@Injectable({
+  providedIn: 'root'
+})
+export class TransferStateService {
+  constructor(
+    private state: TransferState,
+    @Inject(PLATFORM_ID) private platformId: any
+  ) {
+    transferState = state;
+    isBrowser = isPlatformBrowser(this.platformId);
+    isServer = isPlatformServer(this.platformId);
+  }
+}
+
+export const preserveServerState = (
+  keyName: string,
+  emptyResult: any = null
+) => {
+  const key = makeStateKey(keyName);
+  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
+    const method = descriptor.value;
+    descriptor.value = function() {
+      if (isBrowser && transferState.hasKey(key)) {
+        const state = transferState.get(key, emptyResult);
+        return of(state);
+      }
+
+      return method.apply(this, arguments).pipe(
+        tap(res => {
+         // if (isServer) {
+            transferState.set(key, res);
+        //  }
+        })
+      );
+    };
+  };
+};