TSK-843: implemented foldable report in frontend. (structure only)
This commit is contained in:
parent
adb861e583
commit
620b315c7c
|
|
@ -10,22 +10,22 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import pro.taskana.TaskState;
|
import pro.taskana.TaskState;
|
||||||
import pro.taskana.impl.report.item.TimestampQueryItem;
|
|
||||||
import pro.taskana.report.Timestamp;
|
|
||||||
import pro.taskana.TaskanaEngine;
|
import pro.taskana.TaskanaEngine;
|
||||||
import pro.taskana.TaskanaRole;
|
import pro.taskana.TaskanaRole;
|
||||||
import pro.taskana.exceptions.InvalidArgumentException;
|
import pro.taskana.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.exceptions.NotAuthorizedException;
|
import pro.taskana.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.impl.report.header.TimeIntervalColumnHeader;
|
import pro.taskana.impl.report.header.TimeIntervalColumnHeader;
|
||||||
|
import pro.taskana.impl.report.item.TimestampQueryItem;
|
||||||
import pro.taskana.impl.report.preprocessor.DaysToWorkingDaysPreProcessor;
|
import pro.taskana.impl.report.preprocessor.DaysToWorkingDaysPreProcessor;
|
||||||
import pro.taskana.mappings.TaskMonitorMapper;
|
import pro.taskana.mappings.TaskMonitorMapper;
|
||||||
|
import pro.taskana.report.Timestamp;
|
||||||
import pro.taskana.report.TimestampReport;
|
import pro.taskana.report.TimestampReport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The implementation of {@link TimestampReport.Builder}.
|
* The implementation of {@link TimestampReport.Builder}.
|
||||||
*/
|
*/
|
||||||
public class TimestampReportBuilderImpl extends
|
public class TimestampReportBuilderImpl extends
|
||||||
TimeIntervalReportBuilderImpl<TimestampReport.Builder, TimestampQueryItem, TimeIntervalColumnHeader.Date>
|
TimeIntervalReportBuilderImpl<TimestampReport.Builder, TimestampQueryItem, TimeIntervalColumnHeader>
|
||||||
implements TimestampReport.Builder {
|
implements TimestampReport.Builder {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(TimestampReport.Builder.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(TimestampReport.Builder.class);
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,17 @@ public abstract class Report<I extends QueryItem, H extends ColumnHeader<? super
|
||||||
private Map<String, Row<I>> reportRows = new LinkedHashMap<>();
|
private Map<String, Row<I>> reportRows = new LinkedHashMap<>();
|
||||||
private Row<I> sumRow;
|
private Row<I> sumRow;
|
||||||
private String rowDesc;
|
private String rowDesc;
|
||||||
|
private String[] expandableHeaders;
|
||||||
|
|
||||||
protected Report(List<H> columnHeaders, String rowDesc) {
|
protected Report(List<H> columnHeaders, String rowDesc, String[] expandableHeaders) {
|
||||||
this.rowDesc = rowDesc;
|
this.rowDesc = rowDesc;
|
||||||
sumRow = createRow(columnHeaders.size());
|
sumRow = createRow(columnHeaders.size());
|
||||||
this.columnHeaders = new ArrayList<>(columnHeaders);
|
this.columnHeaders = new ArrayList<>(columnHeaders);
|
||||||
|
this.expandableHeaders = expandableHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Report(List<H> columnHeaders, String rowDesc) {
|
||||||
|
this(columnHeaders, rowDesc, new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Map<String, Row<I>> getRows() {
|
public final Map<String, Row<I>> getRows() {
|
||||||
|
|
@ -48,6 +54,10 @@ public abstract class Report<I extends QueryItem, H extends ColumnHeader<? super
|
||||||
return rowDesc;
|
return rowDesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final String[] getExpandableHeaders() {
|
||||||
|
return expandableHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
public Row<I> getRow(String key) {
|
public Row<I> getRow(String key) {
|
||||||
return reportRows.get(key);
|
return reportRows.get(key);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ import pro.taskana.impl.report.structure.Report;
|
||||||
/**
|
/**
|
||||||
* A {@link TimestampReport} displays created and competed tasks for a specific dates.
|
* A {@link TimestampReport} displays created and competed tasks for a specific dates.
|
||||||
*/
|
*/
|
||||||
public class TimestampReport extends Report<TimestampQueryItem, TimeIntervalColumnHeader.Date> {
|
public class TimestampReport extends Report<TimestampQueryItem, TimeIntervalColumnHeader> {
|
||||||
|
|
||||||
public TimestampReport(List<TimeIntervalColumnHeader.Date> dates) {
|
public TimestampReport(List<TimeIntervalColumnHeader> dates) {
|
||||||
super(dates, "STATES");
|
super(dates, "STATES", new String[] {"ORG LEVEL 1", "ORG LEVEL 2", "ORG LEVEL 3", "ORG LEVEL 4"});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -32,7 +32,7 @@ public class TimestampReport extends Report<TimestampQueryItem, TimeIntervalColu
|
||||||
* Builder for {@link TimestampReport}.
|
* Builder for {@link TimestampReport}.
|
||||||
*/
|
*/
|
||||||
public interface Builder extends
|
public interface Builder extends
|
||||||
TimeIntervalReportBuilder<TimestampReport.Builder, TimestampQueryItem, TimeIntervalColumnHeader.Date> {
|
TimeIntervalReportBuilder<TimestampReport.Builder, TimestampQueryItem, TimeIntervalColumnHeader> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
TimestampReport buildReport() throws NotAuthorizedException, InvalidArgumentException;
|
TimestampReport buildReport() throws NotAuthorizedException, InvalidArgumentException;
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ import org.junit.runner.RunWith;
|
||||||
import pro.taskana.TaskMonitorService;
|
import pro.taskana.TaskMonitorService;
|
||||||
import pro.taskana.impl.report.header.TimeIntervalColumnHeader;
|
import pro.taskana.impl.report.header.TimeIntervalColumnHeader;
|
||||||
import pro.taskana.impl.report.item.TimestampQueryItem;
|
import pro.taskana.impl.report.item.TimestampQueryItem;
|
||||||
import pro.taskana.impl.report.row.TimestampRow;
|
|
||||||
import pro.taskana.impl.report.row.SingleRow;
|
import pro.taskana.impl.report.row.SingleRow;
|
||||||
|
import pro.taskana.impl.report.row.TimestampRow;
|
||||||
import pro.taskana.report.TimestampReport;
|
import pro.taskana.report.TimestampReport;
|
||||||
import pro.taskana.security.JAASRunner;
|
import pro.taskana.security.JAASRunner;
|
||||||
import pro.taskana.security.WithAccessId;
|
import pro.taskana.security.WithAccessId;
|
||||||
|
|
@ -43,7 +43,7 @@ public class ProvideTimestampReportAccTest extends AbstractReportAccTest {
|
||||||
TaskMonitorService taskMonitorService = taskanaEngine.getTaskMonitorService();
|
TaskMonitorService taskMonitorService = taskanaEngine.getTaskMonitorService();
|
||||||
|
|
||||||
//last 14 days. Today excluded.
|
//last 14 days. Today excluded.
|
||||||
List<TimeIntervalColumnHeader.Date> headers = IntStream.range(-14, 0)
|
List<TimeIntervalColumnHeader> headers = IntStream.range(-14, 0)
|
||||||
.mapToObj(TimeIntervalColumnHeader.Date::new)
|
.mapToObj(TimeIntervalColumnHeader.Date::new)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
TimestampReport timestampReport = taskMonitorService.createTimestampReportBuilder()
|
TimestampReport timestampReport = taskMonitorService.createTimestampReportBuilder()
|
||||||
|
|
|
||||||
|
|
@ -91,11 +91,11 @@ public class MonitorController {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/daily-entry-exit-report")
|
@GetMapping(path = "/timestamp-report")
|
||||||
@Transactional(readOnly = true, rollbackFor = Exception.class)
|
@Transactional(readOnly = true, rollbackFor = Exception.class)
|
||||||
public ResponseEntity<ReportResource> getDailyEntryExitReport()
|
public ResponseEntity<ReportResource> getDailyEntryExitReport()
|
||||||
throws NotAuthorizedException, InvalidArgumentException {
|
throws NotAuthorizedException, InvalidArgumentException {
|
||||||
List<TimeIntervalColumnHeader.Date> columnHeaders = IntStream.range(-14, 0)
|
List<TimeIntervalColumnHeader> columnHeaders = IntStream.range(-14, 0)
|
||||||
.mapToObj(TimeIntervalColumnHeader.Date::new)
|
.mapToObj(TimeIntervalColumnHeader.Date::new)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
return ResponseEntity.status(HttpStatus.OK)
|
return ResponseEntity.status(HttpStatus.OK)
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,14 @@ import pro.taskana.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.exceptions.NotAuthorizedException;
|
import pro.taskana.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.impl.report.row.FoldableRow;
|
import pro.taskana.impl.report.row.FoldableRow;
|
||||||
import pro.taskana.impl.report.row.SingleRow;
|
import pro.taskana.impl.report.row.SingleRow;
|
||||||
import pro.taskana.report.ClassificationReport;
|
|
||||||
import pro.taskana.report.TimestampReport;
|
|
||||||
import pro.taskana.report.TaskStatusReport;
|
|
||||||
import pro.taskana.report.WorkbasketReport;
|
|
||||||
import pro.taskana.impl.report.structure.ColumnHeader;
|
import pro.taskana.impl.report.structure.ColumnHeader;
|
||||||
import pro.taskana.impl.report.structure.QueryItem;
|
import pro.taskana.impl.report.structure.QueryItem;
|
||||||
import pro.taskana.impl.report.structure.Report;
|
import pro.taskana.impl.report.structure.Report;
|
||||||
import pro.taskana.impl.report.structure.Row;
|
import pro.taskana.impl.report.structure.Row;
|
||||||
|
import pro.taskana.report.ClassificationReport;
|
||||||
|
import pro.taskana.report.TaskStatusReport;
|
||||||
|
import pro.taskana.report.TimestampReport;
|
||||||
|
import pro.taskana.report.WorkbasketReport;
|
||||||
import pro.taskana.rest.MonitorController;
|
import pro.taskana.rest.MonitorController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -80,7 +80,7 @@ public class ReportAssembler {
|
||||||
report.getClass().getSimpleName(),
|
report.getClass().getSimpleName(),
|
||||||
time.toString(),
|
time.toString(),
|
||||||
header,
|
header,
|
||||||
report.getRowDesc());
|
report.getExpandableHeaders(), report.getRowDesc());
|
||||||
|
|
||||||
// iterate over each Row and transform it to a RowResource while keeping the domain key.
|
// iterate over each Row and transform it to a RowResource while keeping the domain key.
|
||||||
Map<String, ReportResource.RowResource> rows = report.getRows()
|
Map<String, ReportResource.RowResource> rows = report.getRows()
|
||||||
|
|
|
||||||
|
|
@ -109,12 +109,14 @@ public class ReportResource extends ResourceSupport {
|
||||||
private String name;
|
private String name;
|
||||||
private String date;
|
private String date;
|
||||||
private String[] header;
|
private String[] header;
|
||||||
|
private String[] expHeader;
|
||||||
private String rowDesc;
|
private String rowDesc;
|
||||||
|
|
||||||
public MetaInformation(String name, String date, String[] header, String rowDesc) {
|
public MetaInformation(String name, String date, String[] header, String[] expHeader, String rowDesc) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.date = date;
|
this.date = date;
|
||||||
this.header = header;
|
this.header = header;
|
||||||
|
this.expHeader = expHeader;
|
||||||
this.rowDesc = rowDesc;
|
this.rowDesc = rowDesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,18 +136,18 @@ public class ReportResource extends ResourceSupport {
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String[] getExpHeader() {
|
||||||
|
return expHeader;
|
||||||
|
}
|
||||||
|
|
||||||
public String getRowDesc() {
|
public String getRowDesc() {
|
||||||
return rowDesc;
|
return rowDesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "MetaInformation ["
|
return String.format("MetaInformation [name= %s, date= %s, header= %s, expHeader= %s, rowDesc= %s]",
|
||||||
+ "name= " + this.name
|
name, date, Arrays.toString(header), Arrays.toString(expHeader), rowDesc);
|
||||||
+ "date= " + this.date
|
|
||||||
+ "header= " + Arrays.toString(this.header)
|
|
||||||
+ "rowDesc= " + this.rowDesc
|
|
||||||
+ "]";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import pro.taskana.impl.report.header.TimeIntervalColumnHeader;
|
||||||
import pro.taskana.impl.report.item.DetailedMonitorQueryItem;
|
import pro.taskana.impl.report.item.DetailedMonitorQueryItem;
|
||||||
import pro.taskana.impl.report.item.MonitorQueryItem;
|
import pro.taskana.impl.report.item.MonitorQueryItem;
|
||||||
import pro.taskana.report.ClassificationReport;
|
import pro.taskana.report.ClassificationReport;
|
||||||
|
import pro.taskana.report.TimestampReport;
|
||||||
import pro.taskana.report.WorkbasketReport;
|
import pro.taskana.report.WorkbasketReport;
|
||||||
import pro.taskana.rest.TestConfiguration;
|
import pro.taskana.rest.TestConfiguration;
|
||||||
|
|
||||||
|
|
@ -67,6 +68,7 @@ public class ReportResourceTest {
|
||||||
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
||||||
assertEquals("WORKBASKET KEYS", meta.getRowDesc());
|
assertEquals("WORKBASKET KEYS", meta.getRowDesc());
|
||||||
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
||||||
|
assertArrayEquals(new String[0], meta.getExpHeader());
|
||||||
assertEquals("Total", meta.getTotalDesc());
|
assertEquals("Total", meta.getTotalDesc());
|
||||||
|
|
||||||
// rows
|
// rows
|
||||||
|
|
@ -103,6 +105,7 @@ public class ReportResourceTest {
|
||||||
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
||||||
assertEquals("CLASSIFICATION KEYS", meta.getRowDesc());
|
assertEquals("CLASSIFICATION KEYS", meta.getRowDesc());
|
||||||
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
||||||
|
assertArrayEquals(new String[0], meta.getExpHeader());
|
||||||
assertEquals("Total", meta.getTotalDesc());
|
assertEquals("Total", meta.getTotalDesc());
|
||||||
|
|
||||||
// rows
|
// rows
|
||||||
|
|
@ -154,6 +157,7 @@ public class ReportResourceTest {
|
||||||
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
||||||
assertEquals("CLASSIFICATION KEYS", meta.getRowDesc());
|
assertEquals("CLASSIFICATION KEYS", meta.getRowDesc());
|
||||||
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
||||||
|
assertArrayEquals(new String[0], meta.getExpHeader());
|
||||||
assertEquals("Total", meta.getTotalDesc());
|
assertEquals("Total", meta.getTotalDesc());
|
||||||
|
|
||||||
// rows
|
// rows
|
||||||
|
|
@ -219,6 +223,7 @@ public class ReportResourceTest {
|
||||||
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
||||||
assertEquals("TASK CLASSIFICATION KEYS", meta.getRowDesc());
|
assertEquals("TASK CLASSIFICATION KEYS", meta.getRowDesc());
|
||||||
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
||||||
|
assertArrayEquals(new String[0], meta.getExpHeader());
|
||||||
assertEquals("Total", meta.getTotalDesc());
|
assertEquals("Total", meta.getTotalDesc());
|
||||||
|
|
||||||
// rows
|
// rows
|
||||||
|
|
@ -319,6 +324,7 @@ public class ReportResourceTest {
|
||||||
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
||||||
assertEquals("TASK CLASSIFICATION KEYS", meta.getRowDesc());
|
assertEquals("TASK CLASSIFICATION KEYS", meta.getRowDesc());
|
||||||
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
||||||
|
assertArrayEquals(new String[0], meta.getExpHeader());
|
||||||
assertEquals("Total", meta.getTotalDesc());
|
assertEquals("Total", meta.getTotalDesc());
|
||||||
|
|
||||||
// rows
|
// rows
|
||||||
|
|
@ -418,4 +424,20 @@ public class ReportResourceTest {
|
||||||
assertEquals(0, cells.get("2018-12-28").intValue());
|
assertEquals(0, cells.get("2018-12-28").intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExpandableHeader() {
|
||||||
|
//given
|
||||||
|
TimestampReport report = new TimestampReport(headers);
|
||||||
|
//when
|
||||||
|
ReportResource resource = reportAssembler.toReportResource(report, now.toInstant(ZoneOffset.UTC));
|
||||||
|
//then
|
||||||
|
ReportResource.MetaInformation meta = resource.getMeta();
|
||||||
|
assertEquals("TimestampReport", meta.getName());
|
||||||
|
assertEquals("2019-01-02T00:00:00Z", meta.getDate());
|
||||||
|
assertEquals("STATES", meta.getRowDesc());
|
||||||
|
assertArrayEquals(headers.stream().map(TimeIntervalColumnHeader::getDisplayName).toArray(), meta.getHeader());
|
||||||
|
assertArrayEquals(new String[] {"ORG LEVEL 1", "ORG LEVEL 2", "ORG LEVEL 3", "ORG LEVEL 4"},
|
||||||
|
meta.getExpHeader());
|
||||||
|
assertEquals("Total", meta.getTotalDesc());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,94 +1,78 @@
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="pull-right btn-group">
|
<div class="pull-right btn-group">
|
||||||
<button class="btn btn-default" data-toggle="tooltip" title="Query" type="button">
|
<button class="btn btn-default" data-toggle="tooltip" title="Query" type="button">
|
||||||
<span (click)="search()" class="material-icons md-20 blue">search</span>
|
<span (click)="search()" class="material-icons md-20 blue">search</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-default" data-toggle="tooltip" title="Query" type="button">
|
<button class="btn btn-default" data-toggle="tooltip" title="Query" type="button">
|
||||||
<span (click)="clear()" class="material-icons md-20 blue">clear</span>
|
<span (click)="clear()" class="material-icons md-20 blue">clear</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="panel-header">Taskana history query</h4>
|
<h4 class="panel-header">Taskana history query</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div *ngIf="taskQuery" class="table table-striped">
|
<div *ngIf="taskQuery" class="table table-striped">
|
||||||
<form [formGroup]="taskQueryForm">
|
<form [formGroup]="taskQueryForm">
|
||||||
<div class="table-header">
|
<div class="table-header">
|
||||||
<div class="table-row">
|
<div class="table-row">
|
||||||
<ng-container *ngFor="let taskHeader of taskQueryHeader | mapToIterable">
|
<ng-container *ngFor="let taskHeader of taskQueryHeader | mapToIterable">
|
||||||
<div (click)="changeOrderBy(taskHeader.key); search();"
|
<div (click)="changeOrderBy(taskHeader.key); search();" *ngIf="filterFieldsToShow(taskHeader.key) && !filterExpandGroup(taskHeader.key)"
|
||||||
*ngIf="filterFieldsToShow(taskHeader.key) && !filterExpandGroup(taskHeader.key)"
|
class="table-cell table-cell--bold table-cell--justify">
|
||||||
class="table-cell--bold table-cell--justify">
|
|
||||||
<span class="icon-space">
|
<span class="icon-space">
|
||||||
{{getHeaderFieldDescription(taskHeader.key)}}
|
{{getHeaderFieldDescription(taskHeader.key)}}
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="orderBy.sortBy === taskHeader.key"
|
<span *ngIf="orderBy.sortBy === taskHeader.key" [ngClass]="{'flip': orderBy.sortDirection === 'desc'}" class="material-icons md-20 blue pull-right">sort</span>
|
||||||
[ngClass]="{'flip': orderBy.sortDirection === 'desc'}"
|
|
||||||
class="material-icons md-20 blue pull-right">sort</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div (click)="toggleExpand = !toggleExpand" *ngIf="taskHeader.key === 'custom1'"
|
<div (click)="toggleExpand = !toggleExpand" *ngIf="taskHeader.key === 'custom1'"
|
||||||
[ngClass]="{'zoom-in': !toggleExpand, 'zoom-out': toggleExpand}"
|
[ngClass]="{'zoom-in': !toggleExpand, 'zoom-out': toggleExpand}" class="table-cell table-cell--bold table-cell--separator">
|
||||||
class="table-cell--bold table-cell--separator">
|
<span class="material-icons md-24 blue pull-right">{{toggleExpand? 'chevron_left' : 'chevron_right'}}</span>
|
||||||
<span
|
|
||||||
class="material-icons md-24 blue pull-right">{{toggleExpand ? 'chevron_left' : 'chevron_right'}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div (click)="changeOrderBy(taskHeader.key); search();"
|
<div (click)="changeOrderBy(taskHeader.key); search();" *ngIf="filterFieldsToShow(taskHeader.key) && filterExpandGroup(taskHeader.key) && toggleExpand"
|
||||||
*ngIf="filterFieldsToShow(taskHeader.key) && filterExpandGroup(taskHeader.key) && toggleExpand"
|
class="table-cell table-cell--bold table-cell--justify">
|
||||||
class="table-cell--bold">
|
|
||||||
<span class="icon-space">
|
<span class="icon-space">
|
||||||
{{getHeaderFieldDescription(taskHeader.key)}}
|
{{getHeaderFieldDescription(taskHeader.key)}}
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="orderBy.sortBy === taskHeader.key && filterFieldsToAllowQuerying(taskHeader.key)"
|
<span *ngIf="orderBy.sortBy === taskHeader.key && filterFieldsToAllowQuerying(taskHeader.key)" [ngClass]="{'flip': orderBy.sortDirection === 'desc'}"
|
||||||
[ngClass]="{'flip': orderBy.sortDirection === 'desc'}"
|
class="material-icons md-20 blue pull-right">sort</span>
|
||||||
class="material-icons md-20 blue pull-right">sort</span>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-row">
|
<div class="table-row">
|
||||||
<ng-container *ngFor="let taskHeader of taskQueryHeader | mapToIterable">
|
<ng-container *ngFor="let taskHeader of taskQueryHeader | mapToIterable">
|
||||||
<div class="divTableHeader divDate" *ngIf="isDate(taskHeader.key)">
|
<div *ngIf="isDate(taskHeader.key)" class="table-cell table-cell--bold table-cell--justify divDate">
|
||||||
<taskana-date-picker type="text" placeholder="{{getHeaderFieldDescription(taskHeader.key)}}" [name]="'this.created'" [id]="'created'" (dateOutput)="updateDate($event)" formControlName="{{taskHeader.key}}"></taskana-date-picker>
|
<taskana-date-picker (dateOutput)="updateDate($event)" [id]="'created'" [name]="'this.created'" formControlName="{{taskHeader.key}}" placeholder="{{getHeaderFieldDescription(taskHeader.key)}}" type="text"></taskana-date-picker>
|
||||||
</div>
|
</div>
|
||||||
<div class="divTableHeader" *ngIf="!isDate(taskHeader.key) && filterFieldsToShow(taskHeader.key) && !filterExpandGroup(taskHeader.key)">
|
<div *ngIf="!isDate(taskHeader.key) && filterFieldsToShow(taskHeader.key) && !filterExpandGroup(taskHeader.key)" class="table-cell table-cell--bold table-cell--justify">
|
||||||
<input type="text" class="form-control input-sm" (keyup.enter)="search()" placeholder="{{getHeaderFieldDescription(taskHeader.key)}}"
|
<input (keyup.enter)="search()" class="form-control input-sm" formControlName="{{taskHeader.key}}" placeholder="{{getHeaderFieldDescription(taskHeader.key)}}"
|
||||||
formControlName="{{taskHeader.key}}">
|
type="text">
|
||||||
</div>
|
</div>
|
||||||
<div (click)="toggleExpand = !toggleExpand" *ngIf="taskHeader.key === 'custom1'"
|
<div (click)="toggleExpand = !toggleExpand" *ngIf="taskHeader.key === 'custom1'"
|
||||||
[ngClass]="{'zoom-in': !toggleExpand, 'zoom-out': toggleExpand}"
|
[ngClass]="{'zoom-in': !toggleExpand, 'zoom-out': toggleExpand}" class="table-cell table-cell--bold table-cell--separator">
|
||||||
class="table-cell--bold table-cell--separator">
|
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="filterFieldsToShow(taskHeader.key) && filterExpandGroup(taskHeader.key) && toggleExpand"
|
<div *ngIf="filterFieldsToShow(taskHeader.key) && filterExpandGroup(taskHeader.key) && toggleExpand" class="table-cell table-cell--bold table-cell--justify">
|
||||||
class="table-cell--bold">
|
<input (keyup.enter)="search()" *ngIf="filterFieldsToAllowQuerying(taskHeader.key)" class="form-control input-sm"
|
||||||
<input (keyup.enter)="search()" *ngIf="filterFieldsToAllowQuerying(taskHeader.key)"
|
formControlName="{{taskHeader.key}}" placeholder="{{getHeaderFieldDescription(taskHeader.key)}}" type="text">
|
||||||
class="form-control input-sm"
|
|
||||||
formControlName="{{taskHeader.key}}"
|
|
||||||
placeholder="{{getHeaderFieldDescription(taskHeader.key)}}"
|
|
||||||
type="text">
|
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-body">
|
<div class="table-body">
|
||||||
<div *ngFor="let task of taskQuery" class="table-row">
|
<div *ngFor="let task of taskQuery" class="table-row">
|
||||||
<ng-container *ngFor="let taskHeader of taskQueryHeader | mapToIterable">
|
<ng-container *ngFor="let taskHeader of taskQueryHeader | mapToIterable">
|
||||||
<div class="divTableCell" *ngIf="isDate(taskHeader.key)">
|
<div *ngIf="isDate(taskHeader.key)" class="table-cell table-cell--justify">
|
||||||
{{task[taskHeader.key] | dateTimeZone:'yyyy-MM-dd'}}
|
{{task[taskHeader.key] | dateTimeZone:'yyyy-MM-dd'}}
|
||||||
</div>
|
</div>
|
||||||
<div class="divTableCell" *ngIf="!isDate(taskHeader.key) && filterFieldsToShow(taskHeader.key) && !filterExpandGroup(taskHeader.key)">
|
<div *ngIf="!isDate(taskHeader.key) && filterFieldsToShow(taskHeader.key) && !filterExpandGroup(taskHeader.key)" class="table-cell table-cell--justify">
|
||||||
{{task[taskHeader.key]}}
|
{{task[taskHeader.key]}}
|
||||||
</div>
|
</div>
|
||||||
<div (click)="toggleExpand = !toggleExpand" *ngIf="taskHeader.key === 'custom1'"
|
<div (click)="toggleExpand = !toggleExpand" *ngIf="taskHeader.key === 'custom1'" [ngClass]="{'zoom-in': !toggleExpand, 'zoom-out': toggleExpand}"
|
||||||
[ngClass]="{'zoom-in': !toggleExpand, 'zoom-out': toggleExpand}"
|
class="table-cell table-cell--separator">
|
||||||
class="table-cell table-cell--separator">
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div *ngIf="filterFieldsToShow(taskHeader.key) && filterExpandGroup(taskHeader.key) && filterFieldsToAllowQuerying(taskHeader.key) && toggleExpand" class="table-cell table-cell--justify">
|
||||||
*ngIf="filterFieldsToShow(taskHeader.key) && filterExpandGroup(taskHeader.key) && filterFieldsToAllowQuerying(taskHeader.key) && toggleExpand"
|
|
||||||
class="table-cell">
|
|
||||||
{{task[taskHeader.key]}}
|
{{task[taskHeader.key]}}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!filterFieldsToAllowQuerying(taskHeader.key) && toggleExpand" class="table-cell">
|
<div *ngIf="!filterFieldsToAllowQuerying(taskHeader.key) && toggleExpand" class="table-cell table-cell--justify">
|
||||||
<button (click)="openDetails(taskHeader.key, task[taskHeader.key])" *ngIf="task[taskHeader.key]"
|
<button (click)="openDetails(taskHeader.key, task[taskHeader.key])" *ngIf="task[taskHeader.key]" class="btn btn-default btn-xs" type="button">
|
||||||
class="btn btn-default btn-xs" type="button">
|
|
||||||
<span class="material-icons md-16 blue pull-right">open_in_new</span>
|
<span class="material-icons md-16 blue pull-right">open_in_new</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -97,10 +81,9 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="taskQueryResource" class="table-pagination">
|
<div *ngIf="taskQueryResource" class="divTablePagination">
|
||||||
<taskana-pagination (changePage)="changePage($event)" [(page)]="taskQueryResource.page"
|
<taskana-pagination (changePage)="changePage($event)" [(page)]="taskQueryResource.page" [numberOfItems]="taskQuery.length"
|
||||||
[numberOfItems]="taskQuery.length"
|
type="Entries"></taskana-pagination>
|
||||||
type="Entries"></taskana-pagination>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
.table-header {
|
.table-header {
|
||||||
background-color: $light-grey;
|
background-color: $light-grey;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-footer {
|
.table-footer {
|
||||||
|
|
@ -14,8 +15,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-cell {
|
.table-cell {
|
||||||
height: 34px;
|
height: 35px;
|
||||||
max-height: 34px;
|
max-height: 35px;
|
||||||
|
min-height: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.divDate {
|
.divDate {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
export class MetaInfoData {
|
export class MetaInfoData {
|
||||||
date: string;
|
date: string;
|
||||||
header: Array<string>;
|
header: Array<string>;
|
||||||
name: string;
|
expHeader: Array<string>;
|
||||||
rowDesc: string;
|
name: string;
|
||||||
totalDesc: string;
|
rowDesc: string;
|
||||||
|
totalDesc: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,11 @@
|
||||||
export class ReportInfoData {
|
export class ReportInfoData {
|
||||||
cells: Map<string, number>;
|
cells: Map<string, number>;
|
||||||
|
foldableRows: Map<string, Map<string, ReportInfoData>>;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ReportInfoDataIterable {
|
||||||
|
key: string;
|
||||||
|
val: ReportInfoData;
|
||||||
|
depth: number;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,26 +3,35 @@
|
||||||
<li>
|
<li>
|
||||||
<a style="display: none"></a>
|
<a style="display: none"></a>
|
||||||
</li>
|
</li>
|
||||||
<li (click)="selectTab('tasks')" role="presentation" class="active" [ngClass]="{'active':tabSelected === 'tasks'}">
|
<li (click)="selectTab('tasks')" [ngClass]="{'active':tabSelected === 'tasks'}" class="active"
|
||||||
<a href="#tasks-tab" data-toggle="tab" aria-controls="Tasks" role="tab" aria-expanded="true">Tasks</a>
|
role="presentation">
|
||||||
|
<a aria-controls="Tasks" aria-expanded="true" data-toggle="tab" href="#tasks-tab" role="tab">Tasks</a>
|
||||||
</li>
|
</li>
|
||||||
<li (click)="selectTab('workbaskets')" [ngClass]="{'active':tabSelected === 'workbaskets'}">
|
<li (click)="selectTab('workbaskets')" [ngClass]="{'active':tabSelected === 'workbaskets'}">
|
||||||
<a href="#workbaskets-tab" data-toggle="tab" aria-controls="Workbaskets" role="tab" aria-expanded="true">Workbaskets</a>
|
<a aria-controls="Workbaskets" aria-expanded="true" data-toggle="tab" href="#workbaskets-tab" role="tab">Workbaskets</a>
|
||||||
</li>
|
</li>
|
||||||
<li (click)="selectTab('classifications')" [ngClass]="{'active':tabSelected === 'classifications'}">
|
<li (click)="selectTab('classifications')" [ngClass]="{'active':tabSelected === 'classifications'}">
|
||||||
<a href="#classifications-tab" data-toggle="tab" aria-controls="Classifications" role="tab" data-toggle="tab"
|
<a aria-controls="Classifications" aria-expanded="true" data-toggle="tab" href="#classifications-tab"
|
||||||
aria-expanded="true">Classifications</a>
|
role="tab">Classifications</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li (click)="selectTab('timestamp')" [ngClass]="{'active':tabSelected === 'timestamp'}">
|
||||||
|
<a aria-controls="Timestamp" aria-expanded="true" data-toggle="tab" href="#timestamp-tab"
|
||||||
|
role="tab">Timestamp</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div role="tabpanel" class="tab-pane active" id="tasks-tab">
|
<div class="tab-pane active" id="tasks-tab" role="tabpanel">
|
||||||
<taskana-monitor-tasks></taskana-monitor-tasks>
|
<taskana-monitor-tasks></taskana-monitor-tasks>
|
||||||
</div>
|
</div>
|
||||||
<div role="tabpanel" class="tab-pane" id="workbaskets-tab">
|
<div class="tab-pane" id="workbaskets-tab" role="tabpanel">
|
||||||
<taskana-monitor-workbaskets></taskana-monitor-workbaskets>
|
<taskana-monitor-workbaskets></taskana-monitor-workbaskets>
|
||||||
</div>
|
</div>
|
||||||
<div role="tabpanel" class="tab-pane" id="classifications-tab">
|
<div class="tab-pane" id="classifications-tab" role="tabpanel">
|
||||||
<taskana-monitor-classification-tasks></taskana-monitor-classification-tasks>
|
<taskana-monitor-classification-tasks></taskana-monitor-classification-tasks>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane" id="timestamp-tab" role="tabpanel">
|
||||||
|
<taskana-monitor-timestamp></taskana-monitor-timestamp>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,22 @@
|
||||||
import { Component, OnInit, HostListener, OnDestroy } from '@angular/core';
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-monitor',
|
selector: 'taskana-monitor',
|
||||||
templateUrl: './monitor.component.html',
|
templateUrl: './monitor.component.html',
|
||||||
styleUrls: ['./monitor.component.scss']
|
styleUrls: ['./monitor.component.scss']
|
||||||
})
|
})
|
||||||
export class MonitorComponent implements OnInit, OnDestroy {
|
export class MonitorComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
tabSelected = 'tasks';
|
tabSelected = 'tasks';
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
ngOnDestroy(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
selectTab(tab) {
|
ngOnDestroy(): void {
|
||||||
this.tabSelected = tab;
|
}
|
||||||
}
|
|
||||||
|
selectTab(tab) {
|
||||||
|
this.tabSelected = tab;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,25 @@
|
||||||
import { CommonModule } from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import {FormsModule} from '@angular/forms';
|
||||||
import { AlertModule } from 'ngx-bootstrap';
|
import {AlertModule} from 'ngx-bootstrap';
|
||||||
import { ChartsModule } from 'ng2-charts';
|
import {ChartsModule} from 'ng2-charts';
|
||||||
import { TabsModule } from 'ngx-bootstrap/tabs';
|
import {TabsModule} from 'ngx-bootstrap/tabs';
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import {HttpClientModule} from '@angular/common/http';
|
||||||
import { AngularSvgIconModule } from 'angular-svg-icon';
|
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||||
import { MonitorRoutingModule } from './monitor-routing.module';
|
import {MonitorRoutingModule} from './monitor-routing.module';
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import {SharedModule} from '../shared/shared.module';
|
||||||
|
|
||||||
import { ReportComponent } from './report/report.component';
|
import {ReportComponent} from './report/report.component';
|
||||||
import { MonitorComponent } from './monitor.component';
|
import {MonitorComponent} from './monitor.component';
|
||||||
import { TasksComponent } from './tasks/tasks.component';
|
import {TasksComponent} from './tasks/tasks.component';
|
||||||
import { WorkbasketComponent } from './workbasket/workbasket.component';
|
import {WorkbasketComponent} from './workbasket/workbasket.component';
|
||||||
import { ClassificationTasksComponent } from './classification-tasks/classification-tasks.component';
|
import {ClassificationTasksComponent} from './classification-tasks/classification-tasks.component';
|
||||||
|
|
||||||
import { RestConnectorService } from './services/restConnector/rest-connector.service';
|
|
||||||
import {ReportRowComponent} from "./report/row/row.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";
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
|
@ -35,13 +38,14 @@ const DECLARATIONS = [
|
||||||
ReportComponent,
|
ReportComponent,
|
||||||
MonitorComponent,
|
MonitorComponent,
|
||||||
ClassificationTasksComponent,
|
ClassificationTasksComponent,
|
||||||
|
TimestampComponent,
|
||||||
ReportRowComponent
|
ReportRowComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: DECLARATIONS,
|
declarations: DECLARATIONS,
|
||||||
imports: MODULES,
|
imports: MODULES,
|
||||||
providers: [RestConnectorService]
|
providers: [RestConnectorService, MapToIterable]
|
||||||
})
|
})
|
||||||
export class MonitorModule {
|
export class MonitorModule {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,26 @@
|
||||||
<div *ngIf="reportData" class="report table table-striped">
|
<div *ngIf="reportData" class="report table table-body-striped">
|
||||||
<div class="table-header">
|
<div class="table-header">
|
||||||
<div class="table-row">
|
<div class="table-row">
|
||||||
<div class="table-cell--bold table-cell--justify">{{reportData.meta.rowDesc}}</div>
|
<div class="table-cell table-cell--bold table-cell--justify">{{reportData.meta.rowDesc}}</div>
|
||||||
<div *ngFor="let header of reportData.meta.header" class="table-cell--bold">{{header}}</div>
|
<ng-container *ngFor="let header of reportData.meta.expHeader; let i = index">
|
||||||
<div class="table-cell--bold">{{reportData.meta.totalDesc}}</div>
|
<ng-container *ngIf="i < currentExpHeaders">
|
||||||
</div>
|
<div class="table-cell table-cell--bold table-cell--justify">{{header}}</div>
|
||||||
</div>
|
</ng-container>
|
||||||
<div class="table-body">
|
</ng-container>
|
||||||
<div *ngFor="let row of reportData.rows | mapToIterable | orderBy:['key']" [headers]="reportData.meta.header"
|
<div *ngFor="let header of reportData.meta.header" class="table-cell table-cell--bold">{{header}}</div>
|
||||||
[row]="row" class="table-row" monitor-report-row>
|
<div class="table-cell table-cell--bold">{{reportData.meta.totalDesc}}</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="table-footer">
|
|
||||||
<div class="table-row">
|
|
||||||
<div class="table-cell--bold table-cell--justify">
|
|
||||||
{{reportData.meta.totalDesc}}
|
|
||||||
</div>
|
|
||||||
<div *ngFor="let header of reportData.meta.header" class="table-cell--bold">
|
|
||||||
{{reportData.sumRow.cells[header]}}
|
|
||||||
</div>
|
|
||||||
<div class="table-cell--bold">
|
|
||||||
{{reportData.sumRow.total}}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<monitor-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)"
|
||||||
|
[headers]="reportData.meta.header"
|
||||||
|
[maxTableDepth]="currentExpHeaders"
|
||||||
|
[row]="_sumRow"
|
||||||
|
bold="true"
|
||||||
|
class="table-footer"></monitor-report-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
import {ReportData} from 'app/monitor/models/report-data';
|
import {ReportData} from 'app/monitor/models/report-data';
|
||||||
|
import {ReportInfoDataIterable} from "../models/report-info-data";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-report',
|
selector: 'taskana-report',
|
||||||
|
|
@ -8,13 +9,36 @@ import {ReportData} from 'app/monitor/models/report-data';
|
||||||
})
|
})
|
||||||
export class ReportComponent implements OnInit {
|
export class ReportComponent implements OnInit {
|
||||||
|
|
||||||
@Input()
|
|
||||||
reportData: ReportData;
|
expHeaders: Array<number>;
|
||||||
|
currentExpHeaders: number = 0;
|
||||||
|
_sumRow: ReportInfoDataIterable;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _reportData: ReportData;
|
||||||
|
|
||||||
|
get reportData(): ReportData {
|
||||||
|
return this._reportData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set reportData(reportData: ReportData) {
|
||||||
|
this._reportData = reportData;
|
||||||
|
this.expHeaders = new Array<number>(Object.keys(reportData.rows).length + 1).fill(0);
|
||||||
|
this._sumRow = new ReportInfoDataIterable();
|
||||||
|
this._sumRow.val = reportData.sumRow;
|
||||||
|
this._sumRow.key = reportData.meta.totalDesc;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expandHeader(depth: number, index: number) {
|
||||||
|
this.expHeaders[index] = depth;
|
||||||
|
this.currentExpHeaders = Math.max(...this.expHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,19 @@
|
||||||
<ng-container *ngIf="headers && row">
|
<ng-container *ngIf="headers && flatRows">
|
||||||
<div (click)="toggleFold()" class="table-cell table-cell--justify">
|
<ng-container *ngFor="let row of flatRows; let i = index">
|
||||||
{{row.key}}
|
<div *ngIf="currentDepth >= row.depth" class="table-row">
|
||||||
</div>
|
<div *ngFor="let _ of range(row.depth)" class="table-cell"></div>
|
||||||
<div *ngFor="let header of headers" class="table-cell">
|
<div (click)="toggleFold(row.depth)"
|
||||||
{{row.val.cells[header]}}
|
[ngClass]="{'table-cell--clickable': maxDepth > row.depth, 'table-cell--bold' : bold}"
|
||||||
</div>
|
class="table-cell table-cell--justify">
|
||||||
<div class="table-cell--bold">
|
{{row.key}}
|
||||||
{{row.val.total}}
|
</div>
|
||||||
</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">
|
||||||
|
{{row.val.cells[header]}}
|
||||||
|
</div>
|
||||||
|
<div class="table-cell table-cell--bold">
|
||||||
|
{{row.val.total}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
.table-cell--clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
@ -1,41 +1,63 @@
|
||||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||||
import {ReportInfoDataIterable} from "../../models/report-info-data";
|
import {ReportInfoDataIterable} from "../../models/report-info-data";
|
||||||
|
import {MapToIterable} from "../../../shared/pipes/mapToIterable/mapToIterable";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: '[monitor-report-row]',
|
selector: 'monitor-report-row',
|
||||||
templateUrl: './row.component.html',
|
templateUrl: './row.component.html',
|
||||||
styleUrls: ['./row.component.scss']
|
styleUrls: ['./row.component.scss']
|
||||||
})
|
})
|
||||||
export class ReportRowComponent implements OnInit {
|
export class ReportRowComponent implements OnInit {
|
||||||
|
|
||||||
@Output()
|
@Input()
|
||||||
expand: EventEmitter<boolean> = new EventEmitter<boolean>();
|
headers: Array<string>;
|
||||||
@Input()
|
@Input()
|
||||||
headers: Array<string>;
|
bold: boolean = false;
|
||||||
expanded = false;
|
@Input()
|
||||||
foldable: boolean;
|
maxTableDepth: number = 0;
|
||||||
|
@Output()
|
||||||
|
expandedDepth: EventEmitter<number> = new EventEmitter<number>();
|
||||||
|
maxDepth: number;
|
||||||
|
currentDepth: number = 0;
|
||||||
|
flatRows: Array<ReportInfoDataIterable>;
|
||||||
|
|
||||||
constructor() {
|
constructor(private mapToIterable: MapToIterable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _row: ReportInfoDataIterable;
|
|
||||||
|
private _row: ReportInfoDataIterable;
|
||||||
get row(): ReportInfoDataIterable {
|
|
||||||
return this._row;
|
get row(): ReportInfoDataIterable {
|
||||||
}
|
return this._row;
|
||||||
|
}
|
||||||
@Input() set row(row: ReportInfoDataIterable) {
|
|
||||||
this._row = row;
|
@Input()
|
||||||
this.foldable = !!row.val.foldableRows;
|
set row(row: ReportInfoDataIterable) {
|
||||||
}
|
this._row = row;
|
||||||
|
this.flatRows = new Array<ReportInfoDataIterable>();
|
||||||
ngOnInit() {
|
this.maxDepth = this.flatten(row, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleFold() {
|
ngOnInit() {
|
||||||
if (this.foldable) {
|
}
|
||||||
this.expanded = !this.expanded;
|
|
||||||
this.expand.emit(this.expanded);
|
toggleFold(depth: number) {
|
||||||
}
|
this.currentDepth = depth == this.currentDepth && depth < this.maxDepth ? depth + 1 : depth;
|
||||||
|
this.expandedDepth.emit(this.currentDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
range(depth: number): Array<null> {
|
||||||
|
return new Array<null>(Math.max(depth, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private flatten(row: ReportInfoDataIterable, depth: number): number {
|
||||||
|
row.depth = depth;
|
||||||
|
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)))
|
||||||
}
|
}
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export class RestConnectorService {
|
||||||
|
|
||||||
getDailyEntryExitReport(): Observable<ReportData> {
|
getDailyEntryExitReport(): Observable<ReportData> {
|
||||||
return this.httpClient.get<ReportData>(environment.taskanaRestUrl
|
return this.httpClient.get<ReportData>(environment.taskanaRestUrl
|
||||||
+ monitorUrl + 'daily-entry-exit-report');
|
+ monitorUrl + 'timestamp-report');
|
||||||
}
|
}
|
||||||
|
|
||||||
getChartData(source: ReportData): Array<ChartData> {
|
getChartData(source: ReportData): Array<ChartData> {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<div *ngIf="reportData" class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h4>{{reportData.meta.name}} ({{reportData.meta.date | dateTimeZone}})</h4>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body blub">
|
||||||
|
<taskana-report [reportData]="reportData"></taskana-report>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {ReportData} from "../models/report-data";
|
||||||
|
import {RestConnectorService} from "../services/restConnector/rest-connector.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'taskana-monitor-timestamp',
|
||||||
|
templateUrl: './timestamp.component.html',
|
||||||
|
styleUrls: ['./timestamp.component.scss']
|
||||||
|
})
|
||||||
|
export class TimestampComponent implements OnInit {
|
||||||
|
|
||||||
|
reportData: ReportData;
|
||||||
|
|
||||||
|
constructor(private restConnectorService: RestConnectorService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.restConnectorService.getDailyEntryExitReport().subscribe((data: ReportData) => {
|
||||||
|
this.reportData = data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -300,7 +300,7 @@ span.flip {
|
||||||
}
|
}
|
||||||
|
|
||||||
taskana-workbasket-information, taskana-workbasket-access-items, taskana-workbaskets-distribution-targets, taskana-workbasket-details, taskana-monitor-tasks,
|
taskana-workbasket-information, taskana-workbasket-access-items, taskana-workbaskets-distribution-targets, taskana-workbasket-details, taskana-monitor-tasks,
|
||||||
taskana-monitor-workbaskets, taskana-monitor-classification-tasks {
|
taskana-monitor-workbaskets, taskana-monitor-classification-tasks, taskana-monitor-timestamp {
|
||||||
& .panel{
|
& .panel{
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
display: table;
|
display: table;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-striped div.table-body div.table-row:nth-of-type(odd) {
|
.table-striped .table-body .table-row:nth-of-type(odd),
|
||||||
|
.table-body-striped .table-body:nth-of-type(odd) {
|
||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -13,14 +14,18 @@
|
||||||
|
|
||||||
.table-header, .table-footer {
|
.table-header, .table-footer {
|
||||||
display: table-header-group;
|
display: table-header-group;
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-header > .table-row:last-child > .table-cell--bold {
|
.table-header > .table-row:last-child > .table-cell {
|
||||||
border-bottom: 2px solid #ddd;
|
border-bottom: 2px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-footer > .table-row > .table-cell--bold, .table-header > .table-row > .table-cell--bold {
|
.table-footer > .table-row:first-child > .table-cell {
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-footer > .table-row:last-of-type > .table-cell,
|
||||||
|
.table-header > .table-row > .table-cell {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,7 +37,7 @@
|
||||||
display: table-row;
|
display: table-row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-cell, .table-cell--bold {
|
.table-cell {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
@ -40,6 +45,7 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #ddd;
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-cell--justify {
|
.table-cell--justify {
|
||||||
|
|
@ -59,6 +65,7 @@
|
||||||
cursor: zoom-in;
|
cursor: zoom-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&.zoom-out {
|
&.zoom-out {
|
||||||
cursor: zoom-out;
|
cursor: zoom-out;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue