feature/810 Add querying by planned date in monitor screen

This commit is contained in:
Martin Rojas Miguel Angel 2019-03-13 17:00:55 +01:00 committed by Mustapha Zorgati
parent 5d54e99b6f
commit 7720baaa2b
49 changed files with 1086 additions and 822 deletions

View File

@ -12,7 +12,6 @@ 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.MonitorQueryItem; import pro.taskana.impl.report.item.MonitorQueryItem;
import pro.taskana.impl.report.preprocessor.DaysToWorkingDaysPreProcessor; import pro.taskana.impl.report.preprocessor.DaysToWorkingDaysPreProcessor;
import pro.taskana.impl.report.structure.Report;
import pro.taskana.mappings.TaskMonitorMapper; import pro.taskana.mappings.TaskMonitorMapper;
import pro.taskana.report.CategoryReport; import pro.taskana.report.CategoryReport;
@ -58,10 +57,4 @@ public class CategoryReportBuilderImpl
LOGGER.debug("exit from buildReport()."); LOGGER.debug("exit from buildReport().");
} }
} }
@Override
public Report<MonitorQueryItem, TimeIntervalColumnHeader> buildPlannedDateBasedReport()
throws NotAuthorizedException, InvalidArgumentException {
throw new java.lang.UnsupportedOperationException("Not supported yet.");
}
} }

View File

