diff options
author | Mukesh Paliwal <mukesh.paliwal1@huawei.com> | 2021-06-24 11:54:21 +0530 |
---|---|---|
committer | Mukesh Paliwal <mukesh.paliwal1@huawei.com> | 2021-06-24 11:54:21 +0530 |
commit | a5778f6f60d56686656411fbe927ba6ef472a4b4 (patch) | |
tree | b0494235cf7a8b8cc6f7ed5bc25b17981cef4819 /so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src | |
parent | 8f8791fb45cd9c135fa269dc15be2c39ab5817c4 (diff) |
Dynamic BPMN workflow GUI
Issue-ID: SO-3681
Signed-off-by: mukesh.paliwal <mukesh.paliwal1@huawei.com>
Change-Id: Ia9e64dbb12fc45c0da73d67f64abb0ff0147d783
Diffstat (limited to 'so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src')
18 files changed, 1137 insertions, 4 deletions
diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/package.json b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/package.json index fcbeabe..4c3dfbd 100644 --- a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/package.json +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/package.json @@ -27,6 +27,7 @@ "core-js": "^2.5.4", "jquery": "^3.3.1", "ngx-spinner": "^6.1.2", + "node-sass": "^4.14.1", "rxjs": "^6.0.0", "toastr": "^2.1.4", "zone.js": "^0.8.26" diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/app-routing.module.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/app-routing.module.ts index 428998d..ebe0909 100644 --- a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/app-routing.module.ts +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/app-routing.module.ts @@ -24,6 +24,8 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { HomeComponent } from './home/home.component'; import { DetailsComponent } from './details/details.component'; +import { OnboardComponent } from './onboard/onboard.component'; + const routes: Routes = [ { @@ -35,6 +37,10 @@ const routes: Routes = [ // Route to page to show individual process based on ID path: 'details/:id', component: DetailsComponent + }, + { + path: 'onboard', + component: OnboardComponent } ]; diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/app.module.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/app.module.ts index 75be395..eee1398 100644 --- a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/app.module.ts +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/app.module.ts @@ -37,6 +37,8 @@ import { MatFormFieldModule, MatInputModule, MatTableModule, MatTabsModule, MatS import { NgxSpinnerModule } from 'ngx-spinner'; import { RouterModule, Routes } from '@angular/router'; import { APP_BASE_HREF } from '@angular/common'; +import { OnboardComponent } from './onboard/onboard.component'; +import { RecipeComponent } from './recipe/recipe.component' @NgModule({ declarations: [ @@ -44,7 +46,9 @@ import { APP_BASE_HREF } from '@angular/common'; SidebarComponent, TopbarComponent, HomeComponent, - DetailsComponent + DetailsComponent, + OnboardComponent, + RecipeComponent ], imports: [ BrowserModule, @@ -68,6 +72,7 @@ import { APP_BASE_HREF } from '@angular/common'; RouterModule.forRoot([]), ReactiveFormsModule ], + entryComponents: [RecipeComponent], schemas: [ CUSTOM_ELEMENTS_SCHEMA ], diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/data.service.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/data.service.ts index b391672..2e7f1e7 100644 --- a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/data.service.ts +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/data.service.ts @@ -21,7 +21,7 @@ SPDX-License-Identifier: Apache-2.0 */ import { Injectable } from '@angular/core'; -import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; import { BpmnInfraRequest } from './model/bpmnInfraRequest.model'; import { catchError } from 'rxjs/operators'; import { Observable } from 'rxjs'; @@ -37,7 +37,7 @@ import { ActivityInstance } from './model/activityInstance.model'; providedIn: 'root' }) export class DataService { - + httpOptions:any; constructor(private http: HttpClient, private httpErrorHandlerService: HttpErrorHandlerService) { } // HTTP POST call to running Spring Boot application @@ -92,4 +92,65 @@ export class DataService { catchError(this.httpErrorHandlerService.handleError("GET", url)) ); } + + onboardBPMNInfra(formData: any): Observable<Object> { + var url = environment.soMonitoringBackendURL + 'workflowPackages/onboard'; + return this.http.post<any>(url, formData) + .pipe( + catchError(this.httpErrorHandlerService.handleError("POST", url)) + ); + } + + saveServiceRecipe(data: any): Observable<Object> { + this.httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + }) + }; + var url = environment.soMonitoringBackendURL + 'serviceRecipes'; + return this.http.post<any>(url, data, this.httpOptions) + .pipe( + catchError(this.httpErrorHandlerService.handleError("POST", url)) + ); + } + + getServiceRecipe(): Observable<Object> { + this.httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + }) + }; + var url = environment.soMonitoringBackendURL + 'serviceRecipes'; + return this.http.get<any>(url, this.httpOptions) + .pipe( + catchError(this.httpErrorHandlerService.handleError("GET", url)) + ); + } + + getNetworkRecipe(): Observable<Object> { + this.httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + }) + }; + var url = environment.soMonitoringBackendURL + 'networkRecipes'; + return this.http.get<any>(url, this.httpOptions) + .pipe( + catchError(this.httpErrorHandlerService.handleError("GET", url)) + ); + } + + getVnfRecipe(): Observable<Object> { + this.httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + }) + }; + var url = environment.soMonitoringBackendURL + 'vnfRecipes'; + return this.http.get<any>(url, this.httpOptions) + .pipe( + catchError(this.httpErrorHandlerService.handleError("GET", url)) + ); + } + } diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/networkRecipe.model.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/networkRecipe.model.ts new file mode 100644 index 0000000..98a5fb7 --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/networkRecipe.model.ts @@ -0,0 +1,12 @@ +export interface NetworkRecipe { + id: string; + modelName: string; + paramXsd: string; + action: string; + description: string; + orchestrationUri: string; + recipeTimeout: string; + versionStr: string; + serviceType: string; + created: string; +} diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/recipeRequest.model.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/recipeRequest.model.ts new file mode 100644 index 0000000..627246c --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/recipeRequest.model.ts @@ -0,0 +1,9 @@ +export interface RecipeRequest { + modelName: string; + modelVersionId: string; + operation: string; + orchestrationFlow: string; + modelType: string; + orchestrationPackageName: string; + } +
\ No newline at end of file diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/serviceRecipe.model.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/serviceRecipe.model.ts new file mode 100644 index 0000000..67f5e6a --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/serviceRecipe.model.ts @@ -0,0 +1,11 @@ +export interface ServiceRecipe { + id: string; + serviceModelUUID: string; + paramXsd: string; + action: string; + description: string; + orchestrationUri: string; + recipeTimeout: string; + serviceTimeoutInterim: string; + created: string; +} diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/vnfRecipe.model.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/vnfRecipe.model.ts new file mode 100644 index 0000000..4c57524 --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/model/vnfRecipe.model.ts @@ -0,0 +1,13 @@ +export interface VnfRecipe { + id: string; + nfRole: string; + paramXsd: string; + vfModuleId: string; + action: string; + description: string; + orchestrationUri: string; + recipeTimeout: string; + versionStr: string; + serviceType: string; + created: string; + } diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.html b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.html new file mode 100644 index 0000000..249ee7c --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.html @@ -0,0 +1,183 @@ + +<base href="/"> +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> +<link rel="stylesheet" ng-href="./onboard.component.scss"> +<div class="completeForm"> + <div> + <h3>Dynamic Deployment of workflow</h3> + </div> + <div class="uploadwar onboard-box" style="padding: 25px;"> + <label> + Recipe war: + <span style="font-weight: bold;cursor: pointer;margin-left: 5px;" title="bpmn, groovy and java files"> + ? + </span> + </label> + <br> + <input class="" type="file" class="upload-box form-control pad-top-not5" + (change)="beforeUpload($event)" + placeholder="Upload-BPMN-war" required> + <br> + <button (click)="onSubmit()" class="btn btn-primary"> + Upload Recipe + </button> + </div> + <br /> + <!-- Table to display selected fields if data present --> + <div class="example-container mat-elevation-z8"> + <mat-tab-group class="tab-group" (focusChange)="changeTab($event)"> + <mat-tab label="Service Recipe"> + <mat-table [dataSource]="serviceRecipe" matSort> + <ng-container matColumnDef="serviceModelUUID"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Model UUID </mat-header-cell> + <mat-cell *matCellDef="let recipe">{{ recipe.serviceModelUUID }}</mat-cell> + </ng-container> + <ng-container matColumnDef="paramXsd"> + <mat-header-cell *matHeaderCellDef mat-sort-header> ParamXsd </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.paramXsd }} </mat-cell> + </ng-container> + <ng-container matColumnDef="action"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Action </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.action }} </mat-cell> + </ng-container> + <ng-container matColumnDef="orchestrationUri"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Orchestration Uri </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.orchestrationUri }} </mat-cell> + </ng-container> + <ng-container matColumnDef="description"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Description </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.description }} </mat-cell> + </ng-container> + <ng-container matColumnDef="recipeTimeout"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Recipe Timeout </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.recipeTimeout }} </mat-cell> + </ng-container> + <ng-container matColumnDef="serviceTimeoutInterim"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Timeout Interim </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.serviceTimeoutInterim }} </mat-cell> + </ng-container> + <ng-container matColumnDef="created"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Created </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.created }} </mat-cell> + </ng-container> + <ng-container matColumnDef="operation"> + <mat-header-cell *matHeaderCellDef>Operation</mat-header-cell> + <mat-cell *matCellDef="let recipe"> + <button class="btn btn-primary" (click)="addRecipe(recipe)">Modify</button> + </mat-cell> + </ng-container> + <mat-header-row *matHeaderRowDef="displayedServiceColumns"></mat-header-row> + <mat-row *matRowDef="let row; columns: displayedServiceColumns;"></mat-row> + </mat-table> + <mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons> + </mat-paginator> + </mat-tab> + <mat-tab label="Network Recipe"> + <mat-table [dataSource]="networkRecipe" matSort> + <ng-container matColumnDef="modelName"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Model Name </mat-header-cell> + <mat-cell *matCellDef="let recipe">{{ recipe.modelName }}</mat-cell> + </ng-container> + <ng-container matColumnDef="paramXsd"> + <mat-header-cell *matHeaderCellDef mat-sort-header> ParamXsd </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.paramXsd }} </mat-cell> + </ng-container> + <ng-container matColumnDef="action"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Action </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.action }} </mat-cell> + </ng-container> + <ng-container matColumnDef="description"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Description </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.description }} </mat-cell> + </ng-container> + <ng-container matColumnDef="orchestrationUri"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Orchestration Uri </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.orchestrationUri }} </mat-cell> + </ng-container> + <ng-container matColumnDef="recipeTimeout"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Recipe Timeout </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.recipeTimeout }} </mat-cell> + </ng-container> + <ng-container matColumnDef="versionStr"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Version </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.versionStr }} </mat-cell> + </ng-container> + <ng-container matColumnDef="serviceType"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Type </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.serviceType }} </mat-cell> + </ng-container> + <ng-container matColumnDef="created"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Created </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.created }} </mat-cell> + </ng-container> + <ng-container matColumnDef="operation"> + <mat-header-cell *matHeaderCellDef>Operation</mat-header-cell> + <mat-cell *matCellDef="let recipe"> + <button class="btn btn-primary" (click)="addRecipe(recipe)">Modify</button> + </mat-cell> + </ng-container> + <mat-header-row *matHeaderRowDef="displayedNetworkColumns"></mat-header-row> + <mat-row *matRowDef="let row; columns: displayedNetworkColumns;"></mat-row> + </mat-table> + <mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons> + </mat-paginator> + </mat-tab> + <mat-tab label="VNF Recipe"> + <mat-table [dataSource]="vnfRecipe" matSort> + <ng-container matColumnDef="nfRole"> + <mat-header-cell *matHeaderCellDef mat-sort-header> NF Role </mat-header-cell> + <mat-cell *matCellDef="let recipe">{{ recipe.nfRole }}</mat-cell> + </ng-container> + <ng-container matColumnDef="paramXsd"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Param Xsd </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.paramXsd }} </mat-cell> + </ng-container> + <ng-container matColumnDef="vfModuleId"> + <mat-header-cell *matHeaderCellDef mat-sort-header> VFM Id </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.vfModuleId }} </mat-cell> + </ng-container> + <ng-container matColumnDef="action"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Action </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.action }} </mat-cell> + </ng-container> + <ng-container matColumnDef="description"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Description </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.description }} </mat-cell> + </ng-container> + <ng-container matColumnDef="orchestrationUri"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Orchestration Uri </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.orchestrationUri }} </mat-cell> + </ng-container> + <ng-container matColumnDef="recipeTimeout"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Recipe Timeout </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.recipeTimeout }} </mat-cell> + </ng-container> + <ng-container matColumnDef="versionStr"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Version </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.versionStr }} </mat-cell> + </ng-container> + <ng-container matColumnDef="serviceType"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Type </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.serviceType }} </mat-cell> + </ng-container> + <ng-container matColumnDef="created"> + <mat-header-cell *matHeaderCellDef mat-sort-header> Created </mat-header-cell> + <mat-cell *matCellDef="let recipe"> {{ recipe.created }} </mat-cell> + </ng-container> + <ng-container matColumnDef="operation"> + <mat-header-cell *matHeaderCellDef>Operation</mat-header-cell> + <mat-cell *matCellDef="let recipe"> + <button class="btn btn-primary" (click)="addRecipe(recipe)">Modify</button> + </mat-cell> + </ng-container> + <mat-header-row *matHeaderRowDef="displayedVnfColumns"></mat-header-row> + <mat-row *matRowDef="let row; columns: displayedVnfColumns;"></mat-row> + </mat-table> + <mat-paginator [pageSizeOptions]="pageSizeOptions" showFirstLastButtons> + </mat-paginator> + </mat-tab> + </mat-tab-group> + </div> +</div> +<ngx-spinner bdColor="rgba(51, 51, 51, 0.8)" size="large" color="#00285f" type="ball-spin-clockwise-fade-rotating"></ngx-spinner> +<router-outlet></router-outlet> diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.scss b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.scss new file mode 100644 index 0000000..7eff84f --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.scss @@ -0,0 +1,178 @@ +.uploadwar { + background-color: white; + padding: 10px; + box-shadow: 0 5px 5px -3px rgba(0,0,0,.2), 0 8px 10px 1px rgba(0,0,0,.14), 0 3px 14px 2px rgba(0,0,0,.12); + } + .mat-form-field-appearance-legacy .mat-form-field-wrapper { + padding-bottom: 1.25em; + font-family: 'Montserrat', sans-serif; + } + .width-40p{ + width: 40%!important; + } + + .selectFilter { + width: 120px; + } + + .valueInput { + width: 505px; + margin: 0; + } + .dispaly-flex{ + display: flex; + } + .selectFilter.mat-select.ng-tns-c5-1.ng-star-inserted { + font-family: 'Montserrat', sans-serif; + font-size: 17px; + } + + .mat-form-field-flex .valueInput { + font-family: 'Montserrat', sans-serif; + font-size: 17px; + } + + .upload-box{ + border: 1px solid gainsboro; + padding: 3px !important; + width: 30%; + margin-right: 15px; +} + +.label-txt{ + line-height: 45px; + text-align: left; +} + +.container-home{ + display: flex; + flex-direction: row; + /* align-items: center; */ + justify-content: center; +} +.pad-top{ + padding-top: 12px; +} + +// .onboard-box{ +// display: flex; +// flex-direction: row; +// align-items: center; +// justify-content: center; +// } + +.selectHour, +.selectMinute { + margin-left: 30px; + width: 100px; +} +.pad-top-not5{ + padding-top: 0.5%; +} +.searchArea { + background-color: white; + padding: 10px; + height: 345px; + box-shadow: 0 5px 5px -3px rgba(0,0,0,.2), 0 8px 10px 1px rgba(0,0,0,.14), 0 3px 14px 2px rgba(0,0,0,.12); +} + +.mat-form-field-appearance-legacy .mat-form-field-wrapper { + padding-bottom: 1.25em; + font-family: 'Montserrat', sans-serif; +} + +.selectFilter { + width: 120px; +} + +.valueInput { + width: 400px; + margin-left: 30px; +} + +.selectFilter.mat-select.ng-tns-c5-1.ng-star-inserted { + font-family: 'Montserrat', sans-serif; + font-size: 17px; +} + +.mat-form-field-flex .valueInput { + font-family: 'Montserrat', sans-serif; + font-size: 17px; +} + +.mat-primary .mat-option.mat-selected:not(.mat-option-disabled) { + color: #00285F; +} + +.mat-option { + font-size: 17px; + line-height: 3em; + height: 3em; + font-family: 'Montserrat', sans-serif; +} + +.mat-select-arrow { + color: #00285F; +} + +.mat-form-field.mat-focused.mat-primary .mat-select-arrow { + color: #00285F; +} + +.mat-form-field-appearance-legacy .mat-form-field-underline { + color: #00285F; +} + +.fa { + float: left; + width: 120px; + padding: 10px; + background: #2196F3; + color: white; + height: 40px; + font-size: 17px; + border: 1px solid grey; + border-left: none; + cursor: pointer; +} + +form.example button:hover { + background: #0b7dda; +} + +form.example::after { + content: ""; + clear: both; + display: table; +} + +.formFields { + display: inline-flex; +} + +.endDate, +.startDate { + margin-left: 90px; + width: 140px; +} + +.selectHour, +.selectMinute { + margin-left: 30px; + width: 100px; +} + +#servStats { + background-color: white; + padding: 10px; + font-size: 17px; + font-family: 'Montserrat', sans-serif; +} + +.statsTable { + td { + padding: 12px 80px 12px 12px; + text-align: left; + border-bottom: 1px solid #ccc; + } +} diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.spec.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.spec.ts new file mode 100644 index 0000000..a4e78e0 --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OnboardComponent } from './onboard.component'; + +describe('OnboardComponent', () => { + let component: OnboardComponent; + let fixture: ComponentFixture<OnboardComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ OnboardComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(OnboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.ts new file mode 100644 index 0000000..e7e1d83 --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.component.ts @@ -0,0 +1,180 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { DataService } from '../data.service'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { HttpClient } from '@angular/common/http'; +import { ToastrNotificationService } from '../toastr-notification-service.service'; +import { MatPaginator, MatSort, MatTableDataSource } from '@angular/material'; +import { ServiceRecipe } from '../model/serviceRecipe.model'; +import { NetworkRecipe } from '../model/networkRecipe.model'; +import { VnfRecipe } from '../model/vnfRecipe.model'; +import { Constants } from './onboard.constant'; +import { MatDialog } from '@angular/material/dialog'; +import { RecipeComponent } from '../recipe/recipe.component'; + +@Component({ + selector: 'app-onboard', + templateUrl: './onboard.component.html', + styleUrls: ['./onboard.component.scss'] +}) +export class OnboardComponent implements OnInit { + + constructor(private data: DataService, private spinner: NgxSpinnerService, private http: HttpClient, + private popup: ToastrNotificationService, public dialog: MatDialog) { } + + fileList = []; + serviceRecipe: MatTableDataSource<ServiceRecipe>; + networkRecipe: MatTableDataSource<NetworkRecipe>; + vnfRecipe: MatTableDataSource<VnfRecipe>; + + displayedServiceColumns = Constants.DISPLAYED_COLUMNS_SERVICE; + displayedNetworkColumns = Constants.DISPLAYED_COLUMNS_NETWORK; + displayedVnfColumns = Constants.DISPLAYED_COLUMNS_VNF; + pageSizeOptions = Constants.DEFAULT_PAGE_SIZE_OPTIONS; + currentTab:String = 'Service Recipe' + + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + ngOnInit() { + this.createFormControls(); + this.getServiceRecipe() + } + + createFormControls() { + let tabDiv = document.getElementsByClassName('mat-tab-label-container') + var bDiv = document.createElement("div"); + bDiv.className = 'addRecipe'; + var node = document.createElement("button"); + node.className = 'btn btn-primary'; + node.title = 'Click here to add recipes or update existing recipes in the SO-Catalog'; + node.addEventListener("click", this.addRecipe.bind(this)); + var textnode = document.createTextNode("Add Recipe"); + node.appendChild(textnode); + bDiv.appendChild(node) + tabDiv[0].appendChild(bDiv); + } + + changeTab(evt: any) { + if (evt.tab) { + this.currentTab = evt.tab.textLabel + } + if (this.currentTab === 'Network Recipe') { + this.getNetworkRecipe() + } else if (this.currentTab === 'VNF Recipe') { + this.getVnfRecipe() + } else { + this.getServiceRecipe() + } + + } + + addRecipe(evt: any) { + let isAdd = false; + if (evt.currentTarget) { + isAdd = true + } + const dialogRef = this.dialog.open(RecipeComponent, { + height: '500px', + width: '600px', + data: { + tab: this.currentTab, + addNew: isAdd, + fData: isAdd ? {} : evt + } + }); + dialogRef.afterClosed().subscribe(result => { + console.log('The dialog was closed'); + this.changeTab(this.currentTab) + }); + } + + getServiceRecipe () { + this.data.getServiceRecipe() + .subscribe((data: any) => { + console.log(JSON.stringify(data)); + this.serviceRecipe = new MatTableDataSource<ServiceRecipe>(data.serviceRecipes); + this.serviceRecipe.sort = this.sort; + this.serviceRecipe.paginator = this.paginator; + this.serviceRecipe.paginator.firstPage(); + },error => { + console.log(error); + console.log("Unable to store bpmn data, Error code:" + error.status); + this.spinner.hide(); + }); + } + + getNetworkRecipe () { + this.data.getNetworkRecipe() + .subscribe((data: any) => { + this.networkRecipe = new MatTableDataSource<NetworkRecipe>(data.networkRecipes); + console.log(JSON.stringify(data)); + },error => { + console.log(error); + console.log("Unable to store bpmn data, Error code:" + error.status); + this.spinner.hide(); + }); + } + + getVnfRecipe () { + this.data.getVnfRecipe() + .subscribe((data: any) => { + this.vnfRecipe = new MatTableDataSource<VnfRecipe>(data.vnfRecipes); + console.log(JSON.stringify(data)); + },error => { + console.log(error); + console.log("Unable to store bpmn data, Error code:" + error.status); + this.spinner.hide(); + }); + } + + onSubmit() { + if(this.fileList.length > 0) { + this.handleUpload(); + } else { + this.popup.error("Please select atleast one file."); + } + } + + beforeUpload = (evt: any): boolean => { + this.fileList = []; + if(evt) { + let file = evt.currentTarget.files[0]; + if(file.name.includes(".war")) { + this.fileList = this.fileList.concat(file); + } else { + this.popup.error("Invalid file format."); + } + } + return false; + }; + + handleUpload(): void { + if (this.fileList.length == 0) { + return; + } + this.spinner.show() + const formData = new FormData(); + this.fileList.forEach((file: any) => { + formData.append('file', file, file.name); + }); + this.data.onboardBPMNInfra(formData) + .subscribe((data: any) => { + this.spinner.hide(); + console.log(JSON.stringify(data)); + if(data != null) { + if(data.result == "true") { + this.popup.info(data.message); + } else if(data.errMsg) { + this.popup.error(data.errMsg); + } else { + this.popup.error(data.message); + } + } + },error => { + console.log(error); + this.popup.error("Unable to upload bpmn file, Error code:" + error.status); + this.spinner.hide(); + }); + } +} + diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.constant.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.constant.ts new file mode 100644 index 0000000..b83f32c --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/onboard/onboard.constant.ts @@ -0,0 +1,31 @@ + +/** +============LICENSE_START======================================================= + Copyright (C) 2019 Ericsson. All rights reserved. +================================================================================ +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and + limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +============LICENSE_END========================================================= + +@authors: andrei.barcovschi@ericsson.com, waqas.ikram@ericsson.com +*/ + +export class Constants { + + public static DISPLAYED_COLUMNS_SERVICE = ['serviceModelUUID', 'paramXsd', 'action', 'description', 'orchestrationUri', 'recipeTimeout', 'serviceTimeoutInterim', 'created', 'operation']; + public static DISPLAYED_COLUMNS_NETWORK = ['modelName', 'paramXsd', 'action', 'description', 'orchestrationUri', 'recipeTimeout', 'versionStr', 'serviceType', 'created', 'operation']; + public static DISPLAYED_COLUMNS_VNF = ['nfRole', 'paramXsd', 'vfModuleId', 'action', 'description', 'orchestrationUri', 'recipeTimeout', 'versionStr', 'serviceType', 'created', 'operation']; + + public static DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50, 100]; +} diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/recipe/recipe.component.html b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/recipe/recipe.component.html new file mode 100644 index 0000000..eddae28 --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/recipe/recipe.component.html @@ -0,0 +1,86 @@ +<h1 mat-dialog-title style="font-size: 25px;">{{this.parentData.addNew ? 'Add Recipe' : 'Update Recipe'}}</h1> +<hr> +<div mat-dialog-content> + <div class="container"> + <form novalidate + [formGroup]="myform" + (ngSubmit)="onSubmit()"> + <div class="form-group" + *ngIf="modeType == 'Network Recipe'" + [ngClass]="{ + 'has-danger': modelName.invalid && (modelName.dirty || modelName.touched), + 'has-success': modelName.valid && (modelName.dirty || modelName.touched) + }"> + <label>Model Name</label> + <input type="text" + class="form-control" + formControlName="modelName" + required> + </div> + <div class="form-group" + *ngIf="modeType == 'VNF Recipe'" + [ngClass]="{ + 'has-danger': modelName.invalid && (modelName.dirty || modelName.touched), + 'has-success': modelName.valid && (modelName.dirty || modelName.touched) + }"> + <label>NF Role</label> + <input type="text" + class="form-control" + formControlName="nfRole" + required> + </div> + <div class="form-group" + [ngClass]="{ + 'has-danger': modelVersionId.invalid && (modelVersionId.dirty || modelVersionId.touched), + 'has-success': modelVersionId.valid && (modelVersionId.dirty || modelVersionId.touched) + }"> + <label>{{modeType == 'Service Recipe' ? 'Service Model UUID' : 'Model Versison ID'}}</label> + <input type="text" + class="form-control" + formControlName="modelVersionId" + required> + </div> + <div class="form-group" + [ngClass]="{ + 'has-danger': operation.invalid && (operation.dirty || operation.touched), + 'has-success': operation.valid && (operation.dirty || operation.touched) + }"> + <label>Action</label> + <input type="operation" + class="form-control" + formControlName="operation" + required> + </div> + <div class="form-group" + [ngClass]="{ + 'has-danger': orchestrationFlow.invalid && (orchestrationFlow.dirty || orchestrationFlow.touched), + 'has-success': orchestrationFlow.valid && (orchestrationFlow.dirty || orchestrationFlow.touched) + }"> + <label>Orchestration URI</label> + <input type="orchestrationFlow" + class="form-control" + formControlName="orchestrationFlow" + required> + </div> + <div class="form-group" + *ngIf="modeType == 'Service Recipe'" + [ngClass]="{ + 'has-danger': modelName.invalid && (modelName.dirty || modelName.touched), + 'has-success': modelName.valid && (modelName.dirty || modelName.touched) + }"> + <label>Recipe Timeout</label> + <input type="text" + class="form-control" + formControlName="recipeTimeout" + required> + </div> + <button style="margin-right: 10px;" + class="btn btn-primary" + (click)="closeDialog($event)">Cancel + </button> + <button type="submit" + class="btn btn-primary">Submit + </button> + </form> + </div> +</div>
\ No newline at end of file diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/recipe/recipe.component.scss b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/recipe/recipe.component.scss new file mode 100644 index 0000000..bd1fe48 --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/recipe/recipe.component.scss @@ -0,0 +1,190 @@ +.uploadwar { + background-color: white; + padding: 10px; + box-shadow: 0 5px 5px -3px rgba(0,0,0,.2), 0 8px 10px 1px rgba(0,0,0,.14), 0 3px 14px 2px rgba(0,0,0,.12); + } + .mat-form-field-appearance-legacy .mat-form-field-wrapper { + padding-bottom: 1.25em; + font-family: 'Montserrat', sans-serif; + } + .width-40p{ + width: 40%!important; + } + + .selectFilter { + width: 120px; + } + + .valueInput { + width: 505px; + margin: 0; + } + .dispaly-flex{ + display: flex; + } + .selectFilter.mat-select.ng-tns-c5-1.ng-star-inserted { + font-family: 'Montserrat', sans-serif; + font-size: 17px; + } + + .mat-form-field-flex .valueInput { + font-family: 'Montserrat', sans-serif; + font-size: 17px; + } + + .upload-box{ + border: 1px solid gainsboro; + padding: 8px; + width: 100%; +} + +.label-txt{ + line-height: 45px; + text-align: left; +} + +.container-home{ + display: flex; + flex-direction: row; + /* align-items: center; */ + justify-content: center; +} +.pad-top{ + padding-top: 12px; +} + +// .onboard-box{ +// display: flex; +// flex-direction: row; +// align-items: center; +// justify-content: center; +// } + +.selectHour, +.selectMinute { + margin-left: 30px; + width: 100px; +} +.pad-top-not5{ + padding-top: 0.5%; +} +.searchArea { + background-color: white; + padding: 10px; + height: 345px; + box-shadow: 0 5px 5px -3px rgba(0,0,0,.2), 0 8px 10px 1px rgba(0,0,0,.14), 0 3px 14px 2px rgba(0,0,0,.12); +} + +.mat-form-field-appearance-legacy .mat-form-field-wrapper { + padding-bottom: 1.25em; + font-family: 'Montserrat', sans-serif; +} + +.selectFilter { + width: 120px; +} + +.valueInput { + width: 400px; + margin-left: 30px; +} + +.selectFilter.mat-select.ng-tns-c5-1.ng-star-inserted { + font-family: 'Montserrat', sans-serif; + font-size: 17px; +} + +.mat-form-field-flex .valueInput { + font-family: 'Montserrat', sans-serif; + font-size: 17px; +} + +.mat-primary .mat-option.mat-selected:not(.mat-option-disabled) { + color: #00285F; +} + +.mat-option { + font-size: 17px; + line-height: 3em; + height: 3em; + font-family: 'Montserrat', sans-serif; +} + +.mat-select-arrow { + color: #00285F; +} + +.mat-form-field.mat-focused.mat-primary .mat-select-arrow { + color: #00285F; +} + +.mat-form-field-appearance-legacy .mat-form-field-underline { + color: #00285F; +} + +.fa { + float: left; + width: 120px; + padding: 10px; + background: #2196F3; + color: white; + height: 40px; + font-size: 17px; + border: 1px solid grey; + border-left: none; + cursor: pointer; +} + +form.example button:hover { + background: #0b7dda; +} + +form.example::after { + content: ""; + clear: both; + display: table; +} + +.formFields { + display: inline-flex; +} + +.endDate, +.startDate { + margin-left: 90px; + width: 140px; +} + +.selectHour, +.selectMinute { + margin-left: 30px; + width: 100px; +} + +#servStats { + background-color: white; + padding: 10px; + font-size: 17px; + font-family: 'Montserrat', sans-serif; +} + +.statsTable { + td { + padding: 12px 80px 12px 12px; + text-align: left; + border-bottom: 1px solid #ccc; + } +} + +.mat-tab-label-container { + button { + height: 15px !important; + cursor: pointer !important; + height: 36px; + margin: 5px 10px; + } +} + +input.ng-invalid.ng-touched { + border: 1px solid red; +} diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/recipe/recipe.component.ts b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/recipe/recipe.component.ts new file mode 100644 index 0000000..cbe8a72 --- /dev/null +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/recipe/recipe.component.ts @@ -0,0 +1,130 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { DataService } from '../data.service'; +import { NgxSpinnerService } from 'ngx-spinner'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { HttpClient } from '@angular/common/http'; +import { ToastrNotificationService } from '../toastr-notification-service.service'; +import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog'; + +@Component({ + selector: 'app-recipe', + templateUrl: './recipe.component.html', + styleUrls: ['./recipe.component.scss'] +}) +export class RecipeComponent implements OnInit { + + constructor(private data: DataService, private spinner: NgxSpinnerService, private http: HttpClient, + private popup: ToastrNotificationService,public dialogRef: MatDialogRef<RecipeComponent>, + @Inject(MAT_DIALOG_DATA) public parentData: any) { } + + fileList = []; + myform: FormGroup; + modelName: FormControl; + recipeTimeout: FormControl; + nfRole: FormControl; + modelVersionId: FormControl; + operation: FormControl; + orchestrationFlow: FormControl; + isCancel: boolean = false; + modeType: string = this.parentData.tab; + + ngOnInit() { + this.createFormControls(); + this.createForm(); + } + + createFormControls() { + let dd: any; + if (!this.parentData.addNew) { + let dd = this.parentData.fData; + this.modelName = new FormControl(dd.modelName); + if (dd.tab === 'Service Recipe') { + this.modelVersionId = new FormControl(dd.serviceModelUUID); + } else { + this.modelVersionId = new FormControl(dd.versionStr); + } + if (dd.tab === 'Network Recipe') { + this.nfRole = new FormControl(dd.nfRole,); + } else { + this.nfRole = new FormControl('',); + } + this.recipeTimeout = new FormControl(dd.recipeTimeout); + this.operation = new FormControl(dd.action,); + this.orchestrationFlow = new FormControl(dd.orchestrationUri,); + } else { + this.modelName = new FormControl('', ); + this.modelVersionId = new FormControl('', ); + this.operation = new FormControl('', ); + this.orchestrationFlow = new FormControl('',); + this.nfRole = new FormControl('',); + this.recipeTimeout = new FormControl('',); + } + } + + getModelType (type: string) { + if(type == 'Service Recipe') { + return 'Service' + } else if(type == 'Network Recipe') { + return 'Network' + } else { + return 'VNF' + } + } + + createForm() { + this.myform = new FormGroup({ + modelName: this.modelName, + nfRole: this.nfRole, + recipeTimeout: this.recipeTimeout, + modelVersionId: this.modelVersionId, + operation: this.operation, + orchestrationFlow: this.orchestrationFlow, + }); + if(this.parentData.isAdd === false) { + this.myform = this.parentData.fData; + } + } + + onSubmit() { + if (this.isCancel) { + return; + } + if (this.myform.valid ) { + console.log("Form Submitted!"); + console.log("formdata", this.myform.value) + let data = this.myform.value; + data['modelType'] = this.getModelType(this.parentData.tab); + this.saveServiceRecipes(JSON.stringify(data)); + this.myform.reset(); + } else { + this.popup.error("Please fill valid data."); + } + } + + closeDialog (evt: any) { + this.isCancel = true + this.dialogRef.close() + } + + saveServiceRecipes(data: any): void { + this.data.saveServiceRecipe(data) + .subscribe((data: any) => { + console.log(JSON.stringify(data)); + if(data != null) { + if (data.id && data.id != "") { + this.popup.info("Data stored in database."); + } else if(data.errMsg) { + this.popup.error(data.errMsg); + } + } + this.spinner.hide(); + this.dialogRef.close() + },error => { + console.log(error); + this.popup.error("Unable to store bpmn data, Error code:" + error.status); + this.spinner.hide(); + this.dialogRef.close() + }); + } +} + diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/sidebar/sidebar.component.html b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/sidebar/sidebar.component.html index 4b8edbe..fb36be5 100644 --- a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/sidebar/sidebar.component.html +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/app/sidebar/sidebar.component.html @@ -26,4 +26,9 @@ SPDX-License-Identifier: Apache-2.0 <a routerLink="/">Home</a> </li> </ul> + <ul> + <li> + <a routerLink="/onboard">Update Recipe</a> + </li> + </ul> </nav> diff --git a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/styles.scss b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/styles.scss index 8457059..99dff22 100644 --- a/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/styles.scss +++ b/so-admin-cockpit-monitoring-workflow/so-admin-cockpit-monitoring-workflow-ui/src/main/frontend/src/styles.scss @@ -28,7 +28,14 @@ body { font-family: 'Montserrat', sans-serif; height: 100vh; } - +.addRecipe { + button { + height: 15px !important; + cursor: pointer !important; + height: 36px !important; + margin: 5px 10px !important; + } +} #container { display: grid; grid-template-columns: 70px auto; |