diff --git a/src/app/admin/admin-routing.module.ts b/src/app/admin/admin-routing.module.ts index b631033a414a8f592b157687e4e8adeccf5a89cb..91b25c935fba046c8f0f127242f1238635a02444 100644 --- a/src/app/admin/admin-routing.module.ts +++ b/src/app/admin/admin-routing.module.ts @@ -1,3 +1,4 @@ +import { SyscheckComponent } from './syscheck/syscheck.component'; import { MonitorComponent } from './monitor/monitor.component'; import { ResultsComponent } from './results/results.component'; import { NgModule, Component } from '@angular/core'; @@ -12,6 +13,7 @@ const routes: Routes = [ children: [ {path: '', redirectTo: 'myfiles', pathMatch: 'full'}, {path: 'myfiles', component: MyfilesComponent}, + {path: 'syscheck', component: SyscheckComponent}, {path: 'monitor', component: MonitorComponent}, {path: 'results', component: ResultsComponent}, {path: '**', component: MyfilesComponent} diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts index dff930d93f9f6c9dd09bb08ab63a7c5f917b552f..e65b467c86b131aa84a9947567f5e6cc408b0988 100644 --- a/src/app/admin/admin.component.ts +++ b/src/app/admin/admin.component.ts @@ -12,6 +12,7 @@ import { WorkspaceData } from './backend.service'; export class AdminComponent implements OnInit { public navLinks = [ {path: 'myfiles', label: 'Dateien'}, + {path: 'syscheck', label: 'System-Check Berichte'}, {path: 'monitor', label: 'Monitor'}, {path: 'results', label: 'Ergebnisse'} ]; diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts index 0d50daac0e33e981d32c896c4098ae250baf1e97..d349c5e875546ca379949a6f18f1887e8be3b243 100644 --- a/src/app/admin/admin.module.ts +++ b/src/app/admin/admin.module.ts @@ -22,6 +22,7 @@ import { MatTableModule, MatTabsModule, MatButtonModule, MatIconModule, MatToolb import { MonitorComponent } from './monitor/monitor.component'; import { MatExpansionModule } from '@angular/material/expansion'; import {MatGridListModule} from '@angular/material/grid-list'; +import { SyscheckComponent } from './syscheck/syscheck.component'; @NgModule({ imports: [ @@ -60,7 +61,8 @@ import {MatGridListModule} from '@angular/material/grid-list'; AdminComponent, MyfilesComponent, ResultsComponent, - MonitorComponent + MonitorComponent, + SyscheckComponent ], providers: [ BackendService, diff --git a/src/app/admin/backend.service.ts b/src/app/admin/backend.service.ts index 384755de7654548090189ca8ca62077088a073aa..d01270e86904b3cdc5e5f1a6f608b5eeaeaa1d62 100644 --- a/src/app/admin/backend.service.ts +++ b/src/app/admin/backend.service.ts @@ -219,6 +219,38 @@ export class BackendService { .post<boolean>(this._serverUrl + 'deleteData.php', {at: adminToken, ws: workspaceId, g: groups}, httpOptions); } + getSysCheckReportList(adminToken: string, workspaceId: number): Observable<SysCheckStatistics[]> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<SysCheckStatistics[]>(this._serverUrl + 'getSysCheckReportList.php', {at: adminToken, ws: workspaceId}, httpOptions); + } + + getSysCheckReport(adminToken: string, workspaceId: number, reports: string[], columnDelimiter: string, quoteChar: string): Observable<string[]> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<string[]>(this._serverUrl + 'getSysCheckReport.php', + {at: adminToken, ws: workspaceId, r: reports, cd: columnDelimiter, q: quoteChar}, httpOptions); + } + + deleteSysCheckReports(adminToken: string, workspaceId: number, reports: string[]): Observable<boolean> { + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }; + return this.http + .post<boolean>(this._serverUrl + 'deleteSysCheckReports.php', + {at: adminToken, ws: workspaceId, r: reports}, httpOptions); + } + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . private handleError(errorObj: HttpErrorResponse): Observable<ServerError> { const myreturn: ServerError = { @@ -341,3 +373,10 @@ export interface ReviewData { reviewtime: Date; entry: string; } + +export interface SysCheckStatistics { + id: string; + label: string; + count: number; + details: string[]; +} diff --git a/src/app/admin/syscheck/syscheck.component.css b/src/app/admin/syscheck/syscheck.component.css new file mode 100644 index 0000000000000000000000000000000000000000..c495430b14a19cf4d0fe394fb8a7217058eff209 --- /dev/null +++ b/src/app/admin/syscheck/syscheck.component.css @@ -0,0 +1,3 @@ +.mat-icon { + margin-right: 5px; +} diff --git a/src/app/admin/syscheck/syscheck.component.html b/src/app/admin/syscheck/syscheck.component.html new file mode 100644 index 0000000000000000000000000000000000000000..d059389ba3aa33478aaa029a72dd38111ad5f8e2 --- /dev/null +++ b/src/app/admin/syscheck/syscheck.component.html @@ -0,0 +1,57 @@ +<div class="columnhost" fxLayout="column"> + <div class="spinner-container" *ngIf="dataLoading"> + <mat-spinner></mat-spinner> + </div> + <div fxLayout="row" fxLayoutGap="10px"> + <button mat-raised-button (click)="downloadReportsCSV()" [disabled]="!tableselectionCheckbox.hasValue()" + matTooltip="Download Berichte als CSV für Excel" matTooltipPosition="above"> + <mat-icon>cloud_download</mat-icon>Berichte + </button> + <button mat-raised-button (click)="deleteReports()" [disabled]="!tableselectionCheckbox.hasValue()" + matTooltip="Löschen Berichte für markierte System-Checks" matTooltipPosition="above"> + <mat-icon>delete</mat-icon> + </button> + </div> + + <mat-table [dataSource]="resultDataSource" matSort> + <ng-container matColumnDef="selectCheckbox"> + <mat-header-cell *matHeaderCellDef fxFlex="70px"> + <mat-checkbox (change)="$event ? masterToggle() : null" + [checked]="tableselectionCheckbox.hasValue() && isAllSelected()" + [indeterminate]="tableselectionCheckbox.hasValue() && !isAllSelected()"> + </mat-checkbox> + </mat-header-cell> + <mat-cell *matCellDef="let row" fxFlex="70px"> + <mat-checkbox (click)="$event.stopPropagation()" + (change)="$event ? tableselectionCheckbox.toggle(row) : null" + [checked]="tableselectionCheckbox.isSelected(row)"> + </mat-checkbox> + </mat-cell> + </ng-container> + + <ng-container matColumnDef="syscheckId"> + <mat-header-cell *matHeaderCellDef mat-sort-header fxFlex="300px">System-Check Id</mat-header-cell> + <mat-cell *matCellDef="let element" fxFlex="300px">{{element.id}}</mat-cell> + </ng-container> + + <ng-container matColumnDef="syscheckLabel"> + <mat-header-cell *matHeaderCellDef mat-sort-header fxLayoutAlign="start center">System-Check Name</mat-header-cell> + <mat-cell *matCellDef="let element" fxLayoutAlign="start center"> {{element.label}} </mat-cell> + </ng-container> + + <ng-container matColumnDef="number"> + <mat-header-cell *matHeaderCellDef mat-sort-header fxLayoutAlign="center center">Anzahl Berichte</mat-header-cell> + <mat-cell *matCellDef="let element" fxLayoutAlign="center center">{{element.count}} </mat-cell> + </ng-container> + + <ng-container matColumnDef="details"> + <mat-header-cell *matHeaderCellDef mat-sort-header fxLayoutAlign="center center">Betriebssysteme und Browser</mat-header-cell> + <mat-cell *matCellDef="let element" fxLayout="column" fxLayoutAlign="center start"> + <div *ngFor="let d of element.details">{{d}}</div> + </mat-cell> + </ng-container> + + <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> + <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> + </mat-table> +</div> diff --git a/src/app/admin/syscheck/syscheck.component.spec.ts b/src/app/admin/syscheck/syscheck.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1210159f5a75b9fd205fab50fdd402ff2e2be8f --- /dev/null +++ b/src/app/admin/syscheck/syscheck.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SyscheckComponent } from './syscheck.component'; + +describe('SyscheckComponent', () => { + let component: SyscheckComponent; + let fixture: ComponentFixture<SyscheckComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SyscheckComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SyscheckComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/syscheck/syscheck.component.ts b/src/app/admin/syscheck/syscheck.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..03f06b82758f7299272eaff266e18844b386dbf0 --- /dev/null +++ b/src/app/admin/syscheck/syscheck.component.ts @@ -0,0 +1,132 @@ +import { ConfirmDialogComponent, ConfirmDialogData } from './../../iqb-common/confirm-dialog/confirm-dialog.component'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { BackendService, SysCheckStatistics } from './../backend.service'; +import { MainDatastoreService } from './../maindatastore.service'; +import { MatSnackBar, MatSort, MatTableDataSource, MatDialog } from '@angular/material'; +import { SelectionModel } from '@angular/cdk/collections'; +import { saveAs } from 'file-saver'; + + +@Component({ + templateUrl: './syscheck.component.html', + styleUrls: ['./syscheck.component.css'] +}) +export class SyscheckComponent implements OnInit { + displayedColumns: string[] = ['selectCheckbox', 'syscheckLabel', 'number', 'details']; + private resultDataSource = new MatTableDataSource<SysCheckStatistics>([]); + private isAdmin = false; + // prepared for selection if needed sometime + private tableselectionCheckbox = new SelectionModel<SysCheckStatistics>(true, []); + private dataLoading = false; + + @ViewChild(MatSort) sort: MatSort; + + constructor( + private bs: BackendService, + private mds: MainDatastoreService, + private deleteConfirmDialog: MatDialog, + public snackBar: MatSnackBar + ) { + this.mds.isAdmin$.subscribe( + i => this.isAdmin = i); + } + + ngOnInit() { + this.mds.adminToken$.subscribe(at => this.updateTable()); + this.mds.workspaceId$.subscribe(ws => this.updateTable()); + // console.log(saveAs); + } + + updateTable() { + this.dataLoading = true; + this.tableselectionCheckbox.clear(); + this.bs.getSysCheckReportList(this.mds.adminToken$.getValue(), this.mds.workspaceId$.getValue()).subscribe( + (resultData: SysCheckStatistics[]) => { + this.dataLoading = false; + this.resultDataSource = new MatTableDataSource<SysCheckStatistics>(resultData); + this.resultDataSource.sort = this.sort; + } + ) + } + + isAllSelected() { + const numSelected = this.tableselectionCheckbox.selected.length; + const numRows = this.resultDataSource.data.length; + return numSelected === numRows; + } + + masterToggle() { + this.isAllSelected() ? + this.tableselectionCheckbox.clear() : + this.resultDataSource.data.forEach(row => this.tableselectionCheckbox.select(row)); + } + + // 444444444444444444444444444444444444444444444444444444444444444444444444444444444444444 + downloadReportsCSV() { + if (this.tableselectionCheckbox.selected.length > 0) { + this.dataLoading = true; + const selectedReports: string[] = []; + this.tableselectionCheckbox.selected.forEach(element => { + selectedReports.push(element.id); + }); + this.bs.getSysCheckReport( + this.mds.adminToken$.getValue(), + this.mds.workspaceId$.getValue(), + selectedReports, ';', '"').subscribe( + (reportData: string[]) => { + if (reportData.length > 0) { + const lineDelimiter = '\n'; + let myCsvData = ''; + reportData.forEach((repLine: string) => { + myCsvData += repLine + lineDelimiter; + }); + var blob = new Blob([myCsvData], {type: "text/csv;charset=utf-8"}); + saveAs(blob, "iqb-testcenter-syscheckreports.csv"); + } else { + this.snackBar.open('Keine Daten verfügbar.', 'Fehler', {duration: 3000}); + } + this.tableselectionCheckbox.clear(); + this.dataLoading = false; + }) + } + } + + deleteReports() { + if (this.tableselectionCheckbox.selected.length > 0) { + const selectedReports: string[] = []; + this.tableselectionCheckbox.selected.forEach(element => { + selectedReports.push(element.id); + }); + + let prompt = 'Es werden alle Berichte für diese'; + if (selectedReports.length > 1) { + prompt = prompt + ' ' + selectedReports.length + ' System-Checks '; + } else { + prompt = prompt + 'n System-Check "' + selectedReports[0] + '" '; + } + + const dialogRef = this.deleteConfirmDialog.open(ConfirmDialogComponent, { + width: '400px', + data: <ConfirmDialogData>{ + title: 'Löschen von Berichten', + content: prompt + 'gelöscht. Fortsetzen?', + confirmbuttonlabel: 'Berichtsdaten löschen' + } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result !== false) { + // ========================================================= + this.dataLoading = true; + this.bs.deleteSysCheckReports( + this.mds.adminToken$.getValue(), + this.mds.workspaceId$.getValue(), + selectedReports).subscribe((deleteOk: boolean) => { + this.tableselectionCheckbox.clear(); + this.dataLoading = false; + }); + } + }); + } + } +} diff --git a/src/environments/environment.build.ts b/src/environments/environment.build.ts index aee6118b72f5a9632c7eb7a1db52474a670d2700..e93771a711a6194f5753da162afb39490985e875 100644 --- a/src/environments/environment.build.ts +++ b/src/environments/environment.build.ts @@ -5,5 +5,5 @@ export const environment = { testcenterUrl: '/', appName: 'IQB-Testcenter Verwaltung', appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen', - appVersion: '0.8 - 17.10.2018' + appVersion: '0.9 - 28.1.2019' };