@ -1,7 +1,10 @@
package pro.taskana.impl; package pro.taskana.impl;
import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
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;
@ -13,9 +16,6 @@ import pro.taskana.impl.report.preprocessor.DaysToWorkingDaysPreProcessor;
import pro.taskana.mappings.TaskMonitorMapper; import pro.taskana.mappings.TaskMonitorMapper;
import pro.taskana.report.ClassificationReport; import pro.taskana.report.ClassificationReport;
import pro.taskana.report.ClassificationReport.DetailedClassificationReport; import pro.taskana.report.ClassificationReport.DetailedClassificationReport;
import pro.taskana.report.Report;
import java.util.List;
/** /**
* The implementation of ClassificationReportBuilder. * The implementation of ClassificationReportBuilder.
@ -79,9 +79,4 @@ public class ClassificationReportBuilderImpl
LOGGER.debug("exit from buildDetailedReport()."); LOGGER.debug("exit from buildDetailedReport().");
} }
} }
@Override
public Report<MonitorQueryItem, TimeIntervalColumnHeader> buildPlannedDateBasedReport() throws NotAuthorizedException, InvalidArgumentException {
throw new java.lang.UnsupportedOperationException("Not supported yet.");
}
} }

View File

@ -13,7 +13,6 @@ 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.MonitorQueryItem; import pro.taskana.impl.report.item.MonitorQueryItem;
import pro.taskana.impl.report.preprocessor.DaysToWorkingDaysPreProcessor; import pro.taskana.impl.report.preprocessor.DaysToWorkingDaysPreProcessor;
import pro.taskana.impl.report.structure.Report;
import pro.taskana.mappings.TaskMonitorMapper; import pro.taskana.mappings.TaskMonitorMapper;
import pro.taskana.report.CustomFieldValueReport; import pro.taskana.report.CustomFieldValueReport;
@ -66,11 +65,4 @@ public class CustomFieldValueReportBuilderImpl
LOGGER.debug("exit from buildReport()."); LOGGER.debug("exit from buildReport().");
} }
} }
@Override
public Report<MonitorQueryItem, TimeIntervalColumnHeader> buildPlannedDateBasedReport()
throws NotAuthorizedException, InvalidArgumentException {
throw new java.lang.UnsupportedOperationException("Not supported yet.");
}
} }

View File

@ -8,11 +8,8 @@ import org.slf4j.LoggerFactory;
import pro.taskana.TaskState; import pro.taskana.TaskState;
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.NotAuthorizedException; import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.impl.report.header.TaskStatusColumnHeader;
import pro.taskana.impl.report.item.TaskQueryItem; import pro.taskana.impl.report.item.TaskQueryItem;
import pro.taskana.impl.report.structure.Report;
import pro.taskana.mappings.TaskMonitorMapper; import pro.taskana.mappings.TaskMonitorMapper;
import pro.taskana.report.TaskStatusReport; import pro.taskana.report.TaskStatusReport;
@ -59,11 +56,4 @@ public class TaskStatusReportBuilderImpl implements TaskStatusReport.Builder {
LOGGER.debug("exit from buildReport()."); LOGGER.debug("exit from buildReport().");
} }
} }
@Override
public Report<TaskQueryItem, TaskStatusColumnHeader> buildPlannedDateBasedReport()
throws NotAuthorizedException, InvalidArgumentException {
throw new java.lang.UnsupportedOperationException("Not supported yet.");
}
} }

View File

@ -89,6 +89,12 @@ public class TimeIntervalColumnHeader implements ColumnHeader<AgeQueryItem> {
return "<" + this.getUpperAgeLimit(); return "<" + this.getUpperAgeLimit();
} else if (this.getUpperAgeLimit() == Integer.MAX_VALUE) { } else if (this.getUpperAgeLimit() == Integer.MAX_VALUE) {
return ">" + this.getLowerAgeLimit(); return ">" + this.getLowerAgeLimit();
} else if (this.getLowerAgeLimit() == -1) {
return "-1 day";
} else if (this.getLowerAgeLimit() == 1) {
return "+1 day";
} else if (this.getLowerAgeLimit() == 0) {
return "today";
} else if (this.getLowerAgeLimit() == this.getUpperAgeLimit()) { } else if (this.getLowerAgeLimit() == this.getUpperAgeLimit()) {
return this.getUpperAgeLimit() + ""; return this.getUpperAgeLimit() + "";
} else if (this.getLowerAgeLimit() != this.getUpperAgeLimit()) { } else if (this.getLowerAgeLimit() != this.getUpperAgeLimit()) {

View File

@ -105,6 +105,5 @@ public abstract class Report<I extends QueryItem, H extends ColumnHeader<? super
public interface Builder<I extends QueryItem, H extends ColumnHeader<? super I>> { public interface Builder<I extends QueryItem, H extends ColumnHeader<? super I>> {
Report<I, H> buildReport() throws NotAuthorizedException, InvalidArgumentException; Report<I, H> buildReport() throws NotAuthorizedException, InvalidArgumentException;
Report<I, H> buildPlannedDateBasedReport() throws NotAuthorizedException, InvalidArgumentException;
} }
} }

View File

@ -10,13 +10,13 @@ import org.apache.ibatis.annotations.Select;
import pro.taskana.CustomField; import pro.taskana.CustomField;
import pro.taskana.TaskState; import pro.taskana.TaskState;
import pro.taskana.report.Timestamp;
import pro.taskana.impl.SelectedItem; import pro.taskana.impl.SelectedItem;
import pro.taskana.impl.report.CombinedClassificationFilter; import pro.taskana.impl.report.CombinedClassificationFilter;
import pro.taskana.impl.report.item.TimestampQueryItem;
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.impl.report.item.TaskQueryItem; import pro.taskana.impl.report.item.TaskQueryItem;
import pro.taskana.impl.report.item.TimestampQueryItem;
import pro.taskana.report.Timestamp;
/** /**
* This class is the mybatis mapping of task monitoring. * This class is the mybatis mapping of task monitoring.
@ -24,9 +24,10 @@ import pro.taskana.impl.report.item.TaskQueryItem;
public interface TaskMonitorMapper { public interface TaskMonitorMapper {
@Select("<script>" @Select("<script>"
+ "<if test=\"_databaseId == 'db2'\">SELECT T.WORKBASKET_KEY, (DAYS(T.DUE) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "SELECT B.WORKBASKET_KEY, B.AGE_IN_DAYS, COUNT(B.AGE_IN_DAYS) AS NUMBER_OF_TASKS FROM ("
+ "<if test=\"_databaseId == 'h2'\">SELECT T.WORKBASKET_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, T.DUE) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'db2'\">SELECT T.WORKBASKET_KEY, (DAYS(T.DUE) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT T.WORKBASKET_KEY, DATE_PART('DAY', T.DUE - CURRENT_TIMESTAMP) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'h2'\">SELECT T.WORKBASKET_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, T.DUE) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT T.WORKBASKET_KEY, DATE_PART('DAY', T.DUE - CURRENT_TIMESTAMP) as AGE_IN_DAYS </if> "
+ "FROM TASK AS T LEFT JOIN ATTACHMENT AS A ON T.ID = A.TASK_ID " + "FROM TASK AS T LEFT JOIN ATTACHMENT AS A ON T.ID = A.TASK_ID "
+ "<where>" + "<where>"
+ "<if test=\"workbasketIds != null\">" + "<if test=\"workbasketIds != null\">"
@ -60,10 +61,8 @@ public interface TaskMonitorMapper {
+ "</if>" + "</if>"
+ "AND T.DUE IS NOT NULL " + "AND T.DUE IS NOT NULL "
+ "</where>" + "</where>"
+ "<if test=\"_databaseId == 'db2'\">GROUP BY T.WORKBASKET_KEY, (DAYS(T.DUE) - DAYS(CURRENT_TIMESTAMP))</if> " + ") AS B "
+ "<if test=\"_databaseId == 'h2'\">GROUP BY T.WORKBASKET_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, T.DUE)</if> " + "GROUP BY B.WORKBASKET_KEY, B.AGE_IN_DAYS"
+ "<if test=\"_databaseId == 'postgres'\">GROUP BY T.WORKBASKET_KEY, DATE_PART('DAY', T.DUE - CURRENT_TIMESTAMP)</if> "
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
+ "</script>") + "</script>")
@Results({ @Results({
@Result(column = "WORKBASKET_KEY", property = "key"), @Result(column = "WORKBASKET_KEY", property = "key"),
@ -79,9 +78,10 @@ public interface TaskMonitorMapper {
@Param("combinedClassificationFilter") List<CombinedClassificationFilter> combinedClassificationFilter); @Param("combinedClassificationFilter") List<CombinedClassificationFilter> combinedClassificationFilter);
@Select("<script>" @Select("<script>"
+ "<if test=\"_databaseId == 'db2'\">SELECT T.WORKBASKET_KEY, (DAYS(T.PLANNED) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "SELECT B.WORKBASKET_KEY, B.AGE_IN_DAYS, COUNT(B.AGE_IN_DAYS) AS NUMBER_OF_TASKS FROM ("
+ "<if test=\"_databaseId == 'h2'\">SELECT T.WORKBASKET_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, T.PLANNED) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'db2'\">SELECT T.WORKBASKET_KEY, (DAYS(T.PLANNED) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT T.WORKBASKET_KEY, DATE_PART('DAY', T.PLANNED - CURRENT_TIMESTAMP) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'h2'\">SELECT T.WORKBASKET_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, T.PLANNED) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT T.WORKBASKET_KEY, DATE_PART('DAY', T.PLANNED - CURRENT_TIMESTAMP) as AGE_IN_DAYS </if> "
+ "FROM TASK AS T LEFT JOIN ATTACHMENT AS A ON T.ID = A.TASK_ID " + "FROM TASK AS T LEFT JOIN ATTACHMENT AS A ON T.ID = A.TASK_ID "
+ "<where>" + "<where>"
+ "<if test=\"workbasketIds != null\">" + "<if test=\"workbasketIds != null\">"
@ -115,16 +115,15 @@ public interface TaskMonitorMapper {
+ "</if>" + "</if>"
+ "AND T.PLANNED IS NOT NULL " + "AND T.PLANNED IS NOT NULL "
+ "</where>" + "</where>"
+ "<if test=\"_databaseId == 'db2'\">GROUP BY T.WORKBASKET_KEY, (DAYS(T.PLANNED) - DAYS(CURRENT_TIMESTAMP))</if> " + ") AS B "
+ "<if test=\"_databaseId == 'h2'\">GROUP BY T.WORKBASKET_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, T.PLANNED)</if> " + "GROUP BY B.WORKBASKET_KEY, B.AGE_IN_DAYS"
+ "<if test=\"_databaseId == 'postgres'\">GROUP BY T.WORKBASKET_KEY, DATE_PART('DAY', T.PLANNED - CURRENT_TIMESTAMP)</if> "
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
+ "</script>") + "</script>")
@Results({ @Results({
@Result(column = "WORKBASKET_KEY", property = "key"), @Result(column = "WORKBASKET_KEY", property = "key"),
@Result(column = "AGE_IN_DAYS", property = "ageInDays"), @Result(column = "AGE_IN_DAYS", property = "ageInDays"),
@Result(column = "NUMBER_OF_TASKS", property = "numberOfTasks")}) @Result(column = "NUMBER_OF_TASKS", property = "numberOfTasks")})
List<MonitorQueryItem> getTaskCountOfWorkbasketsBasedOnPlannedDate(@Param("workbasketIds") List<String> workbasketIds, List<MonitorQueryItem> getTaskCountOfWorkbasketsBasedOnPlannedDate(
@Param("workbasketIds") List<String> workbasketIds,
@Param("states") List<TaskState> states, @Param("states") List<TaskState> states,
@Param("categories") List<String> categories, @Param("categories") List<String> categories,
@Param("domains") List<String> domains, @Param("domains") List<String> domains,
@ -134,9 +133,10 @@ public interface TaskMonitorMapper {
@Param("combinedClassificationFilter") List<CombinedClassificationFilter> combinedClassificationFilter); @Param("combinedClassificationFilter") List<CombinedClassificationFilter> combinedClassificationFilter);
@Select("<script>" @Select("<script>"
+ "<if test=\"_databaseId == 'db2'\">SELECT CLASSIFICATION_CATEGORY, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "SELECT B.CLASSIFICATION_CATEGORY, B.AGE_IN_DAYS, COUNT(B.AGE_IN_DAYS) AS NUMBER_OF_TASKS FROM ("
+ "<if test=\"_databaseId == 'h2'\">SELECT CLASSIFICATION_CATEGORY, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'db2'\">SELECT CLASSIFICATION_CATEGORY, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT CLASSIFICATION_CATEGORY, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'h2'\">SELECT CLASSIFICATION_CATEGORY, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT CLASSIFICATION_CATEGORY, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP) as AGE_IN_DAYS </if> "
+ "FROM TASK " + "FROM TASK "
+ "<where>" + "<where>"
+ "<if test=\"workbasketIds != null\">" + "<if test=\"workbasketIds != null\">"
@ -162,10 +162,8 @@ public interface TaskMonitorMapper {
+ "</if>" + "</if>"
+ "AND DUE IS NOT NULL " + "AND DUE IS NOT NULL "
+ "</where>" + "</where>"
+ "<if test=\"_databaseId == 'db2'\">GROUP BY CLASSIFICATION_CATEGORY, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP))</if> " + ") AS B "
+ "<if test=\"_databaseId == 'h2'\">GROUP BY CLASSIFICATION_CATEGORY, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE)</if> " + "GROUP BY B.CLASSIFICATION_CATEGORY, B.AGE_IN_DAYS "
+ "<if test=\"_databaseId == 'postgres'\">GROUP BY CLASSIFICATION_CATEGORY, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP)</if> "
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
+ "</script>") + "</script>")
@Results({ @Results({
@Result(column = "CLASSIFICATION_CATEGORY", property = "key"), @Result(column = "CLASSIFICATION_CATEGORY", property = "key"),
@ -180,9 +178,10 @@ public interface TaskMonitorMapper {
@Param("customAttributeFilter") Map<CustomField, String> customAttributeFilter); @Param("customAttributeFilter") Map<CustomField, String> customAttributeFilter);
@Select("<script>" @Select("<script>"
+ "<if test=\"_databaseId == 'db2'\">SELECT CLASSIFICATION_KEY, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "SELECT B.CLASSIFICATION_KEY, B.AGE_IN_DAYS, COUNT(B.AGE_IN_DAYS) AS NUMBER_OF_TASKS FROM ("
+ "<if test=\"_databaseId == 'h2'\">SELECT CLASSIFICATION_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'db2'\">SELECT CLASSIFICATION_KEY, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT CLASSIFICATION_KEY, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'h2'\">SELECT CLASSIFICATION_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT CLASSIFICATION_KEY, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP) as AGE_IN_DAYS </if> "
+ "FROM TASK " + "FROM TASK "
+ "<where>" + "<where>"
+ "<if test=\"workbasketIds != null\">" + "<if test=\"workbasketIds != null\">"
@ -208,10 +207,8 @@ public interface TaskMonitorMapper {
+ "</if>" + "</if>"
+ "AND DUE IS NOT NULL " + "AND DUE IS NOT NULL "
+ "</where>" + "</where>"
+ "<if test=\"_databaseId == 'db2'\">GROUP BY CLASSIFICATION_KEY, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP))</if> " + ") AS B "
+ "<if test=\"_databaseId == 'h2'\">GROUP BY CLASSIFICATION_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE)</if> " + "GROUP BY B.CLASSIFICATION_KEY, B.AGE_IN_DAYS "
+ "<if test=\"_databaseId == 'postgres'\">GROUP BY CLASSIFICATION_KEY, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP)</if> "
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
+ "</script>") + "</script>")
@Results({ @Results({
@Result(column = "CLASSIFICATION_KEY", property = "key"), @Result(column = "CLASSIFICATION_KEY", property = "key"),
@ -226,9 +223,10 @@ public interface TaskMonitorMapper {
@Param("customAttributeFilter") Map<CustomField, String> customAttributeFilter); @Param("customAttributeFilter") Map<CustomField, String> customAttributeFilter);
@Select("<script>" @Select("<script>"
+ "<if test=\"_databaseId == 'db2'\">SELECT T.CLASSIFICATION_KEY as TASK_CLASSIFICATION_KEY, A.CLASSIFICATION_KEY as ATTACHMENT_CLASSIFICATION_KEY, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "SELECT B.TASK_CLASSIFICATION_KEY, B.ATTACHMENT_CLASSIFICATION_KEY, B.AGE_IN_DAYS, COUNT(B.AGE_IN_DAYS) AS NUMBER_OF_TASKS FROM ("
+ "<if test=\"_databaseId == 'h2'\">SELECT T.CLASSIFICATION_KEY as TASK_CLASSIFICATION_KEY, A.CLASSIFICATION_KEY as ATTACHMENT_CLASSIFICATION_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'db2'\">SELECT T.CLASSIFICATION_KEY as TASK_CLASSIFICATION_KEY, A.CLASSIFICATION_KEY as ATTACHMENT_CLASSIFICATION_KEY, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT T.CLASSIFICATION_KEY as TASK_CLASSIFICATION_KEY, A.CLASSIFICATION_KEY as ATTACHMENT_CLASSIFICATION_KEY, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'h2'\">SELECT T.CLASSIFICATION_KEY as TASK_CLASSIFICATION_KEY, A.CLASSIFICATION_KEY as ATTACHMENT_CLASSIFICATION_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT T.CLASSIFICATION_KEY as TASK_CLASSIFICATION_KEY, A.CLASSIFICATION_KEY as ATTACHMENT_CLASSIFICATION_KEY, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP) as AGE_IN_DAYS </if> "
+ "FROM TASK AS T LEFT JOIN ATTACHMENT AS A ON T.ID = A.TASK_ID " + "FROM TASK AS T LEFT JOIN ATTACHMENT AS A ON T.ID = A.TASK_ID "
+ "<where>" + "<where>"
+ "<if test=\"workbasketIds != null\">" + "<if test=\"workbasketIds != null\">"
@ -254,10 +252,8 @@ public interface TaskMonitorMapper {
+ "</if>" + "</if>"
+ "AND DUE IS NOT NULL " + "AND DUE IS NOT NULL "
+ "</where>" + "</where>"
+ "<if test=\"_databaseId == 'db2'\">GROUP BY T.CLASSIFICATION_KEY, A.CLASSIFICATION_KEY, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP))</if> " + ") AS B "
+ "<if test=\"_databaseId == 'h2'\">GROUP BY T.CLASSIFICATION_KEY, A.CLASSIFICATION_KEY, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE)</if> " + "GROUP BY B.TASK_CLASSIFICATION_KEY, B.ATTACHMENT_CLASSIFICATION_KEY, B.AGE_IN_DAYS "
+ "<if test=\"_databaseId == 'postgres'\">GROUP BY T.CLASSIFICATION_KEY, A.CLASSIFICATION_KEY, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP)</if> "
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
+ "</script>") + "</script>")
@Results({ @Results({
@Result(column = "TASK_CLASSIFICATION_KEY", property = "key"), @Result(column = "TASK_CLASSIFICATION_KEY", property = "key"),
@ -274,9 +270,10 @@ public interface TaskMonitorMapper {
@Param("customAttributeFilter") Map<CustomField, String> customAttributeFilter); @Param("customAttributeFilter") Map<CustomField, String> customAttributeFilter);
@Select("<script>" @Select("<script>"
+ "<if test=\"_databaseId == 'db2'\">SELECT ${customField} as CUSTOM_FIELD, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "SELECT B.CUSTOM_FIELD, B.AGE_IN_DAYS, COUNT(B.AGE_IN_DAYS) AS NUMBER_OF_TASKS FROM ("
+ "<if test=\"_databaseId == 'h2'\">SELECT ${customField} as CUSTOM_FIELD, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'db2'\">SELECT ${customField} as CUSTOM_FIELD, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP)) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT ${customField} as CUSTOM_FIELD, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP) as AGE_IN_DAYS, COUNT(*) as NUMBER_OF_TASKS</if> " + "<if test=\"_databaseId == 'h2'\">SELECT ${customField} as CUSTOM_FIELD, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE) as AGE_IN_DAYS </if> "
+ "<if test=\"_databaseId == 'postgres'\">SELECT ${customField} as CUSTOM_FIELD, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP) as AGE_IN_DAYS </if> "
+ "FROM TASK " + "FROM TASK "
+ "<where>" + "<where>"
+ "<if test=\"workbasketIds != null\">" + "<if test=\"workbasketIds != null\">"
@ -302,10 +299,8 @@ public interface TaskMonitorMapper {
+ "</if>" + "</if>"
+ "AND DUE IS NOT NULL " + "AND DUE IS NOT NULL "
+ "</where>" + "</where>"
+ "<if test=\"_databaseId == 'db2'\">GROUP BY ${customField}, (DAYS(DUE) - DAYS(CURRENT_TIMESTAMP))</if> " + ") AS B "
+ "<if test=\"_databaseId == 'h2'\">GROUP BY ${customField}, DATEDIFF('DAY', CURRENT_TIMESTAMP, DUE)</if> " + "GROUP BY B.CUSTOM_FIELD, B.AGE_IN_DAYS "
+ "<if test=\"_databaseId == 'postgres'\">GROUP BY ${customField}, DATE_PART('DAY', DUE - CURRENT_TIMESTAMP)</if> "
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
+ "</script>") + "</script>")
@Results({ @Results({
@Result(column = "CUSTOM_FIELD", property = "key"), @Result(column = "CUSTOM_FIELD", property = "key"),

View File

@ -7,8 +7,8 @@ import java.util.stream.Stream;
import pro.taskana.TaskState; import pro.taskana.TaskState;
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.item.TaskQueryItem;
import pro.taskana.impl.report.header.TaskStatusColumnHeader; import pro.taskana.impl.report.header.TaskStatusColumnHeader;
import pro.taskana.impl.report.item.TaskQueryItem;
import pro.taskana.impl.report.structure.Report; import pro.taskana.impl.report.structure.Report;
/** /**
@ -29,6 +29,9 @@ public class TaskStatusReport extends Report<TaskQueryItem, TaskStatusColumnHead
*/ */
public interface Builder extends Report.Builder<TaskQueryItem, TaskStatusColumnHeader> { public interface Builder extends Report.Builder<TaskQueryItem, TaskStatusColumnHeader> {
@Override
TaskStatusReport buildReport() throws NotAuthorizedException, InvalidArgumentException;
/** /**
* Adds a list of states to the builder. The created report contains only tasks with a state in this list. * Adds a list of states to the builder. The created report contains only tasks with a state in this list.
* *
@ -46,8 +49,5 @@ public class TaskStatusReport extends Report<TaskQueryItem, TaskStatusColumnHead
* @return the Builder * @return the Builder
*/ */
Builder domainIn(List<String> domains); Builder domainIn(List<String> domains);
@Override
TaskStatusReport buildReport() throws NotAuthorizedException, InvalidArgumentException;
} }
} }

