diff --git a/build_and_publish.sh b/build_and_publish.sh index eb401bf5ec2eccc3e7b6662453c61826cc8cfc34..0498bf89eaf44c545eb12f15c192b99f8ef8b7b4 100644 --- a/build_and_publish.sh +++ b/build_and_publish.sh @@ -1,6 +1,6 @@ #!/bin/bash -TAG=1.5.3 +TAG=1.6.0 PACKAGE=nmaas-portal REPOSITORY=artifactory.geant.net/nmaas-docker-local sudo docker build --rm -t $REPOSITORY/$PACKAGE:$TAG -f ./Dockerfile .. diff --git a/src/app/appmarket/appinstance/appinstance.module.ts b/src/app/appmarket/appinstance/appinstance.module.ts index b391deba1930bbd072c930b2648459f3852b3776..9f42e399328249b9239b78d65d1a8630aee5c12b 100644 --- a/src/app/appmarket/appinstance/appinstance.module.ts +++ b/src/app/appmarket/appinstance/appinstance.module.ts @@ -30,6 +30,10 @@ import { SelectPodModalComponent } from './modals/select-pod-modal/select-pod-mo import {TooltipModule} from 'primeng/tooltip'; import {AppinstanceSearchPipe} from './appinstance-search.pipe'; import {InputTextModule} from 'primeng/inputtext'; +import { AppdeploymentComponent } from '../bulkDeployment/appDeployment/appchoose/appdeployment.component'; +import {DropdownModule} from 'primeng/dropdown'; +import {TimelineModule} from 'primeng/timeline'; +import {ButtonModule} from 'primeng/button'; @NgModule({ declarations: [ @@ -43,7 +47,9 @@ import {InputTextModule} from 'primeng/inputtext'; AppInstanceShellViewComponent, AddMembersModalComponent, SelectPodModalComponent, - AppinstanceSearchPipe + AppinstanceSearchPipe, + SelectPodModalComponent, + AppdeploymentComponent ], imports: [ FormioModule, @@ -60,6 +66,9 @@ import {InputTextModule} from 'primeng/inputtext'; NgTerminalModule, MultiSelectModule, TooltipModule, + DropdownModule, + TimelineModule, + ButtonModule, InputTextModule, ], exports: [ diff --git a/src/app/appmarket/appinstance/appinstance.routes.ts b/src/app/appmarket/appinstance/appinstance.routes.ts index fc336ea50ec45d27433a3c12cfe375517662e200..48e4caa6e9c2c416ac95c83aef76931a9cc85d05 100644 --- a/src/app/appmarket/appinstance/appinstance.routes.ts +++ b/src/app/appmarket/appinstance/appinstance.routes.ts @@ -2,9 +2,19 @@ import { Route } from '@angular/router'; import { AppInstanceComponent } from './index'; import { AppInstanceListComponent } from './index'; import {AppInstanceShellViewComponent} from './appinstance-shell-view/appinstance-shell-view.component'; +import {AppdeploymentComponent} from '../bulkDeployment/appDeployment/appchoose/appdeployment.component'; +import {AppnavigatorComponent} from '../bulkDeployment/appDeployment/appnavigator/appnavigator.component'; +import {AppuploadComponent} from '../bulkDeployment/appDeployment/appupload/appupload.component'; +import {AppsummaryComponent} from '../bulkDeployment/appDeployment/appsummary/appsummary.component'; export const AppInstanceRoutes: Route[] = [ { path: 'instances', component: AppInstanceListComponent }, + { path: 'instances/deploy', component: AppnavigatorComponent, children: [ + {path: '', redirectTo: 'select', pathMatch: 'full'}, + {path: 'select', component: AppdeploymentComponent}, + {path: 'upload', component: AppuploadComponent}, + {path: 'summary', component: AppsummaryComponent} + ]}, { path: 'instances/:id', component: AppInstanceComponent }, - { path: 'instances/:id/shell/:podname', component: AppInstanceShellViewComponent } + { path: 'instances/:id/shell/:podname', component: AppInstanceShellViewComponent }, ]; diff --git a/src/app/appmarket/appinstance/appinstance/appinstance.component.spec.ts b/src/app/appmarket/appinstance/appinstance/appinstance.component.spec.ts index 778691010a0020f4cfb8d4d752cc93776bbbdbf1..b7156315a705e1ca1a935a1881c85da499113385 100644 --- a/src/app/appmarket/appinstance/appinstance/appinstance.component.spec.ts +++ b/src/app/appmarket/appinstance/appinstance/appinstance.component.spec.ts @@ -159,8 +159,10 @@ describe('Component: AppInstance', () => { name: 'domain 1', codename: 'dom1', active: true, + deleted: false, domainDcnDetails: null, domainTechDetails: null, + groups: [], applicationStatePerDomain: [ { applicationBaseId: 2, diff --git a/src/app/appmarket/appmanagement/app-management.routes.ts b/src/app/appmarket/appmanagement/app-management.routes.ts index f98c02997f72336b288c138de0ed6b00f72d33b4..243c1eb00971f1f5099962b2e1a3c50720776e37 100644 --- a/src/app/appmarket/appmanagement/app-management.routes.ts +++ b/src/app/appmarket/appmanagement/app-management.routes.ts @@ -6,6 +6,8 @@ import {AppCreateWizardComponent} from './app-create-wizard/app-create-wizard.co import {ComponentMode} from '../../shared'; import {AppPreviewComponent} from './app-preview/apppreview.component'; import {AppVersionCreateWizardComponent} from './app-version-create-wizard/app-version-create-wizard.component'; +import {BulkAppListComponent} from '../bulkDeployment/bulk-app-list/bulk-app-list.component'; +import {BulkViewComponent} from '../bulkDeployment/bulk-view/bulk-view.component'; export const AppManagementRoutes: Route[] = [ { @@ -43,5 +45,17 @@ export const AppManagementRoutes: Route[] = [ component: AppPreviewComponent, canActivate: [AuthGuard, RoleGuard], data: {roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_TOOL_MANAGER']} + }, + { + path: 'admin/apps/bulks', + component: BulkAppListComponent, + canActivate: [AuthGuard, RoleGuard], + data: {roles: ['ROLE_SYSTEM_ADMIN']} + }, + { + path: 'admin/apps/bulks/:id', + component: BulkViewComponent, + canActivate: [AuthGuard, RoleGuard], + data: {roles: ['ROLE_SYSTEM_ADMIN' ]} } ]; diff --git a/src/app/appmarket/appmarket.module.ts b/src/app/appmarket/appmarket.module.ts index 96a3209347d0dd8590c4aa61984a88556965e025..b0613a88d8ad3a7c0159cc5e6e838b52a7eb1797 100644 --- a/src/app/appmarket/appmarket.module.ts +++ b/src/app/appmarket/appmarket.module.ts @@ -31,35 +31,63 @@ import {SessionService} from '../service/session.service'; import {LanguageManagementModule} from './admin/languagemanagement/languagemanagement.module'; import { ModalGuestUserComponent } from './modals/modal-guest-user/modal-guest-user.component'; import {TooltipModule} from 'primeng/tooltip'; +import { AppnavigatorComponent } from './bulkDeployment/appDeployment/appnavigator/appnavigator.component'; +import {AvatarModule} from 'primeng/avatar'; +import {StepsModule} from 'primeng/steps'; +import { AppuploadComponent } from './bulkDeployment/appDeployment/appupload/appupload.component'; +import {FileUploadModule} from 'primeng/fileupload'; +import { AppsummaryComponent } from './bulkDeployment/appDeployment/appsummary/appsummary.component'; +import {TableModule} from 'primeng/table'; +import { DomainNavigatorComponent } from './bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component'; +import { DomainuploadComponent } from './bulkDeployment/domainDeployment/domainupload/domainupload.component'; +import { DomainsummaryComponent } from './bulkDeployment/domainDeployment/domainsummary/domainsummary.component'; +import { BulkDomainListComponent } from './bulkDeployment/bulk-domain-list/bulk-domain-list.component'; +import { BulkViewComponent } from './bulkDeployment/bulk-view/bulk-view.component'; +import { BulkAppListComponent } from './bulkDeployment/bulk-app-list/bulk-app-list.component'; +import { BulkListComponent } from './bulkDeployment/bulk-list/bulk-list.component'; @NgModule({ declarations: [ AppMarketComponent, AppDetailsComponent, ModalGuestUserComponent, + AppnavigatorComponent, + AppuploadComponent, + AppsummaryComponent, + DomainNavigatorComponent, + DomainuploadComponent, + DomainsummaryComponent, + BulkDomainListComponent, + BulkViewComponent, + BulkAppListComponent, + BulkListComponent, ], - imports: [ - FormsModule, - StorageServiceModule, - CommonModule, - RouterModule, - SharedModule, - AppListModule, - AppInstanceModule, - AppManagementModule, - LanguageManagementModule, - DomainsModule, - UsersModule, - AuthModule, - PipesModule, - ClustersModule, - MonitorModule, - ConfigurationModule, - BrowserModule, - HttpClientModule, - TranslateModule.forChild(), - TooltipModule, - ], + imports: [ + FormsModule, + StorageServiceModule, + CommonModule, + RouterModule, + SharedModule, + AppListModule, + AppInstanceModule, + AppManagementModule, + LanguageManagementModule, + DomainsModule, + UsersModule, + AuthModule, + PipesModule, + ClustersModule, + MonitorModule, + ConfigurationModule, + BrowserModule, + HttpClientModule, + TranslateModule.forChild(), + TooltipModule, + AvatarModule, + StepsModule, + FileUploadModule, + TableModule, + ], exports: [ AppMarketComponent, ], diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.css b/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.css new file mode 100644 index 0000000000000000000000000000000000000000..c0ad076212826fc5ce67c6fed4eeb2aa653b0b9d --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.css @@ -0,0 +1,3 @@ +:host ::ng-deep .p-dropdown { + width: 300px; +} diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.html b/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.html new file mode 100644 index 0000000000000000000000000000000000000000..7c6bbba996d1e5b4ac0a9495f8612a36ff4b7a31 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.html @@ -0,0 +1,16 @@ +<div class="col-sm-12 col-sm-12 col-md-12"> + + <div class="row" style="margin-top: 5rem;"> + <h2>{{'BULK.APP.HEADER' | translate}}</h2> + </div> + <div class="row" style="margin-top: 3rem;"> + <h4>{{'BULK.APP.INFO' | translate}}</h4> + </div> + <div style="margin-top: 3rem;"> + <p-dropdown [options]="apps" optionLabel="name" [(ngModel)]="selectedApp"></p-dropdown> + </div> + <div class="row .navbar-right" style="margin-top: 3rem; display: flex; justify-content: right"> + <button pButton class="p-button-success" (click)="selectApp()"> {{'BULK.LIST.CONTINUE' | translate}}</button> + </div> + +</div> diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.spec.ts b/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4f931009df30216f2482d477b69754e10881c567 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.spec.ts @@ -0,0 +1,38 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AppdeploymentComponent } from './appdeployment.component'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {RouterModule} from '@angular/router'; +import {TranslateFakeLoader, TranslateLoader, TranslateModule} from '@ngx-translate/core'; + +describe('AppdeploymentComponent', () => { + let component: AppdeploymentComponent; + let fixture: ComponentFixture<AppdeploymentComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AppdeploymentComponent ], + imports : [ + HttpClientTestingModule, + RouterModule.forRoot([]), + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader + } + }), + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AppdeploymentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.ts b/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b721f3fc0cfda0e28a054553e26409ad103ef9b --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appchoose/appdeployment.component.ts @@ -0,0 +1,30 @@ +import { Component, OnInit } from '@angular/core'; +import {ApplicationBase} from '../../../../model/application-base'; +import {AppsService} from '../../../../service'; +import {AppdeploymentService} from '../../appdeployment.service'; +import {Router} from '@angular/router'; + +@Component({ + selector: 'app-appchoose', + templateUrl: './appdeployment.component.html', + styleUrls: ['./appdeployment.component.css'] +}) +export class AppdeploymentComponent implements OnInit { + + public apps: ApplicationBase[] = []; + + public selectedApp: ApplicationBase = null; + + constructor(private readonly appService: AppsService, + private readonly deployService: AppdeploymentService, + private router: Router) { } + + ngOnInit(): void { + this.appService.getAllActiveApplicationBase().subscribe(data => this.apps = data); + } + + selectApp() { + this.deployService.setSelectedApp(this.selectedApp); + this.router.navigate(['instances/deploy/upload']) + } +} diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.css b/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.css new file mode 100644 index 0000000000000000000000000000000000000000..b9df647110cbde21b3dccabf17e62bd8db6813e4 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.css @@ -0,0 +1,76 @@ +:host ::ng-deep { +.p-steps .p-steps-item .p-menuitem-link { + background: transparent; + transition: box-shadow 0.2s; + border-radius: 3px; +} +.p-steps .p-steps-item .p-menuitem-link .p-steps-number { + color: #495057; + border: 1px solid #e9ecef; + background: #ffffff; + min-width: 2rem; + height: 2rem; + line-height: 4rem; + font-size: 1.143rem; + z-index: 1; + border-radius: 50%; +} +.p-steps .p-steps-item .p-menuitem-link .p-steps-title { + margin-top: 0.5rem; + color: #6c757d; +} +.p-steps .p-steps-item .p-menuitem-link:not(.p-disabled):focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem #a6d5fa; +} +.p-steps .p-steps-item.p-highlight .p-steps-number { + background: #E3F2FD; + color: #495057; +} +.p-steps .p-steps-item.p-highlight .p-steps-title { + font-weight: 600; + color: #495057; +} +.p-steps .p-steps-item:before { + content: " "; + border-top: 1px solid #dee2e6; + width: 100%; + top: 50%; + left: 0; + display: block; + position: absolute; + margin-top: -1rem; +} +} + +.line-1 { + height: 10px; + background: #428bca; + width: 100% !important; + margin-top: 19px; +} + +.line-2 { + height: 10px; + background: white; + width: 100% !important; + margin-top: 19px; +} + +.step-active { + background-color: #428bca; + color: white; +} + +.step-done { + background-color: #428bca; + color: white; +} + +.step-next { + background-color: white; + color: #428bca; +} + + diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.html b/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.html new file mode 100644 index 0000000000000000000000000000000000000000..fe01e376c3564493c32da07855b8356f5dbc185a --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.html @@ -0,0 +1,9 @@ +<div class="col-12 flex align-items-center flex-column" style=";min-height: 100% !important;" > + <p-steps [model]="items" [readonly]="true" [activeIndex]="status"></p-steps> + + + <div class="col-12"> + <router-outlet></router-outlet> + </div> +</div> + diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.spec.ts b/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a5868afdd2a0176a827e514c5b84edcd73cf0e4 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.spec.ts @@ -0,0 +1,31 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AppnavigatorComponent } from './appnavigator.component'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {RouterModule} from '@angular/router'; + +describe('AppnavigatorComponent', () => { + let component: AppnavigatorComponent; + let fixture: ComponentFixture<AppnavigatorComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AppnavigatorComponent ], + imports : [ + HttpClientTestingModule, + RouterModule.forRoot([]), + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AppnavigatorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.ts b/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..f78e8d3f5d4de5e27815e22f70cd2191ce97f524 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appnavigator/appnavigator.component.ts @@ -0,0 +1,55 @@ +import { Component, OnInit } from '@angular/core'; +import {ActivatedRoute, NavigationEnd, Router} from '@angular/router'; + + +@Component({ + selector: 'app-appnavigator', + templateUrl: './appnavigator.component.html', + styleUrls: ['./appnavigator.component.css'] +}) +export class AppnavigatorComponent implements OnInit { + + public status = -1; + public iconPiCheck = 'pi pi-check'; + + public items = [{ + label: 'Select', + }, + { + label: 'Upload', + }, + { + label: 'Summary', + }, + ] + + + constructor(protected readonly router: Router, + protected readonly activatedRoute: ActivatedRoute) { } + + ngOnInit(): void { + this.checkStep(); + this.router.events.subscribe(event => { + if (event instanceof NavigationEnd) { + this.checkStep(); + } + }) + } + + private checkStep() { + switch (this.router.url) { + case '/instances/deploy/select' : + this.status = 0; + break; + case '/instances/deploy/upload' : + this.status = 1 + break; + case '/instances/deploy/summary' : + this.status = 2 + break; + default : + break; + } + } + +} diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.css b/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.html b/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.html new file mode 100644 index 0000000000000000000000000000000000000000..e287ce11471ec03f4cb14282b171ce9444c3cc59 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.html @@ -0,0 +1,23 @@ +<div style="margin-top: 5rem"> + <p-table [value]="deploy.result"> + <ng-template pTemplate="header"> + <tr> + <th>Domain</th> + <th>Application Name</th> + <th>Application Instance</th> + <th>Version</th> + <th>Parameters</th> + </tr> + </ng-template> + <ng-template pTemplate="body" let-deploy> + <tr> + <td>{{deploy.domainName}}</td> + <td>{{deploy.applicationName}}</td> + <td>{{deploy.applicationInstanceName}}</td> + <td>{{deploy.applicationVersion}}</td> + <td>{{deploy.parameters.toString()}}</td> + </tr> + </ng-template> + </p-table> +</div> + diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.spec.ts b/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..753e8e93e6769d085b8170eb9200f9a877d4a2bb --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.spec.ts @@ -0,0 +1,29 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AppsummaryComponent } from './appsummary.component'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; + +describe('AppsummaryComponent', () => { + let component: AppsummaryComponent; + let fixture: ComponentFixture<AppsummaryComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AppsummaryComponent ], + imports : [ + HttpClientTestingModule, + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AppsummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.ts b/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..befdce42d783d624a756ab1b5e65ccc30a600cab --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appsummary/appsummary.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; +import {AppdeploymentService} from '../../appdeployment.service'; + +@Component({ + selector: 'app-appsummary', + templateUrl: './appsummary.component.html', + styleUrls: ['./appsummary.component.css'] +}) +export class AppsummaryComponent implements OnInit { + + constructor(public deploy: AppdeploymentService) { } + + ngOnInit(): void { + } + +} diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.css b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.html b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.html new file mode 100644 index 0000000000000000000000000000000000000000..d069453d416a54454a06bf838cbe8d1f45055755 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.html @@ -0,0 +1,4 @@ +<div style="margin-top: 10rem; margin-left: 4rem; margin-right: 4rem;"> + <p-fileUpload name="file[]" customUpload="true" (uploadHandler)="myUploader($event)" accept=".csv" multiple="false"></p-fileUpload> +</div> + diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.spec.ts b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..31222eb1d5ff348afdc270c1649dd2af63561a71 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.spec.ts @@ -0,0 +1,31 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AppuploadComponent } from './appupload.component'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {RouterModule} from '@angular/router'; + +describe('AppuploadComponent', () => { + let component: AppuploadComponent; + let fixture: ComponentFixture<AppuploadComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AppuploadComponent ], + imports : [ + HttpClientTestingModule, + RouterModule.forRoot([]), + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AppuploadComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.ts b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e3e788c8d17e0e474f6db8578cd424d0547f9f1 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appDeployment/appupload/appupload.component.ts @@ -0,0 +1,28 @@ +import { Component, OnInit } from '@angular/core'; +import {AppdeploymentService} from '../../appdeployment.service'; +import {Router} from '@angular/router'; + +@Component({ + selector: 'app-appupload', + templateUrl: './appupload.component.html', + styleUrls: ['./appupload.component.css'] +}) +export class AppuploadComponent implements OnInit { + + constructor(private readonly deployService: AppdeploymentService, + private router: Router) { } + + ngOnInit(): void { + console.warn("selected app", this.deployService.getSelectedApp()) + } + + myUploader(event: any) { + console.log(event.files[0]) + //TODO add some in progress bar when waiting for information + this.deployService.uploadApplicationFile(event.files[0]).subscribe( val => { + console.warn("done") + this.deployService.result = val; + this.router.navigate(['instances/deploy/summary']) + }); + } +} diff --git a/src/app/appmarket/bulkDeployment/appdeployment.service.ts b/src/app/appmarket/bulkDeployment/appdeployment.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..c20240f2270cdecde0fb577ccad6eec568977319 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/appdeployment.service.ts @@ -0,0 +1,62 @@ +import {Injectable} from '@angular/core'; +import {Application} from '../../model/application'; +import {ApplicationBase} from '../../model/application-base'; +import {HttpClient} from '@angular/common/http'; +import {AppConfigService} from '../../service'; +import {BulkReplay} from '../../model/bulk-replay'; +import {Observable} from 'rxjs'; +import {BulkDeployment} from '../../model/bulk-deployment'; + +@Injectable({ + providedIn: 'root' +}) +export class AppdeploymentService { + + private selectedApp: ApplicationBase = undefined; + + public result: BulkReplay[] = []; + + public bulk: BulkDeployment; + + + constructor(private http: HttpClient, + private appConfig: AppConfigService) { + } + + + setSelectedApp(app: ApplicationBase) { + this.selectedApp = app; + } + + getSelectedApp() {return this.selectedApp;} + + protected getUrl(): string { + return this.appConfig.getApiUrl() + '/bulks/'; + } + + public uploadApplicationFile(file: File): Observable<BulkReplay[]> { + const formParams = new FormData(); + formParams.append('file', file); + return this.http.post<BulkReplay[]>(this.getUrl() + 'apps', formParams); + } + + public uploadUserDomainFile(file: File): Observable<BulkDeployment> { + const formParams = new FormData(); + formParams.append('file', file); + return this.http.post<BulkDeployment>(this.getUrl() + 'domains', formParams); + } + + public getBulksDomainDeployments(): Observable<BulkDeployment[]> { + return this.http.get<BulkDeployment[]>(this.getUrl() + 'domains'); + } + public getBulksAppDeployments(): Observable<BulkDeployment[]> { + return this.http.get<BulkDeployment[]>(this.getUrl() + 'apps'); + } + + public getBulkDeployment(id: number): Observable<BulkDeployment> { + return this.http.get<BulkDeployment>(this.getUrl() + id); + } + + + +} diff --git a/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.css b/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.html b/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.html new file mode 100644 index 0000000000000000000000000000000000000000..a9ce918dd30a4abd34f8d8cb6c9b84a7e25729b9 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.html @@ -0,0 +1,2 @@ +<app-bulk-list [header]="'BULK.APP.HEADER'" [bulks]="bulks"></app-bulk-list> + diff --git a/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.spec.ts b/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..1822ab7b5a20456e548bd7ca5153cf6a3cf64a9a --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.spec.ts @@ -0,0 +1,29 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BulkAppListComponent } from './bulk-app-list.component'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; + +describe('BulkAppListComponent', () => { + let component: BulkAppListComponent; + let fixture: ComponentFixture<BulkAppListComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BulkAppListComponent ], + imports: [ + HttpClientTestingModule + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BulkAppListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.ts b/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b46eb1ac9e5759cef4aa44e1e388e389ecc5e21b --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-app-list/bulk-app-list.component.ts @@ -0,0 +1,19 @@ +import { Component, OnInit } from '@angular/core'; +import {BulkDeployment} from '../../../model/bulk-deployment'; +import {AppdeploymentService} from '../appdeployment.service'; + +@Component({ + selector: 'app-bulk-app-list', + templateUrl: './bulk-app-list.component.html', + styleUrls: ['./bulk-app-list.component.css'] +}) +export class BulkAppListComponent implements OnInit { + + public bulks: BulkDeployment[] = []; + + constructor(private readonly deployService: AppdeploymentService) { } + + ngOnInit(): void { + this.deployService.getBulksAppDeployments().subscribe(data => this.bulks = data); + } +} diff --git a/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.css b/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.html b/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.html new file mode 100644 index 0000000000000000000000000000000000000000..4fce8a7aff77e36402db99e46f19f0b738e1bfe8 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.html @@ -0,0 +1 @@ +<app-bulk-list [header]="'BULK.DOMAIN.HEADER'" [bulks]="bulks"></app-bulk-list> diff --git a/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.spec.ts b/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..2ec6b80e6f048ff52a634ce5369dbf33735a693d --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.spec.ts @@ -0,0 +1,29 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BulkDomainListComponent } from './bulk-domain-list.component'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; + +describe('BulkDomainListComponent', () => { + let component: BulkDomainListComponent; + let fixture: ComponentFixture<BulkDomainListComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BulkDomainListComponent ], + imports: [ + HttpClientTestingModule + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BulkDomainListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.ts b/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad1802a3eec2efd2556fb6e342b11f2ba14a9a23 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-domain-list/bulk-domain-list.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit } from '@angular/core'; +import {AppdeploymentService} from '../appdeployment.service'; +import {BulkDeployment} from '../../../model/bulk-deployment'; + +@Component({ + selector: 'app-bulk-domain-list', + templateUrl: './bulk-domain-list.component.html', + styleUrls: ['./bulk-domain-list.component.css'] +}) +export class BulkDomainListComponent implements OnInit { + + public bulks: BulkDeployment[] = []; + + constructor(private readonly deployService: AppdeploymentService) { } + + ngOnInit(): void { + this.deployService.getBulksDomainDeployments().subscribe(data => this.bulks = data); + } + +} diff --git a/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.css b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.html b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.html new file mode 100644 index 0000000000000000000000000000000000000000..a377030fb7bf0bbab3ccc11af610c0d827f6dadc --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.html @@ -0,0 +1,37 @@ +<div class="col-sm-12 col-sm-offset-1 col-sm 10 col-md-offset-1 col-md-10"> + <h3>{{header | translate}}</h3> + <table class="table table-hover table-condensed" style="margin-top: 3rem" aria-describedby="Bulk deployment table"> + <thead> + <tr> + <th scope="col">{{'BULK.LIST.ID' | translate}}</th> + <th scope="col">{{'BULK.LIST.CREATOR' | translate}}</th> + <th scope="col">{{'BULK.LIST.CREATION_DATE' | translate}}</th> + <th scope="col">{{'BULK.LIST.STATE' | translate}}</th> + <th></th> + </tr> + </thead> + + <tbody> + <ng-template ngFor let-bulk [ngForOf]="bulks" let-i="index"> + <tr class="table-row" > + <td style="width: 25%">{{bulk?.id}}</td> + <td style="width: 20%">{{bulk?.creator.username}}</td> + <td style="width: 15%">{{bulk?.creationDate | date: 'dd.MM.yyyy h:mm'}}</td> + <td style="width: 15%">{{bulk?.state}}</td> + <td style="width: 20%" class="text-right"> + <span class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> + <em class="fas fa-cog icon-black icon-bigger"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> + <li> + <a [routerLink]="['/admin/domains/bulks/', bulk?.id]">{{ 'BULK.LIST.DETAILS' | translate }}</a> + </li> + </ul> + </span> + </td> + </tr> + </ng-template> + </tbody> + </table> +</div> diff --git a/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.spec.ts b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a7f4167cf48146a07b25bd9cf93839ea450df12d --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.spec.ts @@ -0,0 +1,36 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BulkListComponent } from './bulk-list.component'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {TranslateFakeLoader, TranslateLoader, TranslateModule} from '@ngx-translate/core'; + +describe('BulkListComponent', () => { + let component: BulkListComponent; + let fixture: ComponentFixture<BulkListComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BulkListComponent ], + imports: [ + HttpClientTestingModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader + } + }), + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BulkListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.ts b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..612fc6e4c1a8c046eb83aed212a5ca3880ad49c3 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-list/bulk-list.component.ts @@ -0,0 +1,23 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {BulkDeployment} from '../../../model/bulk-deployment'; + +@Component({ + selector: 'app-bulk-list', + templateUrl: './bulk-list.component.html', + styleUrls: ['./bulk-list.component.css'] +}) +export class BulkListComponent implements OnInit { + + @Input() + public bulks: BulkDeployment[] = []; + + @Input() + public header: string; + + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.css b/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.css new file mode 100644 index 0000000000000000000000000000000000000000..98ad64a62b5e96a0d2c0d4b2ca528e7d392cb189 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.css @@ -0,0 +1,12 @@ +.form-control::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ + color: #555555; + opacity: 1; /* Firefox */ +} + +.form-control:-ms-input-placeholder { /* Internet Explorer 10-11 */ + color: #555555; +} + +.form-control::-ms-input-placeholder { /* Microsoft Edge */ + color: #555555; +} diff --git a/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.html b/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.html new file mode 100644 index 0000000000000000000000000000000000000000..d61f95ead395731afad04b2e11ed21af2d8d47ef --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.html @@ -0,0 +1,77 @@ +<div class="col-sm-12 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-10"> + <div *ngIf="bulk && bulkType === 'DOMAIN' "> + <h3>{{'BULK.DOMAIN.HEADER' | translate}}</h3> + <div class="" style="padding-bottom: 5rem; margin-top: 3rem"> + <label for="id" class="col-sm-2 control-label">{{ 'BULK.LIST.ID' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="id" name="id" [disabled]="true" + [(ngModel)]="bulk.id" #name="ngModel"> + </div> + </div> + + <div class="" style="padding-bottom: 5rem"> + <label for="creator" class="col-sm-2 control-label">{{ 'BULK.LIST.CREATOR' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="creator" name="creator" [disabled]="true" + placeholder="{{bulk.creator.username}} ({{bulk.creator.firstname}} {{bulk.creator.lastname}})"> + </div> + </div> + + <div class="" style="padding-bottom: 5rem"> + <label for="date" class="col-sm-2 control-label">{{ 'BULK.LIST.CREATION_DATE' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="date" name="date" [disabled]="true" + placeholder="{{bulk.creationDate | date: 'dd.MM.yyyy h:mm'}}"> + </div> + </div> + + <div class="form-group" style="padding-bottom: 5rem"> + <label for="state" class="col-sm-2 control-label">{{ 'BULK.LIST.STATE' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="state" name="state" [disabled]="true" + [(ngModel)]="bulk.state" #name="ngModel"> + </div> + </div> + + <table class="table table-hover table-condensed" aria-describedby="Domains in Group table" style="margin-top: 3rem"> + <thead> + <tr> + <th>{{'BULK.LIST.TYPE' | translate}}</th> + <th>{{'BULK.LIST.SUCCESSFUL' | translate}}</th> + <th>{{'BULK.LIST.CREATED' | translate}}</th> + <th>{{'BULK.LIST.DETAILS' | translate}}</th> + <th style="width: 5%" scope="col"></th> + </tr> + <ng-template ngFor let-replay [ngForOf]="bulk.entries" let-i="index"> + <tr class="table-row"> + <td>{{replay.type}}</td> + <td>{{replay.successful}}</td> + <td>{{replay.created}}</td> + <td>{{getDetails(replay)}}</td> + <td style="width: 5%" class="text-right"> + <span class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> + <em class="fas fa-cog icon-black icon-bigger"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> + <li *ngIf="replay.type === 'DOMAIN'"> + <a [routerLink]="['/admin/domains/view/', replay?.details['domainId']]">{{ 'BULK.LIST.MOVE_DOMAIN' | translate }}</a> + </li> + <li *ngIf="replay.type === 'USER'"> + <a [routerLink]="['/admin/users/view', replay?.details['userId']]">{{ 'BULK.LIST.MOVE_USER' | translate }}</a> + </li> + </ul> + </span> + </td> + </tr> + </ng-template> + </thead> + </table> + + </div> + + <div *ngIf="bulk && bulkType === 'APPLICATION' "> + + </div> + +</div> diff --git a/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.spec.ts b/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..33287d7e82506504fe8fc2bf9a4100084a49ff39 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.spec.ts @@ -0,0 +1,31 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BulkViewComponent } from './bulk-view.component'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {RouterModule} from '@angular/router'; + +describe('BulkViewComponent', () => { + let component: BulkViewComponent; + let fixture: ComponentFixture<BulkViewComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BulkViewComponent ], + imports: [ + HttpClientTestingModule, + RouterModule.forRoot([]), + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BulkViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.ts b/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..17369fc45c184d22e69a27d8a3838f04aa5d1e64 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/bulk-view/bulk-view.component.ts @@ -0,0 +1,56 @@ +import {Component, OnInit} from '@angular/core'; +import {BulkDeployment} from '../../../model/bulk-deployment'; +import {AppdeploymentService} from '../appdeployment.service'; +import {ActivatedRoute, Router} from '@angular/router'; +import {BulkReplay, BulkType} from '../../../model/bulk-replay'; + +@Component({ + selector: 'app-bulk-view', + templateUrl: './bulk-view.component.html', + styleUrls: ['./bulk-view.component.css'] +}) +export class BulkViewComponent implements OnInit { + + public bulk: BulkDeployment; + public bulkId; + public bulkType: BulkType = BulkType.DOMAIN; + + constructor(private readonly deployService: AppdeploymentService, + private route: ActivatedRoute, + private router: Router, + ) { } + + ngOnInit(): void { + this.route.params.subscribe(params => { + if (params['id'] !== undefined) { + this.bulkId = +params['id']; + this.deployService.getBulkDeployment(this.bulkId).subscribe( + (bulk) => { + this.bulk = bulk; + this.bulkType = bulk.type; + }, + err => { + console.error(err); + if (err.statusCode && (err.statusCode === 404 || + err.statusCode === 401 || err.statusCode === 403 || err.statusCode === 500)) { + this.router.navigateByUrl('/notfound'); + } + } + ) + } + }); + } + + public getDetails(entry: BulkReplay) { + if (entry.type === 'USER') { + return `Username: ${entry.details['userName']} email: ${entry.details['email']} userId: ${entry.details['userId']}` + } else if (entry.type === 'DOMAIN') { + return `DomainId: ${entry.details['domainId']} name: ${entry.details['domainName']}` + } else { + return '' + } + } + + + +} diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.css b/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.html b/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.html new file mode 100644 index 0000000000000000000000000000000000000000..fe01e376c3564493c32da07855b8356f5dbc185a --- /dev/null +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.html @@ -0,0 +1,9 @@ +<div class="col-12 flex align-items-center flex-column" style=";min-height: 100% !important;" > + <p-steps [model]="items" [readonly]="true" [activeIndex]="status"></p-steps> + + + <div class="col-12"> + <router-outlet></router-outlet> + </div> +</div> + diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.spec.ts b/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3bec5b03d25ff7f73873785d319f190efe70d520 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.spec.ts @@ -0,0 +1,29 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DomainNavigatorComponent } from './domain-navigator.component'; +import {RouterTestingModule} from '@angular/router/testing'; + +describe('DomainnavigatorComponent', () => { + let component: DomainNavigatorComponent; + let fixture: ComponentFixture<DomainNavigatorComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DomainNavigatorComponent ], + imports: [ + RouterTestingModule, + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DomainNavigatorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.ts b/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..bffc45802d188b72d8de381e6e2de96524b87aab --- /dev/null +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component.ts @@ -0,0 +1,50 @@ +import { Component, OnInit } from '@angular/core'; +import {ActivatedRoute, NavigationEnd, Router} from '@angular/router'; + +@Component({ + selector: 'app-domainnavigator', + templateUrl: './domain-navigator.component.html', + styleUrls: ['./domain-navigator.component.css'] +}) +export class DomainNavigatorComponent implements OnInit { + + public status = -1; + public iconPiCheck = 'pi pi-check'; + + public items = [ + { + label: 'Upload', + }, + { + label: 'Summary', + }, + ] + + + constructor(protected readonly router: Router, + protected readonly activatedRoute: ActivatedRoute) { } + + ngOnInit(): void { + this.checkStep(); + this.router.events.subscribe(event => { + if (event instanceof NavigationEnd) { + this.checkStep(); + } + }) + } + + private checkStep() { + switch (this.router.url) { + case '/admin/domains/deploy/upload' : + this.status = 0; + break; + case '/admin/domains/deploy/summary' : + this.status = 1 + break; + default : + break; + } + } + + +} diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.css b/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.html b/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.html new file mode 100644 index 0000000000000000000000000000000000000000..1a62f1a59978241eff557e922eae99b37e1f472b --- /dev/null +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.html @@ -0,0 +1,23 @@ +<div style="margin-top: 5rem"> + <div style="margin-bottom: 5rem"> + <h3>{{'NAVBAR.BULK_DEPLOYMENT_DOMAIN' | translate}}</h3> + <p>{{'BULK.DOMAIN.SUMMARY' | translate}}</p> + </div> + <p-table [value]="deploy.result"> + <ng-template pTemplate="header"> + <tr> + <th>{{'BULK.LIST.CREATED' | translate}}</th> + <th>{{'BULK.LIST.SUCCESSFUL' | translate}}</th> + <th>{{'BULK.LIST.DETAILS' | translate}}</th> + + </tr> + </ng-template> + <ng-template pTemplate="body" let-deploy> + <tr> + <td>{{deploy.created}}</td> + <td>{{deploy.successful}}</td> + <td>{{getDetails(deploy)}}</td> + </tr> + </ng-template> + </p-table> +</div> diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.spec.ts b/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..56d6275c61a40fb0e48b187442abfcec7fd6470f --- /dev/null +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.spec.ts @@ -0,0 +1,37 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DomainsummaryComponent } from './domainsummary.component'; +import {RouterTestingModule} from '@angular/router/testing'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {TranslateFakeLoader, TranslateLoader, TranslateModule} from '@ngx-translate/core'; + +describe('DomainsummaryComponent', () => { + let component: DomainsummaryComponent; + let fixture: ComponentFixture<DomainsummaryComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DomainsummaryComponent ], + imports: [ + HttpClientTestingModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader + } + }), + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DomainsummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.ts b/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4fa7fdacd4e2fc81578b0bb1ad9fd13504f27cb --- /dev/null +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainsummary/domainsummary.component.ts @@ -0,0 +1,28 @@ +import {Component, OnInit} from '@angular/core'; +import {AppdeploymentService} from '../../appdeployment.service'; +import {BulkReplay, BulkType} from '../../../../model/bulk-replay'; + +@Component({ + selector: 'app-domainsummary', + templateUrl: './domainsummary.component.html', + styleUrls: ['./domainsummary.component.css'] +}) +export class DomainsummaryComponent implements OnInit { + + constructor(public deploy: AppdeploymentService) { } + + ngOnInit(): void { + console.warn(this.deploy.result); + } + + public getDetails(entry: BulkReplay) { + if (entry.type === 'USER') { + return `Username: ${entry.details['userName']} email: ${entry.details['email']} userId: ${entry.details['userId']}` + } else if (entry.type === 'DOMAIN') { + return `Domain: ${entry.details['domainId']} name: ${entry.details['domainName']}` + } else { + return '' + } +} + +} diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.css b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.html b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.html new file mode 100644 index 0000000000000000000000000000000000000000..36a19ae14aad7e6474d39f9949e081c15ef979fb --- /dev/null +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.html @@ -0,0 +1,12 @@ +<div style="width: 80%; margin: auto; margin-top: 4rem;"> + <div style="margin-bottom: 5rem"> + <h3>{{'NAVBAR.BULK_DEPLOYMENT_DOMAIN' | translate}}</h3> + <p>{{'BULK.DOMAIN.INFO' | translate}}</p> + </div> + <p-fileUpload name="file[]" customUpload="true" (uploadHandler)="myUploader($event)" accept=".csv" + multiple="false"></p-fileUpload> + <div *ngIf="showProgressBar" style="margin-top: 20px;"> + <p>{{ 'BULK.DOMAIN.DEPLOYMENT_IN_PROGRESS' | translate }}</p> + <p-progressBar mode="indeterminate" [style]="{height : '8px'}"></p-progressBar> + </div> +</div> diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.spec.ts b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4b7a42691765016348bc917ee2e6faf511302cf7 --- /dev/null +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.spec.ts @@ -0,0 +1,38 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DomainuploadComponent } from './domainupload.component'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {RouterTestingModule} from '@angular/router/testing'; +import {TranslateFakeLoader, TranslateLoader, TranslateModule} from '@ngx-translate/core'; + +describe('DomainuploadComponent', () => { + let component: DomainuploadComponent; + let fixture: ComponentFixture<DomainuploadComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DomainuploadComponent ], + imports: [ + HttpClientTestingModule, + RouterTestingModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader + } + }), + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DomainuploadComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.ts b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..18a3a28cb5e050013accfbd48f93247ee3ef46ff --- /dev/null +++ b/src/app/appmarket/bulkDeployment/domainDeployment/domainupload/domainupload.component.ts @@ -0,0 +1,36 @@ +import {Component, OnInit} from '@angular/core'; +import {AppdeploymentService} from '../../appdeployment.service'; +import {Router} from '@angular/router'; +import {BulkType} from '../../../../model/bulk-replay'; + +@Component({ + selector: 'app-domainupload', + templateUrl: './domainupload.component.html', + styleUrls: ['./domainupload.component.css'] +}) +export class DomainuploadComponent implements OnInit { + + public showProgressBar = false; + + constructor(private readonly deployService: AppdeploymentService, + private router: Router) { } + + ngOnInit(): void { + } + + myUploader(event: any) { + console.log(event.files[0]) + // TODO add some in progress bar when waiting for information + this.showProgressBar = true; + this.deployService.uploadUserDomainFile(event.files[0]).subscribe( val => { + console.warn("done") + this.deployService.bulk = val; + if (val.type === BulkType.DOMAIN) { + this.router.navigate(['admin/domains/bulks/', val.id]) + } else { + this.router.navigate(['admin/domains/deploy/summary']) + } + }); + } + +} diff --git a/src/app/appmarket/domains/domain-group-view/domain-group-view.component.css b/src/app/appmarket/domains/domain-group-view/domain-group-view.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e7ceefc63fb2a7006f4d4cdb127fd87600f475af --- /dev/null +++ b/src/app/appmarket/domains/domain-group-view/domain-group-view.component.css @@ -0,0 +1,67 @@ +:host ::ng-deep .p-multiselect { + width: 300px; + } + +.no-spin, .no-spin:hover, .no-spin:focus { + -webkit-appearance: none; + margin: 0; + -moz-appearance:textfield; +} + +input.ng-dirty.ng-invalid { + border: 1px solid red; +} + +.row-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + align-items: center; + margin-top: 5px; + min-height: 30px; +} +.row-container:first-of-type { + margin-top: 0; +} + +.row-sub { + flex-basis: 100%; +} + + +.custom-collapse[aria-expanded="false"] { + line-height: 1; + padding: 4px 4px 2px; +} + +.custom-collapse[aria-expanded="false"]:after { + font-family: "Glyphicons Halflings", sans-serif; + content:"\e114"; + /*margin-left: .2px;*/ +} + +.custom-collapse[aria-expanded="true"] { + line-height: 1; + padding: 3px 4px; +} + +.custom-collapse[aria-expanded="true"]:after { + font-family: "Glyphicons Halflings", sans-serif; + content:"\e113"; + /*margin-left: .2px;*/ +} + +.no-bullet { + list-style-type: none; +} + +.no-padding-top { + padding-top: 0!important; +} + +.margin-right { + margin-right: 2rem; +} + + diff --git a/src/app/appmarket/domains/domain-group-view/domain-group-view.component.html b/src/app/appmarket/domains/domain-group-view/domain-group-view.component.html new file mode 100644 index 0000000000000000000000000000000000000000..5e6f3036f86c027ec6a71edae129514f864e2c97 --- /dev/null +++ b/src/app/appmarket/domains/domain-group-view/domain-group-view.component.html @@ -0,0 +1,159 @@ +<div class="col-sm-12 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-10"> + <h3>{{ 'DOMAINS.LIST.GROUP' | translate }}</h3> + <form *ngIf="domainGroup" (submit)="submit(false)" class="form-horizontal" #domainForm="ngForm"> + <div class="form-group"> + <label for="name" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.NAME' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="name" name="name" + [(ngModel)]="domainGroup.name" #name="ngModel" required> + <div *ngIf="name.invalid && (name.dirty || name.touched)" + class="alert alert-danger"> + <div *ngIf="name.errors.required">{{ 'DOMAIN_DETAILS.NAME_IS_REQUIRED_MESSAGE' | translate }}</div> + </div> + </div> + </div> + + <div class="form-group" *ngIf="!isInMode(ComponentMode.EDIT)"> + <label for="codename" class="col-sm-2 control-label">{{ 'DOMAIN_DETAILS.CODE_NAME' | translate }}</label> + <div class="col-sm-10"> + <input type="text" class="form-control" id="codename" + name="codename" pattern="[a-zA-Z0-9-]*" + [(ngModel)]="domainGroup.codename" #codename="ngModel" minlength="2" maxlength="20" required> + <div *ngIf="codename.invalid && (codename.dirty || codename.touched)" + class="alert alert-danger"> + + <div *ngIf="codename.errors.required">{{ 'DOMAIN_DETAILS.CODE_NAME_IS_REQUIRED_MESSAGE' | translate }}</div> + <div *ngIf="codename.errors.pattern">{{ 'DOMAIN_DETAILS.CODE_NAME_PATTERN_MESSAGE_1' | translate }}</div> + <div *ngIf="codename.errors.minlength || codename.errors.maxlength">{{ 'DOMAIN_DETAILS.CODE_NAME_PATTERN_MESSAGE_2' | translate }}</div> + + </div> + </div> + </div> + + <div *ngIf="!this.addingMode" style="margin-top: 3rem"> + <button type="button" class="btn btn-success" (click)="showModal()">{{'DOMAINS.LIST.ADD' | translate}}</button> + <table class="table table-hover table-condensed" aria-describedby="Domains in Group table" style="margin-top: 3rem"> + <thead> + <tr> + <th scope="col">{{'DOMAINS.LIST.DOMAIN_NAME' | translate}}</th> + <th scope="col">{{'DOMAINS.LIST.DOMAIN_CODE_NAME' | translate}}</th> + <th scope="col"></th> + </tr> + <ng-template ngFor let-domain [ngForOf]="domainGroup.domains" let-i="index"> + <tr class="table-row"> + <td>{{domain.name}}</td> + <td>{{domain.codename}}</td> + <td style="width: 20%"> + <button type="button" class="btn btn-danger" (click)="deleteDomainFromGroup(domain)">{{'DOMAINS.LIST.DELETE' | translate}}</button> + </td> + </tr> + </ng-template> + </thead> + </table> + </div> + + <div *ngIf="!this.addingMode" class="panel panel-default" style="margin-top: 3rem"> + <div class="panel-heading"> + + <div style="display: flex; justify-content: space-between; align-items: center"> + <div> + {{ 'DOMAIN_DETAILS.APP_STATUS' | translate }} + </div> + <button class="btn btn-primary" type="button" (click)="toggleAll()">{{'SHARED.TOGGLE' | translate}}</button> + </div> + </div> + <div class="panel-body"> + <table class="table table-hover table-condensed" aria-describedby="Domain details table"> + <thead> + <tr> + <th scope="col"> + {{ 'DOMAIN_DETAILS.APP_NAME' | translate }} + </th> + </tr> + </thead> + + <tbody> + <tr *ngFor="let applicationStatePerDomain of this.domainGroup.applicationStatePerDomain; let i = index"> + <td class="row-container"> + <div class="col-sm-4">{{applicationStatePerDomain.applicationBaseName}}</div> + + <div class="col-sm-8" style="display: flex; justify-content: end;"> + <div *ngIf="domainGroup.applicationStatePerDomain[i].enabled" class="margin-right" > + <i class="pi pi-check" style="color: green; font-size: 2rem"></i> + </div> + <div *ngIf="!domainGroup.applicationStatePerDomain[i].enabled" class="margin-right"> + <i class="pi pi-times" style="color: indianred; font-size: 2rem"></i> + </div> + <button class="btn pull-right custom-collapse" type="button" data-toggle="collapse" [attr.data-target]="'#collapse-'+i" aria-expanded="false"> </button> + </div> + <div class="row-sub collapse" id="collapse-{{i}}"> + <div class="card ui-card-body"> + <div class="form-group"> + <div class="row-container"> + <label for="domain-state-enabled-{{i}}" class="col-sm-4 control-label no-padding-top"> + {{ 'DOMAIN_DETAILS.APP_ENABLED' | translate }} + </label> + <div class="col-sm-8"> + <input type="checkbox" class="custom-control-input" + id="domain-state-enabled-{{i}}" + name="domain-state-enabled-{{i}} " + [(ngModel)]="domainGroup.applicationStatePerDomain[i].enabled"> + </div> + </div> + <div class="row-container"> + <label for="domain-state-storage-limit-size-{{i}}" class="col-sm-4 control-label no-padding-top"> + {{ 'DOMAIN_DETAILS.PV_STORAGE_SIZE_LIMIT' | translate }} + </label> + <div class="col-sm-8"> + <input type="number" step="1" min="1" max="100" class="form-control" + id="domain-state-storage-limit-size-{{i}}" + name="domain-state-storage-limit-size-{{i}} " + [(ngModel)]="domainGroup.applicationStatePerDomain[i].pvStorageSizeLimit"> + </div> + </div> + + <!-- Extend this part to add forms for in domain state--> + + </div> + </div> + </div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + + <button type="submit" class="btn btn-default" [disabled]="!domainForm.form.valid">{{ 'DOMAIN_DETAILS.SUBMIT_BUTTON' | translate }}</button> + + </form> +</div> + +<nmaas-modal> + <div class="nmaas-modal-header">{{'DOMAINS.LIST.GROUP' | translate}}</div> + <div class="nmaas-modal-body" style="height: 300px"> + <div style="height: 100px"> + <p>{{'DOMAINS.LIST.ADD_INFO' | translate}}</p> + <p-multiSelect [panelStyle]="{minWidth:'25em'}" [(ngModel)]="domainsToAdd" [options]="domains" ngDefaultControl name="domains" optionLabel="name"></p-multiSelect> + </div> + <table class="table table-hover table-condensed" aria-describedby="Domains in Group table"> + <thead> + <tr> + <th scope="col">{{'DOMAINS.LIST.DOMAIN_NAME' | translate}}</th> + <th scope="col">{{'DOMAINS.LIST.DOMAIN_CODE_NAME' | translate}}</th> + </tr> + <ng-template ngFor let-domain [ngForOf]="domainsToAdd" let-i="index"> + <tr class="table-row"> + <td>{{domain.name}}</td> + <td>{{domain.codename}}</td> + </tr> + </ng-template> + </thead> + </table> + </div> + <div class="nmaas-modal-footer"> + <button type="button" class="btn btn-default" (click)="modal.hide()">{{'APP_CHANGE_STATE_MODAL.CANCEL_BUTTON' | translate}}</button> + <button type="button" class="btn btn-primary" (click)="closeModal()" [disabled]="false">{{'DOMAINS.LIST.ADD' | translate}}</button> + </div> +</nmaas-modal> + diff --git a/src/app/appmarket/domains/domain-group-view/domain-group-view.component.spec.ts b/src/app/appmarket/domains/domain-group-view/domain-group-view.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c93ab444d18e2ff6638a3ab576c08a3ea73386c --- /dev/null +++ b/src/app/appmarket/domains/domain-group-view/domain-group-view.component.spec.ts @@ -0,0 +1,41 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DomainGroupViewComponent } from './domain-group-view.component'; +import {RouterTestingModule} from '@angular/router/testing'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {TranslateFakeLoader, TranslateLoader, TranslateModule} from '@ngx-translate/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; + +describe('DomainGroupViewComponent', () => { + let component: DomainGroupViewComponent; + let fixture: ComponentFixture<DomainGroupViewComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DomainGroupViewComponent ], + imports: [ + RouterTestingModule, + HttpClientTestingModule, + FormsModule, + ReactiveFormsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader + } + }), + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DomainGroupViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/domains/domain-group-view/domain-group-view.component.ts b/src/app/appmarket/domains/domain-group-view/domain-group-view.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..dc2edd4079fb58382df57f9c446238097353d7b9 --- /dev/null +++ b/src/app/appmarket/domains/domain-group-view/domain-group-view.component.ts @@ -0,0 +1,139 @@ +import {Component, OnInit, ViewChild} from '@angular/core'; +import {ActivatedRoute, Router} from '@angular/router'; +import {DomainService} from '../../../service'; +import {BaseComponent} from '../../../shared/common/basecomponent/base.component'; +import {AuthService} from '../../../auth/auth.service'; +import {ComponentMode, ModalComponent} from '../../../shared'; +import {DomainGroup} from '../../../model/domaingroup'; +import {Domain} from '../../../model/domain'; + +@Component({ + selector: 'app-domain-group-view', + templateUrl: './domain-group-view.component.html', + styleUrls: ['./domain-group-view.component.css'] +}) +export class DomainGroupViewComponent extends BaseComponent implements OnInit { + + public domainGroupId ; + public domainGroup = new DomainGroup(); + public addingMode = false; + public domains: Domain[] = []; + public domainsToAdd = []; + + @ViewChild(ModalComponent, { static: true }) + public readonly modal: ModalComponent; + + constructor(private router: Router, + private route: ActivatedRoute, + private domainService: DomainService, + ) { + super(); + } + + ngOnInit(): void { + this.refreshDomainForAdd(); + if (this.route.snapshot.data['mode'] === ComponentMode.CREATE) { + console.warn("creation mode"); + this.addingMode = true; + } + this.route.params.subscribe(params => { + if (params['id'] !== undefined) { + this.domainGroupId = +params['id']; + this.domainService.getDomainGroup(this.domainGroupId).subscribe( + (domainGroup) => { + this.domainGroup = domainGroup; + }, + err => { + console.error(err); + if (err.statusCode && (err.statusCode === 404 || + err.statusCode === 401 || err.statusCode === 403 || err.statusCode === 500)) { + this.router.navigateByUrl('/notfound'); + } + }) + } else { + + } + }) + } + + submit(refresh = true) { + console.log(this.domainGroup) + // creation + if(this.domainGroup.id === undefined || this.domainGroup.id === null) { + this.domainService.createDomainGroup(this.domainGroup).subscribe( data => { + console.warn("crated", data); + this.router.navigate(['/admin/domains/groups/', data.id]); + }) + } else { + this.domainService.updateDomainGroup(this.domainGroup, this.domainGroupId).subscribe(_ => { + if (refresh) { + this.refresh(); + } else { + this.router.navigate(['/admin/domains/groups']) + } + }); + } + } + + public deleteDomainFromGroup(domain: Domain) { + this.domainService.deleteDomainFromGroup(this.domainGroup.id, domain.id).subscribe( _ => { + this.refresh() + this.refreshDomainForAdd() + }) + } + + public refresh() { + this.domainService.getDomainGroup(this.domainGroup.id).subscribe(data => this.domainGroup = data); + } + + public showModal(): void { + this.filterDomainInModal(); + this.modal.show(); + } + + public filterDomainInModal() { + const domainIds = this.domainGroup.domains.map(val => val.id); + this.domains = this.domains.filter(value => !domainIds.includes(value.id)) + } + + public closeModal(): void { + const domainIds = this.domainsToAdd.map(val => val.id); + this.domainService.addDomainsToGroup(this.domainGroup.codename, domainIds).subscribe(_ => { + this.refresh(); + this.domainsToAdd = []; + this.refreshDomainForAdd(); + }); + this.modal.hide(); + } + + public refreshDomainForAdd(): void { + this.domainService.getAll().subscribe(domains => { + this.domains = domains.filter(value => value.id !== this.domainService.getGlobalDomainId()); + }); + } + + public toggleAll(): void { + const id0 = `#collapse-${0}`; + const el0 = document.querySelector(id0) as HTMLElement; + if (el0) { + if (el0.classList.contains('show')) { + el0.classList.remove('show'); + } else { + el0.classList.add('show'); + } + } + for (let j = 1; j < this.domainGroup.applicationStatePerDomain.length; j++) { + const id = `#collapse-${j}`; + const el = document.querySelector(id) as HTMLElement; + if (el) { + if (el.classList.contains('show')) { + el.classList.remove('show'); + } else { + el.classList.add('show'); + } + } + } + + } + +} diff --git a/src/app/appmarket/domains/domain-groups/domain-groups.component.css b/src/app/appmarket/domains/domain-groups/domain-groups.component.css new file mode 100644 index 0000000000000000000000000000000000000000..64f05678ab2bee44a4333d66d3ae1935af945c39 --- /dev/null +++ b/src/app/appmarket/domains/domain-groups/domain-groups.component.css @@ -0,0 +1,2 @@ + +.pointer {cursor: pointer;} diff --git a/src/app/appmarket/domains/domain-groups/domain-groups.component.html b/src/app/appmarket/domains/domain-groups/domain-groups.component.html new file mode 100644 index 0000000000000000000000000000000000000000..2723e74f200f7da026e4d53f1826c52b6affd161 --- /dev/null +++ b/src/app/appmarket/domains/domain-groups/domain-groups.component.html @@ -0,0 +1,69 @@ +<div class="col-sm-12 col-sm-offset-1 col-sm 10 col-md-offset-1 col-md-10"> + <h3>{{'DOMAINS.LIST.GROUPS' | translate}}</h3> + <button [routerLink]="['/admin/domains/groups/add']" class="btn btn-primary">{{ 'APPS_MANAGEMENT.ADD_BUTTON' | translate }}</button> + <hr> + <table class="table table-hover table-condensed" aria-describedby="Apps management table"> + <thead> + <tr> + <th scope="col"></th> + <th scope="col">{{'APPS_MANAGEMENT.NAME' | translate}}</th> + <th scope="col">{{'DOMAINS.CODE_NAME' | translate}}</th> + <th scope="col">{{'DOMAINS.LIST.DOMAIN_NAME' | translate}}</th> + <th scope="col">{{'DOMAINS.LIST.DOMAIN_CODE_NAME' | translate}}</th> + </tr> + </thead> + + <tbody> + <ng-template ngFor let-domainGroup [ngForOf]="groups" let-i="index"> + <tr class="table-row" (click)="clickTableRow(i)"> + <td style="width: 5%" *ngIf="!domainsRowVisible[i]"><span class="glyphicon glyphicon-chevron-right"></span></td> + <td style="width: 5%" *ngIf="domainsRowVisible[i]"><span class="glyphicon glyphicon-chevron-down"></span></td> + <td style="width: 25%">{{domainGroup?.name}}</td> + <td style="width: 20%">{{domainGroup?.codename}}</td> + <td style="width: 15%"></td> + <td style="width: 15%"></td> + <td style="width: 20%" class="text-right"> + <span class="dropdown"> + <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button"> + <em class="fas fa-cog icon-black icon-bigger"></em> + </a> + <ul class="dropdown-menu pull-right-drop"> +<!-- <li>--> +<!-- <a (click)="showModal()">{{ 'ADD DOMAIN' }}</a>--> +<!-- </li>--> + <li> + <a [routerLink]="['/admin/domains/groups/', domainGroup?.id]">{{ 'APPS_MANAGEMENT.EDIT_BUTTON' | translate }}</a> + </li> + <li> + <a (click)="deleteDomainGroup(domainGroup?.id)">{{ 'APP_INSTANCE.REMOVE_BUTTON' | translate }}</a> + </li> + </ul> + </span> + </td> + </tr> + <ng-template ngFor let-domain [ngForOf]="domainGroup.domains"> + <tr *ngIf="domainsRowVisible[i]" class="table-row pointer" [routerLink]="['/admin/domains/view/', domain.id]"> + <td></td> + <td></td> + <td></td> + <td>{{domain.name}}</td> + <td>{{domain.codename}}</td> + <td class="text-right"> +<!-- <span class="dropdown">--> +<!-- <a style="display: inline-block" class="dropdown-toggle " aria-expanded="false" aria-haspopup="true" data-toggle="dropdown" href="#" role="button">--> +<!-- <em class="fas fa-cog icon-black icon-bigger"></em>--> +<!-- </a>--> +<!-- <ul class="dropdown-menu pull-right-drop">--> +<!-- <li>--> +<!-- <a>{{ 'APPS_MANAGEMENT.EDIT_BUTTON' | translate }}</a>--> +<!-- </li>--> +<!-- </ul>--> +<!-- </span>--> + </td> + </tr> + </ng-template> + </ng-template> + </tbody> + </table> +</div> + diff --git a/src/app/appmarket/domains/domain-groups/domain-groups.component.spec.ts b/src/app/appmarket/domains/domain-groups/domain-groups.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b6c53549cbcb06d654bbf4fb98d36e1a0d9de6c --- /dev/null +++ b/src/app/appmarket/domains/domain-groups/domain-groups.component.spec.ts @@ -0,0 +1,36 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DomainGroupsComponent } from './domain-groups.component'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {TranslateFakeLoader, TranslateLoader, TranslateModule} from '@ngx-translate/core'; + +describe('DomainGroupsComponent', () => { + let component: DomainGroupsComponent; + let fixture: ComponentFixture<DomainGroupsComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DomainGroupsComponent ], + imports: [ + HttpClientTestingModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader + } + }), + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DomainGroupsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/domains/domain-groups/domain-groups.component.ts b/src/app/appmarket/domains/domain-groups/domain-groups.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..dc6dea470f3a81fb70448d730c75479685457911 --- /dev/null +++ b/src/app/appmarket/domains/domain-groups/domain-groups.component.ts @@ -0,0 +1,43 @@ +import {Component, OnInit, ViewChild} from '@angular/core'; +import {DomainService} from '../../../service'; +import {DomainGroup} from '../../../model/domaingroup'; +import {ModalComponent} from '../../../shared'; + +@Component({ + selector: 'app-domain-groups', + templateUrl: './domain-groups.component.html', + styleUrls: ['./domain-groups.component.css'] +}) +export class DomainGroupsComponent implements OnInit { + + + + public groups: DomainGroup[] = []; + public domainsRowVisible: boolean[] = [] + + constructor(private domainService: DomainService) { } + + ngOnInit(): void { + this.refresh(); + } + + public clickTableRow(i: number) { + this.domainsRowVisible[i] = !this.domainsRowVisible[i]; + } + + public deleteDomainGroup(id: number) { + return this.domainService.deleteDomainGroup(id).subscribe( _ => { + console.log(`Group ${id} deleted`); + this.refresh(); + }) + } + + public refresh() { + this.domainService.getAllDomainGroups().subscribe(data => { + this.groups = data; + }) + } + + + +} diff --git a/src/app/appmarket/domains/domain/domain.component.html b/src/app/appmarket/domains/domain/domain.component.html index c25c5a2e2b6abbae42645004f1aa2d6fbfc4f90b..da14d44c6167a556a5445c474f9a41ade38c4bad 100644 --- a/src/app/appmarket/domains/domain/domain.component.html +++ b/src/app/appmarket/domains/domain/domain.component.html @@ -228,6 +228,30 @@ + <div class="panel panel-default" *ngIf="isInMode(ComponentMode.VIEW) && !authService.hasRole('ROLE_OPERATOR')"> + <div class="panel-heading">{{ 'DOMAINS.LIST.GROUP' | translate }}</div> + <div class="panel-body"> + <table class="table table-hover table-condensed" aria-describedby="Domain group list"> + <thead> + <tr> + <th scope="col">{{ 'DOMAINS.LIST.DOMAIN_NAME' | translate }}</th> + <th scope="col">{{ 'DOMAINS.LIST.DOMAIN_CODE_NAME' | translate }}</th> + </tr> + </thead> + + <tbody> + <ng-template ngFor let-group [ngForOf]="domain.groups"> + <tr> + <td>{{group.name}}</td> + <td>{{group.codename}}</td> + </tr> + </ng-template> + + </tbody> + </table> + </div> + </div> + <button *ngIf="!isInMode(ComponentMode.VIEW)" type="submit" class="btn btn-default" [disabled]="!domainForm.form.valid">{{ 'DOMAIN_DETAILS.SUBMIT_BUTTON' | translate }}</button> </form> diff --git a/src/app/appmarket/domains/domains.module.ts b/src/app/appmarket/domains/domains.module.ts index 9bad7541b88ef601bb99847222d23ebf9827a84b..208c185fee6cd4d63c269b190ee0fdb060f23824 100644 --- a/src/app/appmarket/domains/domains.module.ts +++ b/src/app/appmarket/domains/domains.module.ts @@ -18,6 +18,11 @@ import {SearchDomainPipe} from './domain-search.pipe'; import {NgxPaginationModule} from 'ngx-pagination'; import {TableModule} from 'primeng/table'; import {CheckboxModule} from 'primeng/checkbox'; +import { DomainGroupsComponent } from './domain-groups/domain-groups.component'; +import { DomainGroupViewComponent } from './domain-group-view/domain-group-view.component'; +import {DropdownModule} from 'primeng/dropdown'; +import {MultiSelectModule} from 'primeng/multiselect'; +import { RemovalConfirmationModalComponent } from './modals/removal-confirmation-modal/removal-confirmation-modal.component'; @@ -25,7 +30,10 @@ import {CheckboxModule} from 'primeng/checkbox'; declarations: [ DomainsListComponent, DomainComponent, - SearchDomainPipe + SearchDomainPipe, + DomainGroupsComponent, + DomainGroupViewComponent, + RemovalConfirmationModalComponent, ], imports: [ CommonModule, @@ -38,7 +46,9 @@ import {CheckboxModule} from 'primeng/checkbox'; InputTextModule, NgxPaginationModule, TableModule, - CheckboxModule + CheckboxModule, + DropdownModule, + MultiSelectModule ], exports: [ DomainsListComponent, diff --git a/src/app/appmarket/domains/domains.routes.ts b/src/app/appmarket/domains/domains.routes.ts index 917e08576c16a9d141fa5c8b6a58bd7033bfc1cd..6b9fe3e00d4a9343316563c573f2909090fa0c7b 100644 --- a/src/app/appmarket/domains/domains.routes.ts +++ b/src/app/appmarket/domains/domains.routes.ts @@ -1,8 +1,15 @@ import {Route} from '@angular/router'; -import {DomainsListComponent, DomainComponent} from './index'; +import {DomainComponent, DomainsListComponent} from './index'; import {AuthGuard} from '../../auth/auth.guard'; import {RoleGuard} from '../../auth/role.guard'; -import { ComponentMode } from '../../shared/common/componentmode'; +import {ComponentMode} from '../../shared/common/componentmode'; +import {DomainuploadComponent} from '../bulkDeployment/domainDeployment/domainupload/domainupload.component'; +import {DomainsummaryComponent} from '../bulkDeployment/domainDeployment/domainsummary/domainsummary.component'; +import {DomainNavigatorComponent} from '../bulkDeployment/domainDeployment/domainnavigator/domain-navigator.component'; +import {DomainGroupsComponent} from './domain-groups/domain-groups.component'; +import {DomainGroupViewComponent} from './domain-group-view/domain-group-view.component'; +import {BulkDomainListComponent} from '../bulkDeployment/bulk-domain-list/bulk-domain-list.component'; +import {BulkViewComponent} from '../bulkDeployment/bulk-view/bulk-view.component'; export const DomainsRoutes: Route[] = [ {path: 'admin/domains', component: DomainsListComponent, canActivate: [AuthGuard, RoleGuard], @@ -12,6 +19,20 @@ export const DomainsRoutes: Route[] = [ {path: 'admin/domains/view/:id', component: DomainComponent, canActivate: [AuthGuard, RoleGuard], data: {mode: ComponentMode.VIEW, roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_DOMAIN_ADMIN', 'ROLE_OPERATOR']}}, {path: 'admin/domains/edit/:id', component: DomainComponent, canActivate: [AuthGuard, RoleGuard], - data: {mode: ComponentMode.EDIT, roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']}} - + data: {mode: ComponentMode.EDIT, roles: ['ROLE_SYSTEM_ADMIN', 'ROLE_OPERATOR']}}, + { path: 'admin/domains/deploy', component: DomainNavigatorComponent, children: [ + {path: '', redirectTo: 'upload', pathMatch: 'full'}, + {path: 'upload', component: DomainuploadComponent}, + {path: 'summary', component: DomainsummaryComponent} + ]}, + {path: 'admin/domains/groups', component: DomainGroupsComponent, canActivate: [AuthGuard, RoleGuard], + data: {roles: ['ROLE_SYSTEM_ADMIN']}}, + {path: 'admin/domains/groups/add', component: DomainGroupViewComponent, canActivate: [AuthGuard, RoleGuard], + data: {mode: ComponentMode.CREATE, roles: ['ROLE_SYSTEM_ADMIN']}}, + {path: 'admin/domains/groups/:id', component: DomainGroupViewComponent, canActivate: [AuthGuard, RoleGuard], + data: {mode: ComponentMode.VIEW, roles: ['ROLE_SYSTEM_ADMIN']}}, + {path: 'admin/domains/bulks', component: BulkDomainListComponent, canActivate: [AuthGuard, RoleGuard], + data: {roles: ['ROLE_SYSTEM_ADMIN']}}, + {path: 'admin/domains/bulks/:id', component: BulkViewComponent, canActivate: [AuthGuard, RoleGuard], + data: {roles: ['ROLE_SYSTEM_ADMIN']}} ]; diff --git a/src/app/appmarket/domains/list/domainslist.component.html b/src/app/appmarket/domains/list/domainslist.component.html index 9329f7f051eb013b3c0d86c8ad379d99b7f5dc2b..ccc499ade51c152f3054e32f8a9a750e17f78717 100644 --- a/src/app/appmarket/domains/list/domainslist.component.html +++ b/src/app/appmarket/domains/list/domainslist.component.html @@ -63,7 +63,10 @@ class="">{{ 'DOMAINS.EDIT_BUTTON' | translate }}</a> </li> <li><a *roles="['ROLE_SYSTEM_ADMIN']" (click)="$event.stopPropagation(); changeState(domain)" - class="">{{ getStateLabel(domain?.active) }}</a> + class="">{{ getStateLabel(domain?.active) }}</a> + </li> + <li><a *roles="['ROLE_SYSTEM_ADMIN']" (click)="$event.stopPropagation(); openRemovalModal(domain)" + class="">{{ 'DOMAINS.DELETE_BUTTON' | translate }}</a> </li> </ul> </span> @@ -80,3 +83,5 @@ screenReaderCurrentLabel="{{ 'PAGINATION.SCREEN_READER.CURRENT' | translate }}"></pagination-controls> </div> + +<app-removal-confirmation-modal [domain]="domainToRemove" (onConfirm)="softRemoveDomain(domainToRemove.id)"></app-removal-confirmation-modal> diff --git a/src/app/appmarket/domains/list/domainslist.component.ts b/src/app/appmarket/domains/list/domainslist.component.ts index cd7bc16a9c28d28800515a846078912a70e732a9..c3eb612b457da380203c4a31f9b9eb0383b635c4 100644 --- a/src/app/appmarket/domains/list/domainslist.component.ts +++ b/src/app/appmarket/domains/list/domainslist.component.ts @@ -6,6 +6,9 @@ import {Component, OnInit, QueryList, ViewChild, ViewChildren} from '@angular/co import {Observable} from 'rxjs'; import {map} from 'rxjs/operators'; import {TranslateService} from '@ngx-translate/core'; +import { + RemovalConfirmationModalComponent +} from "../modals/removal-confirmation-modal/removal-confirmation-modal.component"; import {ModalComponent} from '../../../shared'; import {SortableHeaderDirective, SortColumn, SortDirection} from '../../../service/sort-domain.directive'; @@ -26,6 +29,11 @@ export class DomainsListComponent implements OnInit { public domains: Observable<Domain[]>; + @ViewChild(RemovalConfirmationModalComponent) + public readonly modal: ModalComponent; + + public domainToRemove: Domain + public searchValue = ''; p: number; @@ -39,8 +47,6 @@ export class DomainsListComponent implements OnInit { @ViewChildren(SortableHeaderDirective) headers: QueryList<SortableHeaderDirective>; - public domainToRemove: Domain - constructor(protected domainService: DomainService, protected authService: AuthService, public translate: TranslateService) { } @@ -134,4 +140,15 @@ export class DomainsListComponent implements OnInit { onSorted(event: any) { console.warn(event) } + public softRemoveDomain(id: number): void { + this.domainService.remove(id, true).subscribe({ + next: () => this.update(), + error: err => console.error(err) + }); + } + + openRemovalModal(domain: Domain) { + this.domainToRemove = domain + this.modal.show() + } } diff --git a/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.css b/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.html b/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.html new file mode 100644 index 0000000000000000000000000000000000000000..8d30d18f677de7fbeb197a549e412208fe27ab4f --- /dev/null +++ b/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.html @@ -0,0 +1,11 @@ +<nmaas-modal styleModal="warning"> + <div class="nmaas-modal-header">{{ 'DOMAIN_DETAILS.MODAL.HEADER' | translate }}</div> + <div class="nmaas-modal-body"> + <p>{{ 'DOMAIN_DETAILS.MODAL.DESCRIPTION' | translate}}</p> + </div> + <div class="nmaas-modal-footer"> + <button role="button" type="button" class="btn btn-success" (click)="removeDomain()">{{ 'DOMAIN_DETAILS.MODAL.BUTTONS.CONFIRM' | translate}}</button> + <button type="button" class="btn btn-primary" (click)="modal.hide()">{{ 'DOMAIN_DETAILS.MODAL.BUTTONS.CANCEL' | translate}}</button> + </div> +</nmaas-modal> + diff --git a/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.spec.ts b/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..13ec4aaf81486cf67066ed27d4b1e5c3c81b5685 --- /dev/null +++ b/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.spec.ts @@ -0,0 +1,34 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RemovalConfirmationModalComponent } from './removal-confirmation-modal.component'; +import {TranslateFakeLoader, TranslateLoader, TranslateModule} from "@ngx-translate/core"; + +describe('RemovalConfirmationModalComponent', () => { + let component: RemovalConfirmationModalComponent; + let fixture: ComponentFixture<RemovalConfirmationModalComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ RemovalConfirmationModalComponent ], + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader + } + }), + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RemovalConfirmationModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.ts b/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..07a0b8a7ce950f69e2f9a51587eec220297afeb7 --- /dev/null +++ b/src/app/appmarket/domains/modals/removal-confirmation-modal/removal-confirmation-modal.component.ts @@ -0,0 +1,34 @@ +import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core'; +import {ModalComponent} from "../../../../shared"; +import {Domain} from "../../../../model/domain"; +import {DomainService} from "../../../../service"; + +@Component({ + selector: 'app-removal-confirmation-modal', + templateUrl: './removal-confirmation-modal.component.html', + styleUrls: ['./removal-confirmation-modal.component.css'] +}) +export class RemovalConfirmationModalComponent { + + @ViewChild(ModalComponent, { static: true }) + public readonly modal: ModalComponent; + + @Input() + public domain: Domain; + + @Output() + public onConfirm: EventEmitter<void> = new EventEmitter<void>(); + + public show() { + this.modal.show(); + } + + public hide() { + this.modal.hide(); + } + + removeDomain() { + this.onConfirm.emit() + this.hide() + } +} diff --git a/src/app/appmarket/users/list/userslist.component.html b/src/app/appmarket/users/list/userslist.component.html index 98178461d93d9e7ecbb06ae696066ef6f74b71d0..9238f1825295d4118fd65ce5faf3769d522ca113 100644 --- a/src/app/appmarket/users/list/userslist.component.html +++ b/src/app/appmarket/users/list/userslist.component.html @@ -1,18 +1,24 @@ -<div class="col-sm-12" *roles="['ROLE_SYSTEM_ADMIN']"> - <nmaas-userslist *ngIf="!isInAddToDomainMode" [users]="allUsers" [allowedModes]="[ComponentMode.VIEW, ComponentMode.DELETE]" (onUserRoleChange)="onUserRoleChange($event)" - (onView)="onUserView($event)" (onModeChange)="onModeChange($event)" (onDelete)="onUserDelete($event)" (onRemoveFromDomain)="onRemoveRole($event)"> - </nmaas-userslist> - <nmaas-userslist *ngIf="isInAddToDomainMode" [users]="usersToAdd" [allowedModes]="[ComponentMode.VIEW, ComponentMode.EDIT]" (onUserRoleChange)="onUserRoleChange($event)" - (onView)="onUserView($event)" (onModeChange)="onModeChange($event)" (onAddToDomain)="onUserAddToDomain($event)"> - </nmaas-userslist> +<div class="col-sm-12" *ngIf="!domainMode" > + <div *roles="['ROLE_SYSTEM_ADMIN']" > + <nmaas-userslist *ngIf="!isInAddToDomainMode" [users]="allUsers" [allowedModes]="[ComponentMode.VIEW, ComponentMode.DELETE]" (onUserRoleChange)="onUserRoleChange($event)" + (onView)="onUserView($event)" (onModeChange)="onModeChange($event)" (onDelete)="onUserDelete($event)" (onRemoveFromDomain)="onRemoveRole($event)"> + </nmaas-userslist> + <nmaas-userslist *ngIf="isInAddToDomainMode" [users]="usersToAdd" [allowedModes]="[ComponentMode.VIEW, ComponentMode.EDIT]" (onUserRoleChange)="onUserRoleChange($event)" + (onView)="onUserView($event)" (onModeChange)="onModeChange($event)" (onAddToDomain)="onUserAddToDomain($event)"> + </nmaas-userslist> + </div> + </div> -<div class="col-sm-12" *roles="['ROLE_DOMAIN_ADMIN']"> - <nmaas-userslist *ngIf="!isInAddToDomainMode" [users]="allUsers" [allowedModes]="[ComponentMode.VIEW, ComponentMode.DELETE]" [domainMode]="true" (onUserRoleChange)="onUserRoleChange($event)" - (onView)="onUserView($event)" (onModeChange)="onModeChange($event)" (onDelete)="onUserDelete($event)" (onRemoveFromDomain)="onRemoveRole($event)"> - </nmaas-userslist> - <nmaas-userslist *ngIf="isInAddToDomainMode" [users]="usersToAdd" [allowedModes]="[ComponentMode.VIEW, ComponentMode.EDIT]" [domainMode]="true" - (onView)="onUserView($event)" (onModeChange)="onModeChange($event)" (onAddToDomain)="onUserAddToDomain($event)" (onUserRoleChange)="onUserRoleChange($event)"> - </nmaas-userslist> +<div class="col-sm-12" *ngIf="domainMode" > + <div *roles="['ROLE_DOMAIN_ADMIN']"> + <nmaas-userslist *ngIf="!isInAddToDomainMode" [users]="allUsers" [allowedModes]="[ComponentMode.VIEW, ComponentMode.DELETE]" [domainMode]="true" (onUserRoleChange)="onUserRoleChange($event)" + (onView)="onUserView($event)" (onModeChange)="onModeChange($event)" (onDelete)="onUserDelete($event)" (onRemoveFromDomain)="onRemoveRole($event)"> + </nmaas-userslist> + <nmaas-userslist *ngIf="isInAddToDomainMode" [users]="usersToAdd" [allowedModes]="[ComponentMode.VIEW, ComponentMode.EDIT]" [domainMode]="true" + (onView)="onUserView($event)" (onModeChange)="onModeChange($event)" (onAddToDomain)="onUserAddToDomain($event)" (onUserRoleChange)="onUserRoleChange($event)"> + </nmaas-userslist> + </div> + </div> diff --git a/src/app/appmarket/users/list/userslist.component.ts b/src/app/appmarket/users/list/userslist.component.ts index 9f578ac07781afb1df96f2012013233fa58bce04..171878c836337b42dabdced9e3a655951099e5e2 100644 --- a/src/app/appmarket/users/list/userslist.component.ts +++ b/src/app/appmarket/users/list/userslist.component.ts @@ -6,7 +6,7 @@ import {UserService} from '../../../service/user.service'; import {UserDataService} from '../../../service/userdata.service'; import {Component, OnInit} from '@angular/core'; import {ComponentMode} from '../../../shared/common/componentmode'; -import {ActivatedRoute, Router} from '@angular/router'; +import {ActivatedRoute, NavigationEnd, Router} from '@angular/router'; import {Location} from '@angular/common'; import {Observable, of} from 'rxjs'; import {map} from 'rxjs/operators'; @@ -20,13 +20,15 @@ export class UsersListComponent implements OnInit { public ComponentMode = ComponentMode; - private domainId: number; + public domainId: number; public allUsers: User[] = []; public usersToAdd: User[] = []; public isInAddToDomainMode = false; + public domainMode = false; + constructor(protected authService: AuthService, protected userService: UserService, protected domainService: DomainService, @@ -39,6 +41,13 @@ export class UsersListComponent implements OnInit { ngOnInit() { this.userDataService.selectedDomainId.subscribe((domainId) => this.update(domainId)); + this.router.events.subscribe(event => { + if (event instanceof NavigationEnd) { + if (event.url.includes('domain')) { + this.domainMode = true; + } + } + }) } public update(domainId: number): void { diff --git a/src/app/model/bulk-deployment.ts b/src/app/model/bulk-deployment.ts new file mode 100644 index 0000000000000000000000000000000000000000..c9e587bebc75cfe5410b36f0f8cec75278764329 --- /dev/null +++ b/src/app/model/bulk-deployment.ts @@ -0,0 +1,16 @@ +import {User} from './user'; +import {BulkReplay, BulkType} from './bulk-replay'; + +export class BulkDeployment { + public id: number; + public creationDate: Date; + public creator: User; + public entries: BulkReplay[] = []; + public state: BulkDeploymentState; + public type: BulkType; +} + +export enum BulkDeploymentState { + PROCESSING, + COMPLETED +} diff --git a/src/app/model/bulk-replay.ts b/src/app/model/bulk-replay.ts new file mode 100644 index 0000000000000000000000000000000000000000..ef846d32707f549335c60d6a54a1c8be909196d1 --- /dev/null +++ b/src/app/model/bulk-replay.ts @@ -0,0 +1,12 @@ +export class BulkReplay { + public successful: boolean; + public created: boolean; + public details: Map<string, string>; + public type: BulkType; +} + +export enum BulkType { + DOMAIN= 'DOMAIN', + APPLICATION = 'APPLICATION', + USER = 'USER', +} diff --git a/src/app/model/domain.ts b/src/app/model/domain.ts index 9ed8e7ba40fc4c2d19c055246f358677cb3e2554..fca050c4f2cbb89151b8eb68866cfdce52abeb75 100644 --- a/src/app/model/domain.ts +++ b/src/app/model/domain.ts @@ -1,6 +1,7 @@ import {DomainDcnDetails} from './domaindcndetails'; import {DomainTechDetails} from './domaintechdetails'; import {DomainApplicationStatePerDomain} from './domainapplicationstateperdomain'; +import {DomainGroup} from './domaingroup'; export class Domain { public id: number = undefined; @@ -10,4 +11,6 @@ export class Domain { public domainDcnDetails: DomainDcnDetails = new DomainDcnDetails(); public domainTechDetails: DomainTechDetails = new DomainTechDetails(); public applicationStatePerDomain: DomainApplicationStatePerDomain[] = []; + public groups: DomainGroup[] = []; + public deleted: boolean; } diff --git a/src/app/model/domaingroup.ts b/src/app/model/domaingroup.ts new file mode 100644 index 0000000000000000000000000000000000000000..b44895c2746063ff2dbe2aeea86762879f98f7a7 --- /dev/null +++ b/src/app/model/domaingroup.ts @@ -0,0 +1,10 @@ +import {Domain} from './domain'; +import {DomainApplicationStatePerDomain} from './domainapplicationstateperdomain'; + +export class DomainGroup { + public id: number = undefined; + public name: string = undefined; + public codename: string = undefined; + public domains: Domain[] = []; + public applicationStatePerDomain: DomainApplicationStatePerDomain[] = []; +} diff --git a/src/app/service/domain.service.ts b/src/app/service/domain.service.ts index caa9625d6a22b425b6b8382c4e7b04fcbb55f13b..3337b1902762aa14cb17b9662962d0b686c4568c 100644 --- a/src/app/service/domain.service.ts +++ b/src/app/service/domain.service.ts @@ -2,12 +2,13 @@ import {Injectable} from '@angular/core'; import {Observable} from 'rxjs'; import {GenericDataService} from './genericdata.service'; -import {HttpClient} from '@angular/common/http' +import {HttpClient, HttpParams} from '@angular/common/http' import {AppConfigService} from './appconfig.service'; import {Id} from '../model'; import {Domain} from '../model/domain'; import {User} from '../model'; +import {DomainGroup} from '../model/domaingroup'; @Injectable({ providedIn: 'root', @@ -60,8 +61,12 @@ export class DomainService extends GenericDataService { return this.patch<Domain, Id>(this.url + domain.id + '/state?active=' + !domain.active, null); } - public remove(domainId: number): Observable<any> { - return this.delete(this.url + domainId); + public remove(domainId: number, softRemove?: boolean): Observable<any> { + let params = new HttpParams() + if (softRemove !== undefined) { + params = params.append("softRemove", softRemove.toString()) + } + return this.http.delete(this.url + domainId, {params}) } public getMyDomains(): Observable<Domain[]> { @@ -79,4 +84,34 @@ export class DomainService extends GenericDataService { public shouldUpdate(): boolean { return this.updateRequiredFlag; } + + // GROUPS + public getAllDomainGroups(): Observable<DomainGroup[]> { + return this.get<DomainGroup[]>(this.url + 'group'); + } + + public getDomainGroup(domainGroupId: number): Observable<DomainGroup> { + return this.get<DomainGroup>(this.url + 'group/' + domainGroupId); + } + + public deleteDomainGroup(domainGroupId: number): Observable<void> { + return this.delete<void>(this.url + 'group/' + domainGroupId); + } + + public addDomainsToGroup(groupCodeName: string, domainIds: number[]): Observable<DomainGroup> { + return this.post(this.url + 'group/' + groupCodeName, domainIds); + } + + public deleteDomainFromGroup(groupId: number, domainId: number): Observable<DomainGroup> { + return this.patch(this.url + 'group/' + groupId, domainId); + } + + public createDomainGroup(domainGroup: DomainGroup): Observable<Id> { + return this.post(this.url + 'group', domainGroup); + } + + public updateDomainGroup(domainGroup: DomainGroup, id: number): Observable<Id> { + return this.put(this.url + 'group/' + id, domainGroup); + } + } diff --git a/src/app/shared/applications/applications.component.ts b/src/app/shared/applications/applications.component.ts index 2f95199cec7c92c3a24f6eca4320e9fa9169d1b3..769aca3069f886f2bdd2007e3496ffe6b1a2ca22 100644 --- a/src/app/shared/applications/applications.component.ts +++ b/src/app/shared/applications/applications.component.ts @@ -95,7 +95,7 @@ export class ApplicationsViewComponent implements OnInit, OnChanges { switch (+this.appView) { case AppViewType.APPLICATION: applications = this.appsService.getAllActiveApplicationBase(); - console.log('get apps update domain') + console.log('get apps update for domain ', this.domainId ) this.updateSelected(); // applications.subscribe((apps) => this.updateSelected(apps)); break; diff --git a/src/app/shared/applications/list/applist.component.ts b/src/app/shared/applications/list/applist.component.ts index 8f1990156b1fdf119de17bbafa419da52905544a..b5db29dd45bdf9a30a59a6d41504c627f2860af2 100644 --- a/src/app/shared/applications/list/applist.component.ts +++ b/src/app/shared/applications/list/applist.component.ts @@ -4,7 +4,7 @@ import {CacheService} from '../../../service'; import {UserDataService} from '../../../service/userdata.service'; import {ListType} from '../../common/listtype'; import {AppViewType} from '../../common/viewtype'; -import {Component, OnInit, Input, ViewEncapsulation} from '@angular/core'; +import {Component, OnInit, Input, ViewEncapsulation, OnChanges, SimpleChanges} from '@angular/core'; import {Observable} from 'rxjs'; import {TranslateService} from '@ngx-translate/core'; import {AppDescription} from '../../../model/app-description'; @@ -18,7 +18,7 @@ import {ApplicationBase} from '../../../model/application-base'; encapsulation: ViewEncapsulation.None, }) -export class AppListComponent implements OnInit { +export class AppListComponent implements OnInit, OnChanges { public ListType = ListType; public AppViewType = AppViewType; @@ -64,6 +64,12 @@ export class AppListComponent implements OnInit { }); } + ngOnChanges(changes: SimpleChanges) { + this.domain.subscribe(data => { + this.domainObject = data; + }); + } + public getDescription(app: ApplicationBase): AppDescription { return app.descriptions.find(val => val.language === this.translate.currentLang); } diff --git a/src/app/shared/applications/list/element/appelement.component.html b/src/app/shared/applications/list/element/appelement.component.html index 7356d548c35eef64c1055256226d9bd7981c9855..064782e5d0b640d37ee14660cba7a192839722d6 100644 --- a/src/app/shared/applications/list/element/appelement.component.html +++ b/src/app/shared/applications/list/element/appelement.component.html @@ -1,5 +1,5 @@ -<div *ngIf="app" class="col-xs-12 col-sm-6 col-md-3 col-lg-3"> +<div *ngIf="app && showAppInList" class="col-xs-12 col-sm-6 col-md-3 col-lg-3"> <div class="thumbnail clickable-tail" [class.subscribed]="selected" [routerLink]="['/apps', app.id]"> <div class = element-container> <div class="image-container-outer"> diff --git a/src/app/shared/applications/list/element/appelement.component.ts b/src/app/shared/applications/list/element/appelement.component.ts index 793f5df4679354864c315e467ea2a57252120608..8eded1fa156878f5533c3c922fb0405c2ab449d9 100644 --- a/src/app/shared/applications/list/element/appelement.component.ts +++ b/src/app/shared/applications/list/element/appelement.component.ts @@ -44,6 +44,8 @@ export class AppElementComponent implements OnInit, OnChanges { @ViewChild(AppInstallModalComponent) public readonly modal: AppInstallModalComponent; + public showAppInList = true; + constructor(public appImagesService: AppImagesService, public appConfigService: AppConfigService, public router: Router, @@ -55,11 +57,15 @@ export class AppElementComponent implements OnInit, OnChanges { if (this.selected === undefined) { this.selected = false; } + if (this.domain) { + this.showAppInList = this.isApplicationEnabledInDomain(); + } } ngOnChanges(changes: SimpleChanges): void { if (this.domain) { this.defaultTooltipOptions.display = !this.isApplicationEnabledInDomain(); + this.showAppInList = this.isApplicationEnabledInDomain(); } } diff --git a/src/app/shared/common/domainfilter/domainfilter.component.spec.ts b/src/app/shared/common/domainfilter/domainfilter.component.spec.ts index 892baf84ce733c4c5b3f193332a404f460070d92..1199fe8e39508404dc0d1bb5ad02cd2674d8fd9f 100644 --- a/src/app/shared/common/domainfilter/domainfilter.component.spec.ts +++ b/src/app/shared/common/domainfilter/domainfilter.component.spec.ts @@ -21,9 +21,11 @@ describe('DomainFilterComponent', () => { name: 'global', codename: 'global', active: true, + deleted: false, domainDcnDetails: undefined, domainTechDetails: undefined, - applicationStatePerDomain: [] + applicationStatePerDomain: [], + groups: [], } const domain1: Domain = { @@ -31,9 +33,11 @@ describe('DomainFilterComponent', () => { name: 'domain one', codename: 'dom-1', active: true, + deleted: false, domainDcnDetails: undefined, domainTechDetails: undefined, - applicationStatePerDomain: [] + applicationStatePerDomain: [], + groups: [], }; const domain2: Domain = { @@ -41,9 +45,11 @@ describe('DomainFilterComponent', () => { name: 'domain two', codename: 'dom-2', active: true, + deleted: false, domainDcnDetails: undefined, domainTechDetails: undefined, - applicationStatePerDomain: [] + applicationStatePerDomain: [], + groups: [], }; beforeEach(waitForAsync(() => { diff --git a/src/app/shared/modal/appinstall/appinstallmodal.component.spec.ts b/src/app/shared/modal/appinstall/appinstallmodal.component.spec.ts index 22af26893856bf33a4a1e914f51ec3367ec6d117..b0b0b1ede0fb13cf298db8a93f89522500263ff6 100644 --- a/src/app/shared/modal/appinstall/appinstallmodal.component.spec.ts +++ b/src/app/shared/modal/appinstall/appinstallmodal.component.spec.ts @@ -41,9 +41,11 @@ describe('AppInstallmodalComponent', () => { name: 'domain one', codename: 'dom-1', active: true, + deleted: false, domainDcnDetails: undefined, domainTechDetails: undefined, - applicationStatePerDomain: [] + applicationStatePerDomain: [], + groups: [], } beforeEach(waitForAsync(() => { diff --git a/src/app/shared/modal/appinstall/appinstallmodal.component.ts b/src/app/shared/modal/appinstall/appinstallmodal.component.ts index 33c84470af8540dc4f035d8f1ec5ea9d70bbdc57..beaa40373513779fbbae9ab50c7e521ef2fbf2bb 100644 --- a/src/app/shared/modal/appinstall/appinstallmodal.component.ts +++ b/src/app/shared/modal/appinstall/appinstallmodal.component.ts @@ -42,6 +42,7 @@ export class AppInstallModalComponent implements OnInit { } ngOnInit() { + this.app.versions = this.app.versions.filter(version => this.getStateAsString(version.state) === 'ACTIVE'); this.app.versions.sort((a, b) => a.version.localeCompare(b.version, undefined, {numeric: true})); this.app.versions.reverse(); this.selectedAppVersion = this.app.versions[0].appVersionId; @@ -76,4 +77,8 @@ export class AppInstallModalComponent implements OnInit { return state; } + public getStateAsString(state: any): string { + return typeof state === 'string' && isNaN(Number(state.toString())) ? state : ApplicationState[state]; + } + } diff --git a/src/app/shared/navbar/navbar.component.html b/src/app/shared/navbar/navbar.component.html index 32221d68902ed478ff2c88cbd22989ffc3b41d03..ed4b015b79dd48f89b131489162e395b9e1636d6 100644 --- a/src/app/shared/navbar/navbar.component.html +++ b/src/app/shared/navbar/navbar.component.html @@ -26,6 +26,29 @@ <li *ngIf="checkUserRole()" class="drop-domain"> <nmaas-domain-filter class="drop-domain"></nmaas-domain-filter> </li> + <li *roles="['ROLE_SYSTEM_ADMIN']" class="divider-vertical"></li> + <li *roles="['ROLE_SYSTEM_ADMIN']" [routerLinkActiveOptions]="{exact:true}" + [routerLinkActive]="['active']" class="dropdown"> + <a aria-expanded="false" aria-haspopup="true" class="dropdown-toggle" data-toggle="dropdown" + role="button">{{'NAVBAR.ADVANCED' | translate}}<strong class="caret"></strong></a> + <ul class="dropdown-menu"> + <li *roles="['ROLE_SYSTEM_ADMIN']"> + <a [routerLink]="['/instances/deploy']">{{ 'NAVBAR.BULK_DEPLOYMENT' | translate }}</a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']"> + <a [routerLink]="['/admin/domains/deploy']">{{ 'NAVBAR.BULK_DEPLOYMENT_DOMAIN' | translate }}</a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']"> + <a [routerLink]="['/admin/domains/groups']">{{ 'NAVBAR.DOMAIN_GROUPS' | translate }}</a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']"> + <a [routerLink]="['/admin/domains/bulks']">{{ 'NAVBAR.PAST_BULK_DOMAIN' | translate }}</a> + </li> + <li *roles="['ROLE_SYSTEM_ADMIN']"> + <a [routerLink]="['/admin/apps/bulks']">{{ 'NAVBAR.PAST_BULK_APP' | translate }}</a> + </li> + </ul> + </li> <li class="divider-vertical"></li> <li *roles="['ROLE_SYSTEM_ADMIN']" [routerLinkActiveOptions]="{exact:true}" [routerLinkActive]="['active']" class="dropdown"> @@ -59,7 +82,7 @@ [routerLink]="['/admin/users']">{{ 'NAVBAR.USERS' | translate }}</a> </li> <li *roles="['ROLE_DOMAIN_ADMIN']"><a - [routerLink]="['/domain/users']">{{ 'NAVBAR.USERS' | translate }}</a> + [routerLink]="['/domain/users']">{{ 'NAVBAR.DOMAIN_USERS' | translate }}</a> </li> <li *roles="['ROLE_SYSTEM_ADMIN']"><a [routerLink]="['/admin/languages']">{{'NAVBAR.LANGUAGES' | translate }}</a>