TSK-843 designed report table and made single row collapsable

This commit is contained in:
Mustapha Zorgati 2019-04-03 17:06:42 +02:00 committed by Holger Hagen
parent b75be882f1
commit d8b1829a53
10 changed files with 101 additions and 45 deletions

View File

@ -38,12 +38,10 @@ import pro.taskana.rest.RestConfiguration;
@SpringBootTest(classes = RestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class MonitorControllerRestDocumentation {
@LocalServerPort
int port;
@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@LocalServerPort
int port;
@Autowired
private WebApplicationContext context;
@ -70,6 +68,8 @@ public class MonitorControllerRestDocumentation {
fieldWithPath("meta.name").description("Name of the report"),
fieldWithPath("meta.date").description("Date of the report creation"),
fieldWithPath("meta.header").description("Column-headers of the report"),
fieldWithPath("meta.expHeader").description(
"Expandable Column-headers which match the depth of the foldable rows within the report."),
fieldWithPath("meta.rowDesc").description("Descriptions for the rows the report"),
fieldWithPath("meta.totalDesc").description("Description for the report itself"),
subsectionWithPath("rows").description("Object holding the rows of the report.\n"
@ -77,7 +77,7 @@ public class MonitorControllerRestDocumentation {
fieldWithPath("sumRow").description("Object holding the sums in the columns over all rows"),
subsectionWithPath("sumRow.cells")
.description("Contains the accumulated numbers over all columns defined in meta.header.\n"
+ "For the exact structure please check the example response above"),
+ "For the exact structure please check the example response above"),
fieldWithPath("sumRow.total").description("Total number of tasks"),
fieldWithPath("_links.self.href").ignored()
};

View File

@ -8,4 +8,5 @@ export class ReportInfoDataIterable {
key: string;
val: ReportInfoData;
depth: number;
display = false;
}

View File

@ -14,12 +14,12 @@ import {MonitorComponent} from './monitor.component';
import {TasksComponent} from './tasks/tasks.component';
import {WorkbasketComponent} from './workbasket/workbasket.component';
import {ClassificationTasksComponent} from './classification-tasks/classification-tasks.component';
import {ReportRowComponent} from "./report/row/row.component";
import {TimestampComponent} from "./timestamp/timestamp.component";
import {ReportRowComponent} from './report/row/row.component';
import {TimestampComponent} from './timestamp/timestamp.component';
import {RestConnectorService} from './services/restConnector/rest-connector.service';
import {MapToIterable} from "../shared/pipes/mapToIterable/mapToIterable";
import {MapToIterable} from '../shared/pipes/mapToIterable/mapToIterable';
const MODULES = [
CommonModule,

View File

@ -1,26 +1,30 @@
<div *ngIf="reportData" class="report table table-body-striped">
<div class="table-header">
<div class="table-row">
<div class="table-cell table-cell--bold table-cell--justify">{{reportData.meta.rowDesc}}</div>
<div
[ngClass]="{'table-cell--border-right': currentExpHeaders === 0}"
class="table-cell table-cell--bold table-cell--justify">{{reportData.meta.rowDesc}}</div>
<ng-container *ngFor="let header of reportData.meta.expHeader; let i = index">
<ng-container *ngIf="i < currentExpHeaders">
<div class="table-cell table-cell--bold table-cell--justify">{{header}}</div>
<div [ngClass]="{'table-cell--border-right': currentExpHeaders - 1 === i}"
class="table-cell table-cell--bold table-cell--justify">{{header}}</div>
</ng-container>
</ng-container>
<div *ngFor="let header of reportData.meta.header" class="table-cell table-cell--bold">{{header}}</div>
<div class="table-cell table-cell--bold">{{reportData.meta.totalDesc}}</div>
<div *ngFor="let header of reportData.meta.header"
class="table-cell table-cell--bold">{{header}}</div>
<div class="table-cell table-cell--bold table-cell--border-left">{{reportData.meta.totalDesc}}</div>
</div>
</div>
<monitor-report-row (expandedDepth)="expandHeader($event, i)"
<taskana-report-row (expandedDepth)="expandHeader($event, i)"
*ngFor="let row of reportData.rows | mapToIterable | orderBy:['key']; let i = index"
[headers]="reportData.meta.header"
[maxTableDepth]="currentExpHeaders"
[row]="row"
class="table-body"></monitor-report-row>
<monitor-report-row (expandedDepth)="expandHeader($event, expHeaders.length - 1)"
class="table-body"></taskana-report-row>
<taskana-report-row (expandedDepth)="expandHeader($event, expHeaders.length - 1)"
[headers]="reportData.meta.header"
[maxTableDepth]="currentExpHeaders"
[row]="_sumRow"
bold="true"
class="table-footer"></monitor-report-row>
class="table-footer"></taskana-report-row>
</div>

View File

@ -1,6 +1,6 @@
import {Component, Input, OnInit} from '@angular/core';
import {ReportData} from 'app/monitor/models/report-data';
import {ReportInfoDataIterable} from "../models/report-info-data";
import {ReportInfoDataIterable} from '../models/report-info-data';
@Component({
selector: 'taskana-report',
@ -11,12 +11,9 @@ export class ReportComponent implements OnInit {
expHeaders: Array<number>;
currentExpHeaders: number = 0;
currentExpHeaders = 0;
_sumRow: ReportInfoDataIterable;
constructor() {
}
private _reportData: ReportData;
get reportData(): ReportData {
@ -32,6 +29,9 @@ export class ReportComponent implements OnInit {
this._sumRow.key = reportData.meta.totalDesc;
}
constructor() {
}
ngOnInit(): void {
}

View File

@ -1,19 +1,28 @@
<ng-container *ngIf="headers && flatRows">
<ng-container *ngFor="let row of flatRows; let i = index">
<div *ngIf="currentDepth >= row.depth" class="table-row">
<div *ngIf="row.display"
[ngClass]="{'table-row--highlight': row.depth === 0 && currentDepth > 0,
'table-row--hover': row.depth > 0 && currentDepth > 0,
'table-row--white': row.depth > 0 && currentDepth > 0}"
class="table-row">
<div *ngFor="let _ of range(row.depth)" class="table-cell"></div>
<div (click)="toggleFold(row.depth)"
[ngClass]="{'table-cell--clickable': maxDepth > row.depth, 'table-cell--bold' : bold}"
<div (click)="toggleFold(i)"
[ngClass]="{'table-cell--clickable': maxDepth > row.depth,
'table-cell--bold' : bold || row.depth === 0 && currentDepth > 0,
'table-cell--border-right': row.depth === maxTableDepth}"
class="table-cell table-cell--justify">
{{row.key}}
<span *ngIf="maxDepth > row.depth"
class="material-icons md-18">{{ canRowCollapse(i) ? "expand_more" : "expand_less"}}</span>{{row.key}}
</div>
<div *ngFor="let _ of range(maxTableDepth - row.depth)" class="table-cell"></div>
<div *ngFor="let header of headers" [ngClass]="{'table-cell--bold' : bold}" class="table-cell">
<div *ngFor="let _ of range(maxTableDepth - row.depth); let i = index"
[ngClass]="{'table-cell--border-right': i === maxTableDepth - row.depth - 1}" class="table-cell"></div>
<div *ngFor="let header of headers"
[ngClass]="{'table-cell--bold' : bold || row.depth === 0 && currentDepth > 0}" class="table-cell">
{{row.val.cells[header]}}
</div>
<div class="table-cell table-cell--bold">
<div class="table-cell table-cell--bold table-cell--border-left">
{{row.val.total}}
</div>
</div>
</ng-container>
</ng-container>
</ng-container>

View File

@ -1,4 +1,19 @@
@import './src/assets/_colors';
.table-cell--clickable {
cursor: pointer;
}
.table-row--highlight,
.table-row--hover:hover {
background-color: #f9f9f9;
}
.table-row--highlight > .table-cell {
border-bottom: 2px solid #ddd;
}
.table-row--white {
background-color: white;
}

View File

@ -1,9 +1,9 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {ReportInfoDataIterable} from "../../models/report-info-data";
import {MapToIterable} from "../../../shared/pipes/mapToIterable/mapToIterable";
import {ReportInfoDataIterable} from '../../models/report-info-data';
import {MapToIterable} from '../../../shared/pipes/mapToIterable/mapToIterable';
@Component({
selector: 'monitor-report-row',
selector: 'taskana-report-row',
templateUrl: './row.component.html',
styleUrls: ['./row.component.scss']
})
@ -12,19 +12,15 @@ export class ReportRowComponent implements OnInit {
@Input()
headers: Array<string>;
@Input()
bold: boolean = false;
bold = false;
@Input()
maxTableDepth: number = 0;
maxTableDepth = 0;
@Output()
expandedDepth: EventEmitter<number> = new EventEmitter<number>();
currentDepth = 0;
maxDepth: number;
currentDepth: number = 0;
flatRows: Array<ReportInfoDataIterable>;
constructor(private mapToIterable: MapToIterable) {
}
private _row: ReportInfoDataIterable;
get row(): ReportInfoDataIterable {
@ -38,25 +34,47 @@ export class ReportRowComponent implements OnInit {
this.maxDepth = this.flatten(row, 0);
}
constructor(private mapToIterable: MapToIterable) {
}
ngOnInit() {
}
toggleFold(depth: number) {
this.currentDepth = depth == this.currentDepth && depth < this.maxDepth ? depth + 1 : depth;
this.expandedDepth.emit(this.currentDepth);
toggleFold(index: number): void {
const toggleRow = this.flatRows[index++];
if (toggleRow.depth < this.maxDepth) {
const firstChildRow = this.flatRows[index++];
firstChildRow.display = !firstChildRow.display;
let end = false;
for (let i = index; i < this.flatRows.length && !end; i++) {
const row = this.flatRows[i];
end = row.depth <= toggleRow.depth;
if (!end) {
row.display = firstChildRow.display && row.depth === firstChildRow.depth;
}
}
this.currentDepth = Math.max(...this.flatRows.filter(r => r.display).map(r => r.depth));
this.expandedDepth.emit(this.currentDepth);
}
}
range(depth: number): Array<null> {
return new Array<null>(Math.max(depth, 0));
}
canRowCollapse(index: number) {
return !this.flatRows[index + 1].display;
}
private flatten(row: ReportInfoDataIterable, depth: number): number {
row.depth = depth;
row.display = depth === 0;
this.flatRows.push(row);
if (row.val.foldableRows) {
depth = Math.max(...this.mapToIterable.transform(row.val.foldableRows)
.sort((a, b) => a.key.localeCompare(b.key))
.map(r => this.flatten(r, depth + 1)))
.map(r => this.flatten(r, depth + 1)));
}
return depth;
}

View File

@ -1,6 +1,6 @@
import {Component, OnInit} from '@angular/core';
import {ReportData} from "../models/report-data";
import {RestConnectorService} from "../services/restConnector/rest-connector.service";
import {ReportData} from '../models/report-data';
import {RestConnectorService} from '../services/restConnector/rest-connector.service';
@Component({
selector: 'taskana-monitor-timestamp',

View File

@ -72,3 +72,12 @@
min-width: 0;
}
.table-cell--border-right {
border-right: 2px solid #ddd;
}
.table-cell--border-left {
border-left: 2px solid #ddd;
}