View File

@ -36,7 +36,6 @@ public class WorkbasketReport extends Report<MonitorQueryItem, TimeIntervalColum
/** /**
* buildPlannedDateBasedReport is querying grouping by plannedDate instead of due date. * buildPlannedDateBasedReport is querying grouping by plannedDate instead of due date.
*/ */
@Override
WorkbasketReport buildPlannedDateBasedReport() throws NotAuthorizedException, InvalidArgumentException; WorkbasketReport buildPlannedDateBasedReport() throws NotAuthorizedException, InvalidArgumentException;
/** /**

View File

@ -28,6 +28,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import pro.taskana.CustomField; import pro.taskana.CustomField;
import pro.taskana.TaskState; import pro.taskana.TaskState;
import pro.taskana.TaskanaRole;
import pro.taskana.configuration.TaskanaEngineConfiguration; import pro.taskana.configuration.TaskanaEngineConfiguration;
import pro.taskana.exceptions.InvalidArgumentException; import pro.taskana.exceptions.InvalidArgumentException;
import pro.taskana.exceptions.NotAuthorizedException; import pro.taskana.exceptions.NotAuthorizedException;
@ -66,7 +67,8 @@ public class WorkbasketReportBuilderImplTest {
} }
@Test @Test
public void testGetTotalNumbersOfWorkbasketReportBasedOnDueDate() throws InvalidArgumentException, NotAuthorizedException { public void testGetTotalNumbersOfWorkbasketReportBasedOnDueDate()
throws InvalidArgumentException, NotAuthorizedException {
List<String> workbasketIds = Collections.singletonList("WBI:000000000000000000000000000000000001"); List<String> workbasketIds = Collections.singletonList("WBI:000000000000000000000000000000000001");
List<TaskState> states = Arrays.asList(TaskState.CLAIMED, TaskState.READY); List<TaskState> states = Arrays.asList(TaskState.CLAIMED, TaskState.READY);
List<String> categories = Collections.singletonList("EXTERN"); List<String> categories = Collections.singletonList("EXTERN");
@ -292,7 +294,8 @@ public class WorkbasketReportBuilderImplTest {
} }
@Test @Test
public void testGetTotalNumbersOfWorkbasketReportBasedOnCreatedDate() throws InvalidArgumentException, NotAuthorizedException { public void testGetTotalNumbersOfWorkbasketReportBasedOnCreatedDate()
throws InvalidArgumentException, NotAuthorizedException {
List<String> workbasketIds = Collections.singletonList("WBI:000000000000000000000000000000000001"); List<String> workbasketIds = Collections.singletonList("WBI:000000000000000000000000000000000001");
List<TaskState> states = Arrays.asList(TaskState.CLAIMED, TaskState.READY); List<TaskState> states = Arrays.asList(TaskState.CLAIMED, TaskState.READY);
List<String> categories = Collections.singletonList("EXTERN"); List<String> categories = Collections.singletonList("EXTERN");
@ -310,9 +313,9 @@ public class WorkbasketReportBuilderImplTest {
monitorQueryItem.setKey("WBI:000000000000000000000000000000000001"); monitorQueryItem.setKey("WBI:000000000000000000000000000000000001");
monitorQueryItem.setNumberOfTasks(1); monitorQueryItem.setNumberOfTasks(1);
expectedResult.add(monitorQueryItem); expectedResult.add(monitorQueryItem);
doReturn(expectedResult).when(taskMonitorMapperMock).getTaskCountOfWorkbasketsBasedOnPlannedDate(workbasketIds, states, when(taskMonitorMapperMock.getTaskCountOfWorkbasketsBasedOnPlannedDate(workbasketIds, states,
categories, domains, classificationIds, excludedClassificationIds, customAttributeFilter, categories, domains, classificationIds, excludedClassificationIds, customAttributeFilter,
combinedClassificationFilter); combinedClassificationFilter)).thenReturn(expectedResult);
WorkbasketReport actualResult = cut.createWorkbasketReportBuilder() WorkbasketReport actualResult = cut.createWorkbasketReportBuilder()
.workbasketIdIn(workbasketIds) .workbasketIdIn(workbasketIds)
@ -327,12 +330,19 @@ public class WorkbasketReportBuilderImplTest {
verify(taskanaEngineImplMock, times(1)) verify(taskanaEngineImplMock, times(1))
.openConnection(); .openConnection();
verify(taskanaEngineImplMock, times(1)).checkRoleMembership(any()); verify(taskanaEngineImplMock, times(1)).checkRoleMembership(TaskanaRole.MONITOR, TaskanaRole.ADMIN);
verify(taskanaEngineImplMock, times(2)).getConfiguration(); verify(taskanaEngineImplMock, times(2)).getConfiguration();
verify(taskanaEngineConfiguration, times(1)).isGermanPublicHolidaysEnabled(); verify(taskanaEngineConfiguration, times(1)).isGermanPublicHolidaysEnabled();
verify(taskanaEngineConfiguration, times(1)).getCustomHolidays(); verify(taskanaEngineConfiguration, times(1)).getCustomHolidays();
verify(taskMonitorMapperMock, times(1)).getTaskCountOfWorkbasketsBasedOnPlannedDate(any(), any(), any(), any(), verify(taskMonitorMapperMock, times(1)).getTaskCountOfWorkbasketsBasedOnPlannedDate(
any(), any(), any(), any()); workbasketIds,
states,
categories,
domains,
classificationIds,
excludedClassificationIds,
customAttributeFilter,
combinedClassificationFilter);
verify(taskanaEngineImplMock, times(1)).returnConnection(); verify(taskanaEngineImplMock, times(1)).returnConnection();
verifyNoMoreInteractions(taskanaEngineImplMock, taskMonitorMapperMock, taskanaEngineConfiguration); verifyNoMoreInteractions(taskanaEngineImplMock, taskMonitorMapperMock, taskanaEngineConfiguration);

View File

@ -63,15 +63,15 @@ public class MonitorController {
@GetMapping(path = "/tasks-workbasket-report") @GetMapping(path = "/tasks-workbasket-report")
@Transactional(readOnly = true, rollbackFor = Exception.class) @Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<?> getTasksWorkbasketReport( public ResponseEntity<?> getTasksWorkbasketReport(
@RequestParam(value = "daysInPast") int daysInPast,
@RequestParam(value = "states") List<TaskState> states) @RequestParam(value = "states") List<TaskState> states)
throws NotAuthorizedException, InvalidArgumentException { throws NotAuthorizedException, InvalidArgumentException {
LOGGER.debug("Entry to getTasksWorkbasketReport()"); LOGGER.debug("Entry to getTasksWorkbasketReport()");
ReportResource report = reportAssembler.toResource( ReportResource report = reportAssembler.toResource(
taskMonitorService.createWorkbasketReportBuilder() taskMonitorService.createWorkbasketReportBuilder()
.stateIn(states) .withColumnHeaders(getRangeTimeInterval())
.withColumnHeaders(getTasksWorkbasketsTimeInterval(daysInPast)).buildReport(), daysInPast, states); .buildReport(), states);
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from getTasksWorkbasketReport(), returning {}", report); LOGGER.debug("Exit from getTasksWorkbasketReport(), returning {}", report);
} }
@ -92,7 +92,7 @@ public class MonitorController {
ReportResource report = reportAssembler.toResource( ReportResource report = reportAssembler.toResource(
taskMonitorService.createWorkbasketReportBuilder() taskMonitorService.createWorkbasketReportBuilder()
.stateIn(states) .stateIn(states)
.withColumnHeaders(getTasksWorkbasketsTimeInterval(daysInPast)).buildPlannedDateBasedReport(), .withColumnHeaders(getDateTimeInterval(daysInPast)).buildPlannedDateBasedReport(),
daysInPast, states); daysInPast, states);
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from getTasksWorkbasketPlannedDateReport(), returning {}", report); LOGGER.debug("Exit from getTasksWorkbasketPlannedDateReport(), returning {}", report);
@ -111,7 +111,7 @@ public class MonitorController {
ReportResource report = reportAssembler.toResource( ReportResource report = reportAssembler.toResource(
taskMonitorService.createClassificationReportBuilder() taskMonitorService.createClassificationReportBuilder()
.withColumnHeaders(getTaskClassificationTimeInterval()) .withColumnHeaders(getRangeTimeInterval())
.buildReport()); .buildReport());
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
@ -136,7 +136,7 @@ public class MonitorController {
.buildReport())); .buildReport()));
} }
private List<TimeIntervalColumnHeader> getTaskClassificationTimeInterval() { private List<TimeIntervalColumnHeader> getRangeTimeInterval() {
return Stream.concat(Stream.concat( return Stream.concat(Stream.concat(
Stream.of(new TimeIntervalColumnHeader.Range(Integer.MIN_VALUE, -10), Stream.of(new TimeIntervalColumnHeader.Range(Integer.MIN_VALUE, -10),
new TimeIntervalColumnHeader.Range(-10, -5) new TimeIntervalColumnHeader.Range(-10, -5)
@ -150,7 +150,7 @@ public class MonitorController {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private List<TimeIntervalColumnHeader> getTasksWorkbasketsTimeInterval(int daysInPast) { private List<TimeIntervalColumnHeader> getDateTimeInterval(int daysInPast) {
List<TimeIntervalColumnHeader> columnHeaders = new ArrayList<>(); List<TimeIntervalColumnHeader> columnHeaders = new ArrayList<>();
for (int i = 0; i <= daysInPast; i++) { for (int i = 0; i <= daysInPast; i++) {

View File

@ -52,11 +52,20 @@ public class ReportAssembler {
return resource; return resource;
} }
public ReportResource toResource(WorkbasketReport report, List<TaskState> states)
throws NotAuthorizedException, InvalidArgumentException {
ReportResource resource = toReportResource(report);
resource.add(
linkTo(methodOn(MonitorController.class).getTasksWorkbasketReport(states))
.withSelfRel().expand());
return resource;
}
public ReportResource toResource(WorkbasketReport report, int daysInPast, List<TaskState> states) public ReportResource toResource(WorkbasketReport report, int daysInPast, List<TaskState> states)
throws NotAuthorizedException, InvalidArgumentException { throws NotAuthorizedException, InvalidArgumentException {
ReportResource resource = toReportResource(report); ReportResource resource = toReportResource(report);
resource.add( resource.add(
linkTo(methodOn(MonitorController.class).getTasksWorkbasketReport(daysInPast, states)) linkTo(methodOn(MonitorController.class).getTasksWorkbasketPlannedDateReport(daysInPast, states))
.withSelfRel().expand()); .withSelfRel().expand());
return resource; return resource;
} }

View File

@ -1,7 +1,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; import { Routes, RouterModule } from '@angular/router';
import { WorkbasketListComponent } from 'app/administration/workbasket/master/list/workbasket-list.component'; import { WorkbasketListComponent } from 'app/administration/workbasket/master/workbasket-list.component';
import { WorkbasketDetailsComponent } from 'app/administration/workbasket/details/workbasket-details.component'; import { WorkbasketDetailsComponent } from 'app/administration/workbasket/details/workbasket-details.component';
import { MasterAndDetailComponent } from 'app/shared/master-and-detail/master-and-detail.component'; import { MasterAndDetailComponent } from 'app/shared/master-and-detail/master-and-detail.component';
import { ClassificationListComponent } from 'app/administration/classification/master/list/classification-list.component'; import { ClassificationListComponent } from 'app/administration/classification/master/list/classification-list.component';

View File

@ -10,8 +10,8 @@ import {InfiniteScrollModule} from 'ngx-infinite-scroll';
/** /**
* Components * Components
*/ */
import {WorkbasketListComponent} from './workbasket/master/list/workbasket-list.component'; import {WorkbasketListComponent} from './workbasket/master/workbasket-list.component';
import {WorkbasketListToolbarComponent} from './workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component' import {WorkbasketListToolbarComponent} from './workbasket/master/workbasket-list-toolbar/workbasket-list-toolbar.component'
import {WorkbasketDetailsComponent} from './workbasket/details/workbasket-details.component'; import {WorkbasketDetailsComponent} from './workbasket/details/workbasket-details.component';
import {WorkbasketInformationComponent} from './workbasket/details/information/workbasket-information.component'; import {WorkbasketInformationComponent} from './workbasket/details/information/workbasket-information.component';
import {DistributionTargetsComponent} from './workbasket/details/distribution-targets/distribution-targets.component'; import {DistributionTargetsComponent} from './workbasket/details/distribution-targets/distribution-targets.component';

