diff options
Diffstat (limited to 'mod2/ui/src/app/comp-specs')
-rw-r--r-- | mod2/ui/src/app/comp-specs/comp-specs.component.css | 121 | ||||
-rw-r--r-- | mod2/ui/src/app/comp-specs/comp-specs.component.html | 189 | ||||
-rw-r--r-- | mod2/ui/src/app/comp-specs/comp-specs.component.spec.ts | 133 | ||||
-rw-r--r-- | mod2/ui/src/app/comp-specs/comp-specs.component.ts | 211 |
4 files changed, 654 insertions, 0 deletions
diff --git a/mod2/ui/src/app/comp-specs/comp-specs.component.css b/mod2/ui/src/app/comp-specs/comp-specs.component.css new file mode 100644 index 0000000..5f86e73 --- /dev/null +++ b/mod2/ui/src/app/comp-specs/comp-specs.component.css @@ -0,0 +1,121 @@ +/* + * # ============LICENSE_START======================================================= + * # Copyright (c) 2020 AT&T Intellectual Property. 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. + * # ============LICENSE_END========================================================= + */ + +td{ + word-break:break-all +} + +textarea +{ + font-size: 12px; +} + +.table_actions_button{ + background-color: transparent; + border: none; + width: 20px; + height: 20px; + vertical-align: middle; +} + +.row-expand-layout{ + display: grid; + grid-template-columns: 30% 40% auto; + grid-gap: 10px; + grid-auto-rows: minmax(100px, auto); +} + +.row-expand-card{ + font-size: 12px; + grid-row: 1; + border-radius: 5px; + border: 1px solid slategray; + padding: 10px; + /* This height prevents vertical scroll bar in Notes */ + height: 92px; + overflow: hidden; +} + +label { + cursor: pointer; +} + +.fa-refresh{ + cursor: pointer; +} + +.input{ + padding-top: 10px; +} + +.inputLabel { + font-weight: 600; + margin-left: 20px; + width: 140px; +} + +.inputFieldSm { + width: 200px; + height: 35px; + padding-left: 6px; +} +.inputFieldMed { + width: 300px; + height: 35px; + padding-left: 6px; +} +.inputFieldLg { + width: 400px; + height: 35px; + padding-left: 6px; +} + +.table_action_item{ + outline: none; + font-size: 12px; +} + +::ng-deep .mat-menu-content { +padding-top: 0px !important; +padding-bottom: 0px !important; +} +.mat-menu-item{ +line-height:30px; +height:30px; +} + +.greenStatus{ + background-color: rgba(80, 233, 105, 0.87) +} + +.redStatus{ + background-color: rgba(255, 29, 29, 0.733) +} + +.blueStatus{ + background-color: rgba(0, 183, 255, 0.432) +} + +.greyStatus{ + background-color: rgba(150, 150, 150, 0.432) +} + +.ui-state-highlight { + background-color: #878C94 !important; + color: black !important; +} diff --git a/mod2/ui/src/app/comp-specs/comp-specs.component.html b/mod2/ui/src/app/comp-specs/comp-specs.component.html new file mode 100644 index 0000000..3061cdf --- /dev/null +++ b/mod2/ui/src/app/comp-specs/comp-specs.component.html @@ -0,0 +1,189 @@ +<!-- + # ============LICENSE_START======================================================= + # Copyright (c) 2020 AT&T Intellectual Property. 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. + # ============LICENSE_END========================================================= + --> + +<div style="margin: 0px 50px 10px 20px; border: 1px solid darkslategray; min-width: 980px"> + <p-table #dt *ngIf="loadTable" [columns]="cols" [value]="csElements" sortMode="multiple" [paginator]="true" + [rows]="18" [rowsPerPageOptions]="[10,12,14,16,18,20,25,50]" (onFilter)="onTableFiltered(dt.filteredValue)" dataKey="id"> + <ng-template pTemplate="caption"> + + <div style="margin-left: -18%; width: 82%; max-height: 25px; display: inline-flex;"> + <!--CS Table Header--> + <div style="float: left;"> + <!--Refresh--> + <i class="fa fa-refresh" (click)="getAllCs()"></i> + <!--Global Filter--> + <input type="text" pInputText size="50" placeholder="Global Filter" + (input)="dt.filterGlobal($event.target.value, 'contains')" + style="width: 250px; height:25px; font-size: 12px; margin-left: 15px"> + <i class="fa fa-search" style="margin:4px 0px 0 8px"></i> + </div> + + <h4 style="margin-left: 15%"><b>Component Specs</b></h4> + + </div> + </ng-template> + + <ng-template pTemplate="header" let-columns> + <tr> + <th style="width: 3em"></th> + <th class="ui-state-highlight" *ngFor="let col of columns" [pSortableColumn]="col.field" + style="font-size: 12px; outline: none; vertical-align: bottom; text-align: center;" [ngStyle]="{'width': col.width}"> + {{col.header}}<br> + <p-sortIcon [field]="col.field"></p-sortIcon> + </th> + <th style="font-size: 13px; width: 8%; vertical-align: top; text-align: center;"> + Actions + </th> + </tr> + + <!--Second header row for individual column filters--> + <tr style="text-align: center;"> + <th style="width: 3em"></th> + <th *ngFor="let col of columns" [ngSwitch]="col.field"> + <input pInputText type="text" (input)="dt.filter($event.target.value, col.field, 'contains')" + style="width: 100%; height: 20px; font-size: 10px;" placeholder="Filter"> + </th> + <th> + + </th> + </tr> + </ng-template> + + <ng-template pTemplate="body" let-rowData let-csElem> + <tr style="font-size: 12px;"> + <!--Column for row expand buttons--> + <td> + <a href="#" [pRowToggler]="rowData"> + <i [ngClass]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'"></i> + </a> + </td> + + <td *ngFor="let col of cols"> + <div *ngIf="col.field==='status'" + style="width: fit-content; width: -moz-max-content; padding: 0px 5px 0px 5px; border-radius: 3px; font-weight: 600;" + [ngClass]="{'greenStatus' : csElem[col.field] === 'ACTIVE', 'greyStatus' : csElem[col.field] === 'INACTIVE'}"> + {{csElem[col.field]}} + </div> + <div *ngIf="col.field!=='status'">{{csElem[col.field]}}</div> + </td> + + <!--Actions Column--> + <td> + <div style="text-align: center;"> + <button pButton type="button" + style="background-color: transparent; border: none; width: 20px; height: 20px; vertical-align: middle;" + class="ui-button-secondary" [matMenuTriggerFor]="menu"> + <i class="pi pi-ellipsis-h" style="color: grey;"></i> + </button> + <mat-menu #menu="matMenu" xPosition="before"> + + <div style="background-color: rgba(128, 128, 128, 0.25);"> + <span style="font-size: 12px; margin-left: 10px; font-weight: 500;"><i class="pi pi-search" style="font-size: 10px;"></i> View</span> + </div> + + <button mat-menu-item class="table_action_item" (click)="showViewCsDialog(rowData)">Component Spec</button> + <div matTooltip="Policy not included" [matTooltipDisabled]="rowData.policyJson" matTooltipPosition="left"> + <button mat-menu-item [disabled]="!rowData.policyJson" class="table_action_item" (click)="showViewPolicyDialog(rowData)">Policy</button> + </div> + <!-- + <div *ngIf="rowData.status !== 'ACTIVE'"> + <div style="background-color: rgba(128, 128, 128, 0.25);"> + <span style="font-size: 12px; margin-left: 10px; font-weight: 500;"><i class="pi pi-pencil" + style="font-size: 10px;"></i> Update</span> + </div> + + <button mat-menu-item class="table_action_item" (click)="toActive(rowData)">To Active</button> + </div>--> + </mat-menu> + </div> + </td> + </tr> + </ng-template> + + <!--Row expand content--> + <ng-template pTemplate="rowexpansion" let-rowData let-columns="columns"> + <tr> + <td [attr.colspan]="columns.length + 2"> + <div class="row-expand-layout" [@rowExpansionTrigger]="'active'"> + <!-- Audit Fields --> + <div class="row-expand-card" style="background-color: rgba(95, 158, 160, 0.295)"> + <b>Created By:</b> {{rowData.metadata.createdBy}}<br> + <b>Created On:</b> {{rowData.metadata.createdOn}}<br> + <b>Updated By:</b> {{rowData.metadata.updatedBy}}<br> + <b>Updated On:</b> {{rowData.metadata.updatedOn}} + </div> + <!-- Notes --> + <div class="row-expand-card" style="background-color: rgba(100, 148, 237, 0.219)"> + <b>Notes:</b><br> + <p-scrollPanel [style]="{width: '100%', height: '62px'}"> + <div style="font-size: 12px; word-break: normal;">{{rowData.metadata.notes}}</div> + </p-scrollPanel> + </div> + <!-- Labels --> + <div class="row-expand-card" style="background-color: rgba(76, 65, 225, 0.199)"> + <b style="padding-bottom: 5px;">Labels:</b><br> + <div *ngFor="let label of rowData['metadata']['labels']" + style="display: inline-flex; margin-top: 5px;"> + <div style="padding: 2px 7px 3px 0px;"> + <span + style="background-color: rgba(80, 80, 80, 0.185); padding: 3px; border-radius: 3px;">{{label}}</span> + </div> + </div> + </div> + </div> + </td> + </tr> + </ng-template> + </p-table> + + <!--download buttons for exporting table to either csv or excel file--> + <div *ngIf="loadTable" style="margin-left: 10px; margin-top: -32px; float: left;"> + <button pButton type="button" (click)="exportTable('csv')" matTooltip="Export Table to CSV" + matTooltipPosition="above" + style="border-radius: 5px; width: 65px; height: 22px; font-size: 14px; border: none; margin-top: 4px; display: inline-flex;"> + <i class="pi pi-file" style="margin-top: 2px; margin-left: 8px;"></i> + <label style="font-weight: 800; vertical-align: middle;">CSV</label> + </button> + <button pButton type="button" (click)="exportTable('excel')" matTooltip="Export Table to XLSX" + matTooltipPosition="above" + style="border-radius: 5px; width: 65px; height: 22px; margin-left: 7px; font-size: 14px; background-color: green; border: none; display: inline-flex;"> + <i class="pi pi-file-excel" style="margin-top: 2px; margin-left: 4px;"></i> + <label style="font-weight: 800; vertical-align: middle;">Excel</label> + </button> + </div> +</div> + +<!-- * * * * View Spec Content Pop Up * * * * --> +<p-dialog [(visible)]="showViewCs" header="Spec Content" appendTo="body" [maximizable]="true" + [modal]="true" [style]="{width: '80vw'}" [baseZIndex]="10000" [closable]="false"> + <pre>{{specContentToView | json}}</pre> + <p-footer> + <button pButton label="Close" (click)="showViewCs=false" type="button"></button> + <button pButton label="Download" (click)="download(specContentToView, 'Component_Spec')" type="button"></button> + </p-footer> +</p-dialog> + +<!-- * * * * View Policy JSON Pop Up * * * * --> +<p-dialog [(visible)]="showViewPolicy" header="Policy Json" appendTo="body" [maximizable]="true" [modal]="true" + [style]="{width: '80vw'}" [baseZIndex]="10000" [closable]="false"> + <pre>{{policyJsonToView | json}}</pre> + <p-footer> + <button pButton label="Close" (click)="showViewPolicy=false" type="button"></button> + <button pButton label="Download" (click)="download(policyJsonToView, 'Policy_Json')" type="button"></button> + </p-footer> +</p-dialog> diff --git a/mod2/ui/src/app/comp-specs/comp-specs.component.spec.ts b/mod2/ui/src/app/comp-specs/comp-specs.component.spec.ts new file mode 100644 index 0000000..64405e3 --- /dev/null +++ b/mod2/ui/src/app/comp-specs/comp-specs.component.spec.ts @@ -0,0 +1,133 @@ +/* + * # ============LICENSE_START======================================================= + * # Copyright (c) 2020 AT&T Intellectual Property. 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. + * # ============LICENSE_END========================================================= + */ + +import { HttpClientModule } from '@angular/common/http'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatMenuModule, MatTooltipModule } from '@angular/material'; +import { RouterTestingModule } from '@angular/router/testing'; +import { JwtHelperService, JWT_OPTIONS } from '@auth0/angular-jwt'; +import { Ng4LoadingSpinnerModule } from 'ng4-loading-spinner'; +import { MessageService } from 'primeng/api'; +import { ButtonModule } from 'primeng/button'; +import { DialogModule } from 'primeng/dialog'; +import { DropdownModule } from 'primeng/dropdown'; +import { ScrollPanelModule } from 'primeng/scrollpanel'; +import { TableModule } from 'primeng/table'; +import { ToastModule } from 'primeng/toast'; + +import { CompSpecsComponent } from './comp-specs.component'; + +describe('CompSpecsComponent', () => { + let component: CompSpecsComponent; + let fixture: ComponentFixture<CompSpecsComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + CompSpecsComponent + ], + imports: [ + Ng4LoadingSpinnerModule, + TableModule, + MatMenuModule, + ScrollPanelModule, + ToastModule, + DialogModule, + DropdownModule, + FormsModule, + ReactiveFormsModule, + ButtonModule, + HttpClientModule, + ToastModule, + RouterTestingModule, + MatTooltipModule + ], + providers: [ + MessageService, + { provide: JWT_OPTIONS, useValue: JWT_OPTIONS }, + JwtHelperService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CompSpecsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it(`should fill csElements Object`, () => { + const fixture = TestBed.createComponent(CompSpecsComponent); + const app = fixture.debugElement.componentInstance; + + let mockCsElement = [{ + id: 'testId1234', + name: 'test-MS', + type: 'k8s', + specContent: '', + policyJson: 'test', + status: 'New', + msInstanceInfo: { + release: '2008', + name: 'test Ms', + }, + metadata: { + createdBy: 'test', + createdOn: '01-01-2020 12:00', + updatedBy: 'test', + updatedOn: '01-01-2020 12:00', + notes: 'test', + labels: ['test'], + } + }] + + app.fillTable(mockCsElement) + + expect(app.loadTable).toEqual(true); + expect(app.csElements.length).toEqual(1); + }); + + it(`should set spec content to view`, () => { + const fixture = TestBed.createComponent(CompSpecsComponent); + const app = fixture.debugElement.componentInstance; + let mockData = { + specContent: 'test' + } + app.showViewCsDialog(mockData) + expect(app.showViewCs).toEqual(true); + expect(app.specContentToView).toEqual('test'); + }); + + it(`should set policy json content to view`, () => { + const fixture = TestBed.createComponent(CompSpecsComponent); + const app = fixture.debugElement.componentInstance; + let mockData = { + policyJson: 'test' + } + app.showViewPolicyDialog(mockData) + expect(app.showViewPolicy).toEqual(true); + expect(app.policyJsonToView).toEqual('test'); + }); +}); + diff --git a/mod2/ui/src/app/comp-specs/comp-specs.component.ts b/mod2/ui/src/app/comp-specs/comp-specs.component.ts new file mode 100644 index 0000000..4327c0b --- /dev/null +++ b/mod2/ui/src/app/comp-specs/comp-specs.component.ts @@ -0,0 +1,211 @@ +/* + * # ============LICENSE_START======================================================= + * # Copyright (c) 2020 AT&T Intellectual Property. 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. + * # ============LICENSE_END========================================================= + */ + +import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; +import { compSpecsService } from '../services/comp-specs-service.service'; +import { Table } from 'primeng/table'; +import { MessageService } from 'primeng/api'; +import { Ng4LoadingSpinnerService } from 'ng4-loading-spinner'; +import { DatePipe } from '@angular/common'; +import { trigger, state, transition, style, animate } from '@angular/animations'; +import { ActivatedRoute } from '@angular/router'; +import { DownloadService } from '../services/download.service'; + +@Component({ + selector: 'app-comp-specs', + templateUrl: './comp-specs.component.html', + styleUrls: ['./comp-specs.component.css'], + animations: [ + trigger('rowExpansionTrigger', [ + state('void', style({ + transform: 'translateX(-10%)', + opacity: 0 + })), + state('active', style({ + transform: 'translateX(0)', + opacity: 1 + })), + transition('* <=> *', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)')) + ]) + ], + providers: [DatePipe] +}) +export class CompSpecsComponent implements OnInit { + @ViewChild(Table, { static: false }) dt: Table; + + csElements: any[] = []; + + cols: any[] = [ + { field: 'instanceName', header: 'Instance Name' }, + { field: 'release', header: 'Release', width: '15%' }, + { field: 'type', header: 'Type', width: '15%' }, + { field: 'policy', header: 'Policy', width: '15%' }, + { field: 'status', header: 'Status', width: '15%' } + ]; + + columns: any[]; + loadTable: boolean; + filteredRows: any; + summaryRows: any; + downloadItems: { label: string; command: () => void; }[]; + + msInstanceId: string; + msInstanceName: any; + msInstanceRelease: any; + + constructor(private csApis: compSpecsService, private messageService: MessageService, + private spinnerService: Ng4LoadingSpinnerService, private datePipe: DatePipe, + private route: ActivatedRoute, private downloadService: DownloadService) { } + + //create table of comp specs + ngOnInit() { + this.loadTable = false; + + this.route.queryParams.subscribe((params) => { + this.msInstanceId = params['instanceId']; + }); + + this.getAllCs() + } + + getAllCs() { + + this.csElements = []; + + this.csApis.getAllCompSpecs(this.msInstanceId) + .subscribe((data: any[]) => { + this.fillTable(data) + }) + + this.columns = this.cols.map(col => ({ title: col.header, dataKey: col.field })); + } + + //filter table + onTableFiltered(values) { + if (values) { this.filteredRows = values; } + else { this.filteredRows = this.summaryRows; } + } + + /* * * * Export ms instance table to excel or csv * * * */ + exportTable(exportTo) { + let downloadElements: any[] = [] + + //labels array not handled well by excel download so converted them to a single string + for (let row of this.filteredRows) { + let labels; + let notes; + if (exportTo === "excel") { + if (row.metadata.labels !== undefined) { + labels = row.metadata.labels.join(",") + } + } else { + labels = row.metadata.labels + } + + if (row.metadata.notes !== null && row.metadata.notes !== undefined && row.metadata.notes !== '') { + notes = encodeURI(row.metadata.notes).replace(/%20/g, " ").replace(/%0A/g, "\\n") + } + + downloadElements.push({ + Instance_Name: row.instanceName, + Release: row.release, + Type: row.type, + Status: row.status, + Created_By: row.metadata.createdBy, + Created_On: row.metadata.createdOn, + Updated_By: row.metadata.updatedBy, + Updated_On: row.metadata.updatedOn, + Notes: notes, + Labels: labels + }) + } + + let arrHeader = [] + + if (exportTo === "csv") { + arrHeader = [ + "Instance_Name", + "Release", + "Type", + "Status", + "Created_By", + "Created_On", + "Updated_By", + "Updated_On", + "Notes", + "Labels" + ]; + } + + this.downloadService.exportTableData(exportTo, downloadElements, arrHeader) + } + + //fill object with microservice data, to be used to fill table. + //checks if fields are empty and if they are, store 'N/A' as the values + fillTable(data) { + for (let elem of data) { + let policy = ''; + if(elem.policyJson){policy = "Included"} + + let tempCsElement: any = { + id: elem.id, + instanceName: elem.msInstanceInfo.name, + release: elem.msInstanceInfo.release, + type: elem.type, + policy: policy, + status: elem.status, + specContent: elem.specContent, + policyJson: elem.policyJson, + metadata: { + createdBy: elem.metadata.createdBy, + createdOn: this.datePipe.transform(elem.metadata.createdOn, 'MM-dd-yyyy HH:mm'), + updatedBy: elem.metadata.updatedBy, + updatedOn: this.datePipe.transform(elem.metadata.updatedOn, 'MM-dd-yyyy HH:mm'), + notes: elem.metadata.notes, + labels: elem.metadata.labels + } + } + this.csElements.unshift(tempCsElement) + } + this.msInstanceName = this.csElements[0]['instanceName'] + this.msInstanceRelease = this.csElements[0]['release'] + this.filteredRows = this.csElements + this.loadTable = true; + this.spinnerService.hide(); + } + + showViewCs: boolean = false; + specContentToView: string; + showViewCsDialog(data) { + this.showViewCs = true; + this.specContentToView = data.specContent; + } + + showViewPolicy: boolean = false; + policyJsonToView: string; + showViewPolicyDialog(data) { + this.showViewPolicy = true; + this.policyJsonToView = data.policyJson; + } + + /* * * * Download single spec file or policy * * * */ + download(content, contentType) { + let fileName = `${this.msInstanceName}_${this.msInstanceRelease}_${contentType}` + this.downloadService.downloadJSON(content, fileName) + } +}
\ No newline at end of file |