-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cobbler-Frontend: Add UI for signatures
- Loading branch information
Showing
6 changed files
with
303 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
projects/cobbler-frontend/src/app/signatures/signatures.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<div class="title-table"> | ||
<div class="title-row"> | ||
<h1 class="title title-cell-text">Signatures</h1> | ||
<span class="title-cell-button"> | ||
<button mat-icon-button (click)="this.updateSignatures()"><mat-icon>refresh</mat-icon></button> | ||
</span> | ||
</div> | ||
</div> | ||
|
||
<mat-spinner *ngIf="isLoading" style="margin:0 auto;"></mat-spinner> | ||
<ng-container *ngIf="!isLoading"> | ||
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl"> | ||
<!-- This is the tree node template for leaf nodes --> | ||
<mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding> | ||
<!-- use a disabled button to provide padding for tree leaf --> | ||
<button mat-icon-button disabled></button> | ||
{{ node.data }} | ||
</mat-tree-node> | ||
<!-- This is the tree node template for expandable nodes --> | ||
<mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding> | ||
<button mat-icon-button matTreeNodeToggle | ||
[attr.aria-label]="'Toggle ' + node.data"> | ||
<mat-icon class="mat-icon-rtl-mirror"> | ||
{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }} | ||
</mat-icon> | ||
</button> | ||
{{ node.data }} | ||
</mat-tree-node> | ||
<!-- This is the tree node template for expandable nodes with an OS version as data --> | ||
<mat-tree-node *matTreeNodeDef="let node;when: hasOsVersion" matTreeNodePadding> | ||
<button mat-icon-button disabled></button> | ||
<table mat-table [dataSource]="node.data" class="mat-elevation-z8"> | ||
@for (column of columns; track column) { | ||
<ng-container [matColumnDef]="column.columnDef"> | ||
<th mat-header-cell *matHeaderCellDef> | ||
{{ column.header }} | ||
</th> | ||
<td mat-cell *matCellDef="let row"> | ||
{{ column.cell(row) }} | ||
</td> | ||
</ng-container> | ||
} | ||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | ||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> | ||
</table> | ||
</mat-tree-node> | ||
</mat-tree> | ||
</ng-container> |
19 changes: 19 additions & 0 deletions
19
projects/cobbler-frontend/src/app/signatures/signatures.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
.title-table { | ||
display: table; | ||
width: 100%; | ||
} | ||
|
||
.title-row { | ||
display: table-cell; | ||
width: 100%; | ||
} | ||
|
||
.title-cell-text { | ||
display: table-cell; | ||
width: 100%; | ||
vertical-align: middle; | ||
} | ||
|
||
.title-cell-button { | ||
display: table-cell; | ||
} |
44 changes: 44 additions & 0 deletions
44
projects/cobbler-frontend/src/app/signatures/signatures.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import {provideHttpClient} from '@angular/common/http'; | ||
import {provideHttpClientTesting} from '@angular/common/http/testing'; | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
import {MatDividerModule} from '@angular/material/divider'; | ||
import {MatIconModule} from '@angular/material/icon'; | ||
import {MatTableModule} from '@angular/material/table'; | ||
import {MatTreeModule} from '@angular/material/tree'; | ||
import {COBBLER_URL} from 'cobbler-api'; | ||
|
||
import { SignaturesComponent } from './signatures.component'; | ||
|
||
describe('SignaturesComponent', () => { | ||
let component: SignaturesComponent; | ||
let fixture: ComponentFixture<SignaturesComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [ | ||
SignaturesComponent, | ||
MatTreeModule, | ||
MatIconModule, | ||
MatTableModule, | ||
MatDividerModule, | ||
], | ||
providers: [ | ||
provideHttpClient(), | ||
provideHttpClientTesting(), | ||
{ | ||
provide: COBBLER_URL, | ||
useValue: new URL('http://localhost/cobbler_api') | ||
}, | ||
] | ||
}) | ||
.compileComponents(); | ||
|
||
fixture = TestBed.createComponent(SignaturesComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
186 changes: 186 additions & 0 deletions
186
projects/cobbler-frontend/src/app/signatures/signatures.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
import {AsyncPipe, NgForOf, NgIf} from '@angular/common'; | ||
import {Component, OnInit} from '@angular/core'; | ||
import {MatDivider} from '@angular/material/divider'; | ||
import {MatList, MatListItem} from '@angular/material/list'; | ||
import {MatProgressSpinner} from '@angular/material/progress-spinner'; | ||
import {MatSnackBar} from '@angular/material/snack-bar'; | ||
import { | ||
MatCell, MatCellDef, | ||
MatColumnDef, | ||
MatHeaderCell, | ||
MatHeaderCellDef, | ||
MatHeaderRow, MatHeaderRowDef, | ||
MatRow, MatRowDef, | ||
MatTable | ||
} from '@angular/material/table'; | ||
import {filter, repeat, take} from 'rxjs/operators'; | ||
import {UserService} from '../services/user.service'; | ||
import {CobblerApiService} from 'cobbler-api'; | ||
import { | ||
MatTree, | ||
MatTreeFlatDataSource, | ||
MatTreeFlattener, | ||
MatTreeNode, MatTreeNodeDef, | ||
MatTreeNodePadding, | ||
MatTreeNodeToggle | ||
} from '@angular/material/tree'; | ||
import {FlatTreeControl} from '@angular/cdk/tree'; | ||
import {MatIcon} from '@angular/material/icon'; | ||
import {MatIconButton} from '@angular/material/button'; | ||
|
||
interface TableRow { | ||
key: string; | ||
value: any; | ||
} | ||
|
||
/** | ||
* Food data with nested structure. | ||
* Each node has a name and an optional list of children. | ||
*/ | ||
interface OsNode { | ||
data: string | Array<TableRow>; | ||
children?: OsNode[]; | ||
} | ||
|
||
/** Flat node with expandable and level information */ | ||
interface OsBreedFlatNode { | ||
expandable: boolean; | ||
data: string | Array<TableRow>; | ||
level: number; | ||
} | ||
|
||
@Component({ | ||
selector: 'cobbler-signatures', | ||
standalone: true, | ||
imports: [ | ||
MatTree, | ||
MatTreeNode, | ||
MatIcon, | ||
MatIconButton, | ||
MatTreeNodeToggle, | ||
MatTreeNodePadding, | ||
MatTreeNodeDef, | ||
MatTable, | ||
MatHeaderCell, | ||
MatCell, | ||
MatHeaderRow, | ||
MatRow, | ||
MatColumnDef, | ||
MatHeaderCellDef, | ||
MatCellDef, | ||
MatHeaderRowDef, | ||
MatRowDef, | ||
MatDivider, | ||
AsyncPipe, | ||
MatList, | ||
MatListItem, | ||
MatProgressSpinner, | ||
NgForOf, | ||
NgIf | ||
], | ||
templateUrl: './signatures.component.html', | ||
styleUrl: './signatures.component.scss' | ||
}) | ||
export class SignaturesComponent implements OnInit { | ||
// Table | ||
columns = [ | ||
{ | ||
columnDef: 'key', | ||
header: 'Attribute', | ||
cell: (element: TableRow) => `${element.key}`, | ||
}, | ||
{ | ||
columnDef: 'value', | ||
header: 'Value', | ||
cell: (element: TableRow) => `${element.value}`, | ||
}, | ||
]; | ||
|
||
displayedColumns = this.columns.map(c => c.columnDef); | ||
|
||
// Tree | ||
private _transformer = (node: OsNode, level: number) => { | ||
return { | ||
expandable: !!node.children && node.children.length > 0, | ||
data: node.data, | ||
level: level, | ||
}; | ||
}; | ||
|
||
treeControl = new FlatTreeControl<OsBreedFlatNode>( | ||
node => node.level, | ||
node => node.expandable, | ||
); | ||
|
||
treeFlattener = new MatTreeFlattener( | ||
this._transformer, | ||
node => node.level, | ||
node => node.expandable, | ||
node => node.children, | ||
); | ||
dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); | ||
|
||
// Spinner | ||
public isLoading = true; | ||
|
||
constructor( | ||
public userService: UserService, | ||
private cobblerApiService: CobblerApiService, | ||
private _snackBar: MatSnackBar, | ||
) { | ||
} | ||
|
||
ngOnInit(): void { | ||
this.generateSignatureUiData(); | ||
} | ||
|
||
hasChild = (_: number, node: OsBreedFlatNode) => node.expandable; | ||
|
||
hasOsVersion = (_: number, node: OsBreedFlatNode) => typeof node.data !== 'string'; | ||
|
||
generateSignatureUiData(): void { | ||
this.cobblerApiService.get_signatures(this.userService.token).subscribe(value => { | ||
const newData: Array<OsNode> = []; | ||
for (const k in value.breeds) { | ||
const children: Array<OsNode> = []; | ||
for (const j in value.breeds[k]) { | ||
const osVersionData: Array<TableRow> = []; | ||
for (const i in value.breeds[k][j]) { | ||
osVersionData.push({key: i, value: value.breeds[k][j][i]}); | ||
} | ||
children.push({data: j, children: [{data: osVersionData, children: []}]}); | ||
} | ||
newData.push({data: k, children: children}); | ||
} | ||
this.dataSource.data = newData; | ||
this.isLoading = false | ||
}, error => { | ||
// HTML encode the error message since it originates from XML | ||
this._snackBar.open(this.toHTML(error.message), 'Close'); | ||
}); | ||
} | ||
|
||
updateSignatures(): void { | ||
this.isLoading = true | ||
this.cobblerApiService.background_signature_update(this.userService.token).subscribe( | ||
value => { | ||
this.cobblerApiService.get_task_status(value).pipe( | ||
repeat(), | ||
filter(data => data.state === "failed" || data.state === "complete"), | ||
take(1) | ||
).subscribe(value1 => { | ||
this.isLoading = false | ||
this.generateSignatureUiData() | ||
}) | ||
}, | ||
error => { | ||
// HTML encode the error message since it originates from XML | ||
this._snackBar.open(this.toHTML(error.message), 'Close'); | ||
}); | ||
} | ||
|
||
toHTML(input: string): any { | ||
// FIXME: Deduplicate method | ||
return new DOMParser().parseFromString(input, 'text/html').documentElement.textContent; | ||
} | ||
} |