View File

@ -4,7 +4,7 @@
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div class="row"> <div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2"> <div class="col-xs-12 col-md-7 col-md-offset-2">
<div style="display: block;"> <div style="display: block;">
<canvas baseChart [datasets]="lineChartData" [labels]="lineChartLabels" [options]="lineChartOptions" <canvas baseChart [datasets]="lineChartData" [labels]="lineChartLabels" [options]="lineChartOptions"
[colors]="lineChartColors" [legend]="lineChartLegend" [chartType]="lineChartType"></canvas> [colors]="lineChartColors" [legend]="lineChartLegend" [chartType]="lineChartType"></canvas>

View File

@ -1,8 +1,9 @@
import { Component, OnInit } from '@angular/core'; import {Component, OnInit} from '@angular/core';
import { RestConnectorService } from 'app/monitor/services/restConnector/rest-connector.service'; import {RestConnectorService} from 'app/monitor/services/restConnector/rest-connector.service';
import { ReportData } from '../models/report-data'; import {ReportData} from '../models/report-data';
import { ChartData } from 'app/monitor/models/chart-data'; import {ChartData} from 'app/monitor/models/chart-data';
import { ChartColorsDefinition } from '../models/chart-colors'; import {ChartColorsDefinition} from '../models/chart-colors';
import {RequestInProgressService} from '../../services/requestInProgress/request-in-progress.service';
@Component({ @Component({
selector: 'taskana-monitor-classification-tasks', selector: 'taskana-monitor-classification-tasks',
@ -22,14 +23,20 @@ export class ClassificationTasksComponent implements OnInit {
}; };
lineChartColors = ChartColorsDefinition.getColors(); lineChartColors = ChartColorsDefinition.getColors();
constructor(private restConnectorService: RestConnectorService) { constructor(
private restConnectorService: RestConnectorService,
private requestInProgressService: RequestInProgressService) {
} }
ngOnInit() { async ngOnInit() {
this.restConnectorService.getClassificationTasksReport().subscribe((data: ReportData) => { this.requestInProgressService.setRequestInProgress(true);
this.reportData = data; this.reportData = await this.restConnectorService.getClassificationTasksReport().toPromise()
this.lineChartData = this.restConnectorService.getChartData(data); this.lineChartData = this.restConnectorService.getChartData(this.reportData);
this.lineChartLabels = this.restConnectorService.getChartHeaders(data); this.lineChartLabels = this.reportData.meta.header;
}) this.requestInProgressService.setRequestInProgress(false);
}
getTitle(): string {
return 'Tasks grouped by classification, querying by due date';
} }
} }

View File

@ -0,0 +1,4 @@
export enum MonitorQueryType {
DueDate,
PlannedDate
}

View File

@ -12,13 +12,20 @@ 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 {ClassificationTasksComponent} from './classification-tasks/classification-tasks.component'; import {ClassificationTasksComponent} from './classification-tasks/classification-tasks.component';
import {TimestampComponent} from './timestamp/timestamp.component'; import {TimestampComponent} from './timestamp/timestamp.component';
import {RestConnectorService} from './services/restConnector/rest-connector.service'; import {RestConnectorService} from './services/restConnector/rest-connector.service';
import {MapToIterable} from '../shared/pipes/mapToIterable/mapToIterable'; import {MapToIterable} from '../shared/pipes/mapToIterable/mapToIterable';
import {MonitorWorkbasketsComponent} from './workbasket/monitor-workbaskets.component';
import {MonitorWorkbasketPlannedDateComponent} from './workbasket/workbasket-planned-date/monitor-workbasket-planned-date.component';
import {MonitorWorkbasketDueDateComponent} from './workbasket/monitor-workbasket-due-date/monitor-workbasket-due-date.component';
import {
MonitorWorkbasketQuerySwitcherComponent
} from './workbasket/monitor-workbasket-query-switcher/monitor-workbasket-query-switcher.component';
const MODULES = [ const MODULES = [
CommonModule, CommonModule,
@ -32,12 +39,15 @@ const MODULES = [
SharedModule SharedModule
]; ];
const DECLARATIONS = [ const DECLARATIONS = [
TasksComponent,
WorkbasketComponent,
ReportComponent, ReportComponent,
MonitorComponent, MonitorComponent,
TimestampComponent,
MonitorWorkbasketsComponent,
MonitorWorkbasketPlannedDateComponent,
MonitorWorkbasketDueDateComponent,
MonitorWorkbasketQuerySwitcherComponent,
TasksComponent,
ClassificationTasksComponent, ClassificationTasksComponent,
TimestampComponent
]; ];
@NgModule({ @NgModule({

View File

@ -1,11 +1,9 @@
import { Injectable } from '@angular/core'; import {Injectable} from '@angular/core';
import { HttpClient } from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import { environment } from 'environments/environment'; import {environment} from 'environments/environment';
import { Observable } from 'rxjs'; import {Observable} from 'rxjs';
import { ReportData } from '../../models/report-data'; import {ReportData} from '../../models/report-data';
import { ChartData } from 'app/monitor/models/chart-data'; import {ChartData} from 'app/monitor/models/chart-data';
import { TaskanaDate } from 'app/shared/util/taskana.date';
import { map } from 'rxjs/internal/operators/map';
const monitorUrl = '/v1/monitor/'; const monitorUrl = '/v1/monitor/';
@ -21,9 +19,14 @@ export class RestConnectorService {
+ 'tasks-status-report?states=READY,CLAIMED,COMPLETED'); + 'tasks-status-report?states=READY,CLAIMED,COMPLETED');
} }
getWorkbasketStatistics(): Observable<ReportData> { getWorkbasketStatisticsQueryingByDueDate(): Observable<ReportData> {
return this.httpClient.get<ReportData>(environment.taskanaRestUrl return this.httpClient.get<ReportData>(environment.taskanaRestUrl
+ monitorUrl + 'tasks-workbasket-planned-date-report?daysInPast=5&states=READY,CLAIMED,COMPLETED'); + monitorUrl + 'tasks-workbasket-report?states=READY,CLAIMED,COMPLETED');
}
getWorkbasketStatisticsQueryingByPlannedDate(): Observable<ReportData> {
return this.httpClient.get<ReportData>(environment.taskanaRestUrl
+ '/v1/monitor/tasks-workbasket-planned-date-report?daysInPast=7&states=READY,CLAIMED,COMPLETED');
} }
getClassificationTasksReport(): Observable<ReportData> { getClassificationTasksReport(): Observable<ReportData> {
@ -37,29 +40,11 @@ export class RestConnectorService {
} }
getChartData(source: ReportData): Array<ChartData> { getChartData(source: ReportData): Array<ChartData> {
const result = new Array<ChartData>(); return source.rows.map(row => {
Object.keys(source.rows).forEach(key => {
const rowData = new ChartData(); const rowData = new ChartData();
rowData.label = row.desc[0];
rowData.label = key; rowData.data = row.cells;
rowData.data = new Array<number>(); return rowData;
source.meta.header.forEach((headerValue: string) => {
rowData.data.push(source.rows[key].cells[headerValue]);
}); });
result.push(rowData)
});
return result;
}
getChartHeaders(source: ReportData): Array<string> {
const result = new Array<string>();
source.meta.header.forEach((header: string) => {
result.push(header);
})
return result;
} }
} }

View File

@ -1,6 +1,7 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {RestConnectorService} from '../services/restConnector/rest-connector.service'; import {RestConnectorService} from '../services/restConnector/rest-connector.service';
import {ReportData} from 'app/monitor/models/report-data'; import {ReportData} from 'app/monitor/models/report-data';
import {RequestInProgressService} from '../../services/requestInProgress/request-in-progress.service';
@Component({ @Component({
selector: 'taskana-monitor-tasks', selector: 'taskana-monitor-tasks',
@ -15,16 +16,22 @@ export class TasksComponent implements OnInit {
pieChartType = 'pie'; pieChartType = 'pie';
reportData: ReportData reportData: ReportData
constructor(private restConnectorService: RestConnectorService) { constructor(
private restConnectorService: RestConnectorService,
private requestInProgressService: RequestInProgressService) {
} }
ngOnInit() { async ngOnInit() {
this.restConnectorService.getTaskStatusReport().subscribe((data: ReportData) => { this.requestInProgressService.setRequestInProgress(true);
this.reportData = data; this.reportData = await this.restConnectorService.getTaskStatusReport().toPromise()
this.pieChartLabels = data.meta.header; this.pieChartLabels = this.reportData.meta.header;
data.sumRow[0].cells.forEach(c => this.pieChartData.push(c)); this.reportData.sumRow[0].cells.forEach(c => {
this.pieChartData.push(c);
}) })
this.requestInProgressService.setRequestInProgress(false);
}
getTitle(): string {
return 'Tasks status grouped by domain';
} }
} }

View File

@ -0,0 +1,9 @@
<div class="row" *ngIf="reportData">
<div class="col-xs-12 col-md-7 col-md-offset-2">
<div style="display: block;">
<canvas baseChart [datasets]="lineChartData" [labels]="lineChartLabels" [options]="lineChartOptions"
[colors]="lineChartColors" [legend]="lineChartLegend" [chartType]="lineChartType" ></canvas>
</div>
</div>
</div>
<taskana-report [reportData]="reportData"></taskana-report>

View File

@ -0,0 +1,25 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {MonitorWorkbasketDueDateComponent} from './monitor-workbasket-due-date.component';
describe('MonitorWorkbasketDueDateComponent', () => {
let component: MonitorWorkbasketDueDateComponent;
let fixture: ComponentFixture<MonitorWorkbasketDueDateComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MonitorWorkbasketDueDateComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MonitorWorkbasketDueDateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,46 @@
import {Component, EventEmitter, OnInit, Output} from '@angular/core';
import {ReportData} from '../../models/report-data';
import {ChartData} from '../../models/chart-data';
import {ChartColorsDefinition} from '../../models/chart-colors';
import {RestConnectorService} from '../../services/restConnector/rest-connector.service';
import {MetaInfoData} from '../../models/meta-info-data';
import {RequestInProgressService} from '../../../services/requestInProgress/request-in-progress.service';
@Component({
selector: 'taskana-monitor-workbasket-due-date',
templateUrl: './monitor-workbasket-due-date.component.html',
styleUrls: ['./monitor-workbasket-due-date.component.scss']
})
export class MonitorWorkbasketDueDateComponent implements OnInit {
@Output()
metaInformation = new EventEmitter<MetaInfoData>()
reportData: ReportData;
lineChartLabels: Array<any>;
lineChartLegend = true;
lineChartType = 'line';
lineChartData: Array<ChartData>;
lineChartOptions: any = {
responsive: true
};
lineChartColors = ChartColorsDefinition.getColors();
constructor(
private restConnectorService: RestConnectorService,
private requestInProgressService: RequestInProgressService) {
}
async ngOnInit() {
this.requestInProgressService.setRequestInProgress(true);
this.reportData = await this.restConnectorService.getWorkbasketStatisticsQueryingByDueDate().toPromise();
this.metaInformation.emit(this.reportData.meta);
this.lineChartLabels = this.reportData.meta.header;
this.lineChartData = this.restConnectorService.getChartData(this.reportData);
this.requestInProgressService.setRequestInProgress(false);
}
}

View File

@ -0,0 +1,15 @@
<div class="btn-group" role="group" aria-label="select chart type">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default"
[ngClass]="selectedChartType === monitorQueryDueDateType ? 'selected' : ''"
(click)="switch(monitorQueryDueDateType)">Due date
</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default"
[ngClass]="selectedChartType === monitorQueryPlannedDateType ? 'selected' : ''"
(click)="switch(monitorQueryPlannedDateType)">Planned date
</button>
</div>
</div>

View File

@ -0,0 +1,6 @@
@import './src/assets/_colors';
.btn.selected {
background-color: $blue;
color: white;
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MonitorWorkbasketQuerySwitcherComponent } from './monitor-workbasket-query-switcher.component';
describe('MonitorWorkbasketQuerySwitcherComponent', () => {
let component: MonitorWorkbasketQuerySwitcherComponent;
let fixture: ComponentFixture<MonitorWorkbasketQuerySwitcherComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ MonitorWorkbasketQuerySwitcherComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MonitorWorkbasketQuerySwitcherComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,30 @@
import {Component, EventEmitter, OnInit, Output} from '@angular/core';
import {MonitorQueryType} from '../../models/monitor-query-type';
@Component({
selector: 'taskana-monitor-workbasket-query-switcher',
templateUrl: './monitor-workbasket-query-switcher.component.html',
styleUrls: ['./monitor-workbasket-query-switcher.component.scss']
})
export class MonitorWorkbasketQuerySwitcherComponent implements OnInit {
@Output()
queryChanged = new EventEmitter<MonitorQueryType>();
selectedChartType: MonitorQueryType;
monitorQueryPlannedDateType = MonitorQueryType.PlannedDate;
monitorQueryDueDateType = MonitorQueryType.DueDate;
constructor() {
}
ngOnInit() {
this.selectedChartType = MonitorQueryType.DueDate;
this.queryChanged.emit(MonitorQueryType.DueDate);
}
switch(queryType: MonitorQueryType) {
this.selectedChartType = queryType;
this.queryChanged.emit(queryType);
}
}

View File

@ -0,0 +1,13 @@
<div class="panel panel-default">
<div class="panel-heading">
<h4 *ngIf="metaInformation">{{getTitle()}} ({{metaInformation?.date | date : 'dd.MM.yyyy HH:mm:ss'}})</h4>
<taskana-monitor-workbasket-query-switcher
(queryChanged)="queryChanged($event)"></taskana-monitor-workbasket-query-switcher>
</div>
<div class="panel-body">
<taskana-monitor-workbasket-due-date [ngClass]="showMonitorQueryDueDate ? '' : 'hidden'"
(metaInformation)="getMetaInformation($event)"></taskana-monitor-workbasket-due-date>
<taskana-monitor-workbasket-planned-date [ngClass]="showMonitorQueryPlannedDate ? '' : 'hidden'"
(metaInformation)="getMetaInformation($event)"></taskana-monitor-workbasket-planned-date>
</div>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MonitorWorkbasketsComponent } from './monitor-workbaskets.component';
describe('MonitorWorkbasketsComponent', () => {
let component: MonitorWorkbasketsComponent;
let fixture: ComponentFixture<MonitorWorkbasketsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ MonitorWorkbasketsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MonitorWorkbasketsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,45 @@
import {Component, OnInit} from '@angular/core';
import {MetaInfoData} from '../models/meta-info-data';
import {MonitorQueryType} from '../models/monitor-query-type';
@Component({
selector: 'taskana-monitor-workbaskets',
templateUrl: './monitor-workbaskets.component.html',
styleUrls: ['./monitor-workbaskets.component.scss']
})
export class MonitorWorkbasketsComponent implements OnInit {
metaInformation: MetaInfoData;
showMonitorQueryPlannedDate: Boolean;
showMonitorQueryDueDate: Boolean;
constructor() {
}
ngOnInit() {
}
getMetaInformation(metaInformation: MetaInfoData) {
this.metaInformation = metaInformation;
}
queryChanged(monitorQueryType: MonitorQueryType) {
this.switchGraphicShowed(monitorQueryType);
}
getTitle(): string {
return this.showMonitorQueryPlannedDate ?
'Tasks grouped by workbasket, querying by planned date' :
'Tasks grouped by workbasket, querying by due date';
}
private switchGraphicShowed(monitorQueryType: MonitorQueryType) {
if (monitorQueryType === MonitorQueryType.PlannedDate) {
this.showMonitorQueryPlannedDate = true;
this.showMonitorQueryDueDate = false
} else if (monitorQueryType === MonitorQueryType.DueDate) {
this.showMonitorQueryPlannedDate = false;
this.showMonitorQueryDueDate = true
}
}
}

View File

@ -0,0 +1,9 @@
<div class="row" *ngIf="reportData">
<div class="col-xs-12 col-md-7 col-md-offset-2">
<div style="display: block;">
<canvas baseChart [datasets]="lineChartData" [labels]="lineChartLabels" [options]="lineChartOptions"
[colors]="lineChartColors" [legend]="lineChartLegend" [chartType]="lineChartType"></canvas>
</div>
</div>
</div>
<taskana-report [reportData]="reportData"></taskana-report>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MonitorWorkbasketPlannedDateComponent } from './monitor-workbasket-planned-date.component';
describe('MonitorWorkbasketPlannedDateComponent', () => {
let component: MonitorWorkbasketPlannedDateComponent;
let fixture: ComponentFixture<MonitorWorkbasketPlannedDateComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ MonitorWorkbasketPlannedDateComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MonitorWorkbasketPlannedDateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,45 @@
import {Component, EventEmitter, OnInit, Output} from '@angular/core';
import {ReportData} from '../../models/report-data';
import {ChartData} from '../../models/chart-data';
import {ChartColorsDefinition} from '../../models/chart-colors';
import {RestConnectorService} from '../../services/restConnector/rest-connector.service';
import {MetaInfoData} from '../../models/meta-info-data';
import {RequestInProgressService} from '../../../services/requestInProgress/request-in-progress.service';
@Component({
selector: 'taskana-monitor-workbasket-planned-date',
templateUrl: './monitor-workbasket-planned-date.component.html',
styleUrls: ['./monitor-workbasket-planned-date.component.scss']
})
export class MonitorWorkbasketPlannedDateComponent implements OnInit {
@Output()
metaInformation = new EventEmitter<MetaInfoData>()
reportData: ReportData;
lineChartLabels: Array<any>;
lineChartLegend = true;
lineChartType = 'line';
lineChartData: Array<ChartData>;
lineChartOptions: any = {
responsive: true,
scales: {xAxes: [{}], yAxes: [{}]},
};
lineChartColors = ChartColorsDefinition.getColors();
constructor(
private restConnectorService: RestConnectorService,
private requestInProgressService: RequestInProgressService) {
}
async ngOnInit() {
this.requestInProgressService.setRequestInProgress(true);
this.reportData = await this.restConnectorService.getWorkbasketStatisticsQueryingByPlannedDate().toPromise();
this.metaInformation.emit(this.reportData.meta);
this.lineChartLabels = this.reportData.meta.header;
this.lineChartData = this.restConnectorService.getChartData(this.reportData);
this.requestInProgressService.setRequestInProgress(false);
}
}

View File

@ -1,16 +0,0 @@
<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">
<div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<div style="display: block;">
<canvas baseChart [datasets]="lineChartData" [labels]="lineChartLabels" [options]="lineChartOptions"
[colors]="lineChartColors" [legend]="lineChartLegend" [chartType]="lineChartType"></canvas>
</div>
</div>
</div>
<taskana-report [reportData]="reportData"></taskana-report>
</div>
</div>

View File

@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { WorkbasketComponent } from './workbasket.component';
describe('WorkbasketComponent', () => {
let component: WorkbasketComponent;
let fixture: ComponentFixture<WorkbasketComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ WorkbasketComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(WorkbasketComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,37 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { RestConnectorService } from 'app/monitor/services/restConnector/rest-connector.service';
import { ChartColorsDefinition } from '../models/chart-colors';
import { ReportData } from 'app/monitor/models/report-data';
import {ChartData} from 'app/monitor/models/chart-data';
@Component({
selector: 'taskana-monitor-workbaskets',
templateUrl: './workbasket.component.html',
styleUrls: ['./workbasket.component.scss'],
providers: [RestConnectorService]
})
export class WorkbasketComponent implements OnInit {
reportData: ReportData;
lineChartLabels: Array<any>;
lineChartLegend = true;
lineChartType = 'line';
lineChartData: Array<ChartData>;
lineChartOptions: any = {
responsive: true
};
lineChartColors = ChartColorsDefinition.getColors();
constructor(private restConnectorService: RestConnectorService) { }
ngOnInit() {
this.restConnectorService.getWorkbasketStatistics().subscribe((data: ReportData) => {
this.reportData = data;
this.lineChartLabels = this.restConnectorService.getChartHeaders(data);
this.lineChartData = this.restConnectorService.getChartData(data);
})
}
}

View File

@ -44,7 +44,7 @@ export class TaskComponent implements OnInit, OnDestroy {
async getTask(id: string) { async getTask(id: string) {
this.requestInProgress = true; this.requestInProgress = true;
this.task = await this.taskService.getTask(id).toPromise() this.task = await this.taskService.getTask(id).toPromise();
const classification = await this.classificationService.getClassification const classification = await this.classificationService.getClassification
(this.task.classificationSummaryResource.classificationId).toPromise(); (this.task.classificationSummaryResource.classificationId).toPromise();
this.address = this.extractUrl(classification.applicationEntryPoint) || `${this.address}/?q=${this.task.name}`; this.address = this.extractUrl(classification.applicationEntryPoint) || `${this.address}/?q=${this.task.name}`;

View File

@ -1,5 +1,4 @@
.placeholder img { .placeholder img {
display: inline-block; display: inline-block;
border-radius: 50%; border-radius: 50%;
@ -62,20 +61,23 @@
*/ */
.white { .white {
color: white; color: white;
& svg { & svg {
fill: white; fill: white;
} }
} }
.blue{ .blue {
color: $blue; color: $blue;
& svg { & svg {
fill: $blue; fill: $blue;
} }
} }
.blue-green{ .blue-green {
color: $blue-green; color: $blue-green;
& svg { & svg {
fill: $blue-green; fill: $blue-green;
} }
@ -83,20 +85,23 @@
.green { .green {
color: $green; color: $green;
& svg { & svg {
fill: $green; fill: $green;
} }
} }
.dark-green{ .dark-green {
color: $dark-green; color: $dark-green;
& svg { & svg {
fill: $dark-green; fill: $dark-green;
} }
} }
.grey { .grey {
color:$grey; color: $grey;
& svg { & svg {
fill: $grey; fill: $grey;
} }
@ -104,6 +109,7 @@
.brown { .brown {
color: $brown; color: $brown;
& svg { & svg {
fill: $brown; fill: $brown;
} }
@ -111,6 +117,7 @@
.red { .red {
color: $invalid; color: $invalid;
& svg { & svg {
fill: $invalid; fill: $invalid;
} }
@ -118,6 +125,7 @@
.green-blue { .green-blue {
color: $blue-green; color: $blue-green;
& svg { & svg {
fill: $blue-green; fill: $blue-green;
} }
@ -125,6 +133,7 @@
.aquamarine { .aquamarine {
color: $aquamarine; color: $aquamarine;
& svg { & svg {
fill: $aquamarine; fill: $aquamarine;
} }
@ -143,6 +152,7 @@ svg-icon {
.panel-default > .panel-heading .badge.warning { .panel-default > .panel-heading .badge.warning {
background-color: $brown; background-color: $brown;
} }
.badge.priority { .badge.priority {
background-color: #e1e1e1; background-color: #e1e1e1;
} }
@ -210,7 +220,7 @@ svg-icon {
vertical-align: middle; vertical-align: middle;
} }
body{ body {
overflow-y: hidden; overflow-y: hidden;
} }
@ -227,7 +237,7 @@ body{
text-align: left; text-align: left;
} }
.dropdown-menu-users >li { .dropdown-menu-users > li {
margin-bottom: 5px; margin-bottom: 5px;
} }
@ -250,26 +260,30 @@ body{
-o-transition: opacity 300ms ease, visibility 300ms ease; -o-transition: opacity 300ms ease, visibility 300ms ease;
transition: opacity 300ms ease, visibility 300ms ease; transition: opacity 300ms ease, visibility 300ms ease;
} }
/* buttons and icons vertical align */ /* buttons and icons vertical align */
.vertical-align{ .vertical-align {
vertical-align: top; vertical-align: top;
} }
.btn > svg-icon > svg{ .btn > svg-icon > svg {
vertical-align: middle; vertical-align: middle;
} }
a > svg-icon > svg{
a > svg-icon > svg {
vertical-align: text-top; vertical-align: text-top;
} }
/*end buttons and icons vertical align */ /*end buttons and icons vertical align */
span.flip { span.flip {
transform: rotateX(180deg); transform: rotateX(180deg);
} }
.panel-heading{ .panel-heading {
padding: 8px 15px 4px 15px; padding: 8px 15px 4px 15px;
} }
.panel { .panel {
border-radius: 0px; border-radius: 0px;
} }
@ -289,23 +303,24 @@ span.flip {
padding: 5px 15px; padding: 5px 15px;
} }
.dual-list > taskana-filter >.list-group-search { .dual-list > taskana-filter > .list-group-search {
margin-top: 4px; margin-top: 4px;
} }
.container { .container {
@media (min-width: 1200px){ @media (min-width: 1200px) {
width: 100%; width: 100%;
} }
} }
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-timestamp { taskana-monitor-workbaskets, taskana-monitor-classification-tasks, taskana-monitor-timestamp {
& .panel{ & .panel {
border: none; border: none;
box-shadow: none; box-shadow: none;
margin-bottom: 0px; margin-bottom: 0px;
&> .panel-body {
& > .panel-body {
height: calc(100vh - 155px); height: calc(100vh - 155px);
max-height: calc(100vh - 155px); max-height: calc(100vh - 155px);
overflow-y: auto; overflow-y: auto;
@ -314,11 +329,12 @@ taskana-monitor-workbaskets, taskana-monitor-classification-tasks, taskana-monit
} }
taskana-task-details, taskana-classification-details, taskana-access-items-management, taskana-task, taskana-task-query { taskana-task-details, taskana-classification-details, taskana-access-items-management, taskana-task, taskana-task-query {
& .panel{ & .panel {
border: none; border: none;
box-shadow: none; box-shadow: none;
margin-bottom: 0px; margin-bottom: 0px;
&> .panel-body {
& > .panel-body {
height: calc(100vh - 100px); height: calc(100vh - 100px);
max-height: calc(100vh - 100px); max-height: calc(100vh - 100px);
overflow-y: auto; overflow-y: auto;
@ -327,17 +343,18 @@ taskana-task-details, taskana-classification-details, taskana-access-items-manag
} }
taskana-task-query { taskana-task-query {
& .panel{ & .panel {
&> .panel-body { & > .panel-body {
height: calc(100vh - 105px); height: calc(100vh - 105px);
max-height: calc(100vh - 105px); max-height: calc(100vh - 105px);
} }
} }
} }
taskana-monitor-tasks, taskana-monitor-workbaskets, taskana-monitor-classification-tasks, taskana-access-items-management { taskana-monitor-tasks, taskana-monitor-workbaskets, taskana-monitor-classification-tasks, taskana-access-items-management {
& .panel { & .panel {
&> .panel-heading { & > .panel-heading {
border-left: 1px solid #ddd; border-left: 1px solid #ddd;
border-right: 1px solid #ddd; border-right: 1px solid #ddd;
} }
@ -350,9 +367,10 @@ li.list-group-item.active:hover {
border-color: $green; border-color: $green;
} }
.list-group-item.active{ .list-group-item.active {
background-color: $green; background-color: $green;
} }
li.list-group-item:hover { li.list-group-item:hover {
color: #555; color: #555;
text-decoration: none; text-decoration: none;
@ -363,11 +381,12 @@ li.list-group-item:hover {
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
height: calc(100vh - 55px); height: calc(100vh - 55px);
} }
.horizontal-bottom-divider { .horizontal-bottom-divider {
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
} }
.center-block>span.empty-icon, .empty-icon { .center-block > span.empty-icon, .empty-icon {
display: block; display: block;
width: 150px; width: 150px;
height: 150px; height: 150px;
@ -377,13 +396,15 @@ li.list-group-item:hover {
font-size: 100px; font-size: 100px;
} }
.btn.no-style{ .btn.no-style {
border: none; border: none;
background-color: transparent; background-color: transparent;
} }
.align-header{
.align-header {
margin-top: 3px; margin-top: 3px;
} }
.container-no-items { .container-no-items {
top: 20vh; top: 20vh;
height: 40vh; height: 40vh;
@ -400,6 +421,7 @@ li.list-group-item:hover {
.btn.rounded { .btn.rounded {
border-radius: 50%; border-radius: 50%;
background-color: transparent; background-color: transparent;
&:focus, &:active:focus { &:focus, &:active:focus {
outline: none; outline: none;
} }
@ -408,11 +430,12 @@ li.list-group-item:hover {
.modal-backdrop.show { .modal-backdrop.show {
background-color: $transparent-grey; background-color: $transparent-grey;
} }
.padding-right.pull-right { .padding-right.pull-right {
padding-right: 15px; padding-right: 15px;
} }
.backdrop{ .backdrop {
position: fixed; position: fixed;
left: 0; left: 0;
right: 0; right: 0;