:bugfixes for workplace
This commit is contained in:
Lars Leo Grätz 2018-05-28 17:55:30 +02:00 committed by Martin Rojas Miguel Angel
parent ace632d814
commit 2aeb815d8e
15 changed files with 574 additions and 484 deletions

View File

@ -1,369 +1,369 @@
package pro.taskana.rest; package pro.taskana.rest;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.PagedResources; import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.PagedResources.PageMetadata; import org.springframework.hateoas.PagedResources.PageMetadata;
import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import pro.taskana.BaseQuery.SortDirection; import pro.taskana.BaseQuery.SortDirection;
import pro.taskana.KeyDomain; import pro.taskana.KeyDomain;
import pro.taskana.Task; import pro.taskana.Task;
import pro.taskana.TaskQuery; import pro.taskana.TaskQuery;
import pro.taskana.TaskService; import pro.taskana.TaskService;
import pro.taskana.TaskState; import pro.taskana.TaskState;
import pro.taskana.TaskSummary; import pro.taskana.TaskSummary;
import pro.taskana.exceptions.AttachmentPersistenceException; import pro.taskana.exceptions.AttachmentPersistenceException;
import pro.taskana.exceptions.ClassificationNotFoundException; import pro.taskana.exceptions.ClassificationNotFoundException;
import pro.taskana.exceptions.ConcurrencyException; import pro.taskana.exceptions.ConcurrencyException;
import pro.taskana.exceptions.InvalidArgumentException; import pro.taskana.exceptions.InvalidArgumentException;
import pro.taskana.exceptions.InvalidOwnerException; import pro.taskana.exceptions.InvalidOwnerException;
import pro.taskana.exceptions.InvalidStateException; import pro.taskana.exceptions.InvalidStateException;
import pro.taskana.exceptions.InvalidWorkbasketException; import pro.taskana.exceptions.InvalidWorkbasketException;
import pro.taskana.exceptions.NotAuthorizedException; import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.exceptions.TaskAlreadyExistException; import pro.taskana.exceptions.TaskAlreadyExistException;
import pro.taskana.exceptions.TaskNotFoundException; import pro.taskana.exceptions.TaskNotFoundException;
import pro.taskana.exceptions.WorkbasketNotFoundException; import pro.taskana.exceptions.WorkbasketNotFoundException;
import pro.taskana.rest.resource.TaskResource; import pro.taskana.rest.resource.TaskResource;
import pro.taskana.rest.resource.TaskSummaryResource; import pro.taskana.rest.resource.TaskSummaryResource;
import pro.taskana.rest.resource.assembler.TaskResourceAssembler; import pro.taskana.rest.resource.assembler.TaskResourceAssembler;
import pro.taskana.rest.resource.assembler.TaskSummaryResourcesAssembler; import pro.taskana.rest.resource.assembler.TaskSummaryResourcesAssembler;
/** /**
* Controller for all {@link Task} related endpoints. * Controller for all {@link Task} related endpoints.
*/ */
@RestController @RestController
@EnableHypermediaSupport(type = HypermediaType.HAL) @EnableHypermediaSupport(type = HypermediaType.HAL)
@RequestMapping(path = "/v1/tasks", produces = "application/hal+json") @RequestMapping(path = "/v1/tasks", produces = "application/hal+json")
public class TaskController extends AbstractPagingController { public class TaskController extends AbstractPagingController {
private static final Logger LOGGER = LoggerFactory.getLogger(TaskController.class); private static final Logger LOGGER = LoggerFactory.getLogger(TaskController.class);
private static final String STATE = "state"; private static final String STATE = "state";
private static final String STATE_VALUE_CLAIMED = "CLAIMED"; private static final String STATE_VALUE_CLAIMED = "CLAIMED";
private static final String STATE_VALUE_COMPLETED = "COMPLETED"; private static final String STATE_VALUE_COMPLETED = "COMPLETED";
private static final String STATE_VALUE_READY = "READY"; private static final String STATE_VALUE_READY = "READY";
private static final String PRIORITY = "priority"; private static final String PRIORITY = "priority";
private static final String NAME = "name"; private static final String NAME = "name";
private static final String OWNER = "owner"; private static final String OWNER = "owner";
private static final String DOMAIN = "domain"; private static final String DOMAIN = "domain";
private static final String WORKBASKET_ID = "workbasket-id"; private static final String WORKBASKET_ID = "workbasket-id";
private static final String WORKBASKET_KEY = "workbasket-key"; private static final String WORKBASKET_KEY = "workbasket-key";
private static final String CLASSIFICATION_KEY = "classification.key"; private static final String CLASSIFICATION_KEY = "classification.key";
private static final String POR_VALUE = "por.value"; private static final String POR_VALUE = "por.value";
private static final String POR_TYPE = "por.type"; private static final String POR_TYPE = "por.type";
private static final String POR_SYSTEM_INSTANCE = "por.instance"; private static final String POR_SYSTEM_INSTANCE = "por.instance";
private static final String POR_SYSTEM = "por.system"; private static final String POR_SYSTEM = "por.system";
private static final String POR_COMPANY = "por.company"; private static final String POR_COMPANY = "por.company";
private static final String DUE = "due"; private static final String DUE = "due";
private static final String PLANNED = "planned"; private static final String PLANNED = "planned";
private static final String SORT_BY = "sortBy"; private static final String SORT_BY = "sortBy";
private static final String SORT_DIRECTION = "order"; private static final String SORT_DIRECTION = "order";
private static final String PAGING_PAGE = "page"; private static final String PAGING_PAGE = "page";
private static final String PAGING_PAGE_SIZE = "page-size"; private static final String PAGING_PAGE_SIZE = "page-size";
@Autowired @Autowired
private TaskService taskService; private TaskService taskService;
@Autowired @Autowired
private TaskResourceAssembler taskResourceAssembler; private TaskResourceAssembler taskResourceAssembler;
@GetMapping @GetMapping
@Transactional(readOnly = true, rollbackFor = Exception.class) @Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<PagedResources<TaskSummaryResource>> getTasks( public ResponseEntity<PagedResources<TaskSummaryResource>> getTasks(
@RequestParam MultiValueMap<String, String> params) throws InvalidArgumentException, NotAuthorizedException { @RequestParam MultiValueMap<String, String> params) throws InvalidArgumentException, NotAuthorizedException {
TaskQuery query = taskService.createTaskQuery(); TaskQuery query = taskService.createTaskQuery();
query = applyFilterParams(query, params); query = applyFilterParams(query, params);
query = applySortingParams(query, params); query = applySortingParams(query, params);
PageMetadata pageMetadata = null; PageMetadata pageMetadata = null;
List<TaskSummary> taskSummaries = null; List<TaskSummary> taskSummaries = null;
String page = params.getFirst(PAGING_PAGE); String page = params.getFirst(PAGING_PAGE);
String pageSize = params.getFirst(PAGING_PAGE_SIZE); String pageSize = params.getFirst(PAGING_PAGE_SIZE);
params.remove(PAGING_PAGE); params.remove(PAGING_PAGE);
params.remove(PAGING_PAGE_SIZE); params.remove(PAGING_PAGE_SIZE);
validateNoInvalidParameterIsLeft(params); validateNoInvalidParameterIsLeft(params);
if (page != null && pageSize != null) { if (page != null && pageSize != null) {
// paging // paging
long totalElements = query.count(); long totalElements = query.count();
pageMetadata = initPageMetadata(pageSize, page, pageMetadata = initPageMetadata(pageSize, page,
totalElements); totalElements);
taskSummaries = query.listPage((int) pageMetadata.getNumber(), taskSummaries = query.listPage((int) pageMetadata.getNumber(),
(int) pageMetadata.getSize()); (int) pageMetadata.getSize());
} else if (page == null && pageSize == null) { } else if (page == null && pageSize == null) {
// not paging // not paging
taskSummaries = query.list(); taskSummaries = query.list();
} else { } else {
throw new InvalidArgumentException("Paging information is incomplete."); throw new InvalidArgumentException("Paging information is incomplete.");
} }
TaskSummaryResourcesAssembler taskSummaryResourcesAssembler = new TaskSummaryResourcesAssembler(); TaskSummaryResourcesAssembler taskSummaryResourcesAssembler = new TaskSummaryResourcesAssembler();
PagedResources<TaskSummaryResource> pagedResources = taskSummaryResourcesAssembler.toResources(taskSummaries, PagedResources<TaskSummaryResource> pagedResources = taskSummaryResourcesAssembler.toResources(taskSummaries,
pageMetadata); pageMetadata);
return new ResponseEntity<>(pagedResources, HttpStatus.OK); return new ResponseEntity<>(pagedResources, HttpStatus.OK);
} }
@GetMapping(path = "/{taskId}") @GetMapping(path = "/{taskId}")
@Transactional(readOnly = true, rollbackFor = Exception.class) @Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<TaskResource> getTask(@PathVariable String taskId) public ResponseEntity<TaskResource> getTask(@PathVariable String taskId)
throws TaskNotFoundException, NotAuthorizedException { throws TaskNotFoundException, NotAuthorizedException {
Task task = taskService.getTask(taskId); Task task = taskService.getTask(taskId);
ResponseEntity<TaskResource> result = new ResponseEntity<>(taskResourceAssembler.toResource(task), ResponseEntity<TaskResource> result = new ResponseEntity<>(taskResourceAssembler.toResource(task),
HttpStatus.OK); HttpStatus.OK);
return result; return result;
} }
@PostMapping(path = "/{taskId}/claim") @PostMapping(path = "/{taskId}/claim")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ResponseEntity<TaskResource> claimTask(@PathVariable String taskId, @RequestBody String userName) public ResponseEntity<TaskResource> claimTask(@PathVariable String taskId, @RequestBody String userName)
throws TaskNotFoundException, InvalidStateException, InvalidOwnerException, NotAuthorizedException { throws TaskNotFoundException, InvalidStateException, InvalidOwnerException, NotAuthorizedException {
// TODO verify user // TODO verify user
taskService.claim(taskId); taskService.claim(taskId);
Task updatedTask = taskService.getTask(taskId); Task updatedTask = taskService.getTask(taskId);
ResponseEntity<TaskResource> result = new ResponseEntity<>(taskResourceAssembler.toResource(updatedTask), ResponseEntity<TaskResource> result = new ResponseEntity<>(taskResourceAssembler.toResource(updatedTask),
HttpStatus.OK); HttpStatus.OK);
return result; return result;
} }
@RequestMapping(method = RequestMethod.POST, value = "/{taskId}/complete") @RequestMapping(method = RequestMethod.POST, value = "/{taskId}/complete")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ResponseEntity<TaskResource> completeTask(@PathVariable String taskId) public ResponseEntity<TaskResource> completeTask(@PathVariable String taskId)
throws TaskNotFoundException, InvalidOwnerException, InvalidStateException, NotAuthorizedException { throws TaskNotFoundException, InvalidOwnerException, InvalidStateException, NotAuthorizedException {
taskService.forceCompleteTask(taskId); taskService.forceCompleteTask(taskId);
Task updatedTask = taskService.getTask(taskId); Task updatedTask = taskService.getTask(taskId);
ResponseEntity<TaskResource> result = new ResponseEntity<>(taskResourceAssembler.toResource(updatedTask), ResponseEntity<TaskResource> result = new ResponseEntity<>(taskResourceAssembler.toResource(updatedTask),
HttpStatus.OK); HttpStatus.OK);
return result; return result;
} }
@RequestMapping(method = RequestMethod.DELETE, value = "/{taskId}") @RequestMapping(method = RequestMethod.DELETE, value = "/{taskId}")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ResponseEntity<TaskResource> deleteTask(@PathVariable String taskId) public ResponseEntity<TaskResource> deleteTask(@PathVariable String taskId)
throws TaskNotFoundException, InvalidStateException, NotAuthorizedException { throws TaskNotFoundException, InvalidStateException, NotAuthorizedException {
taskService.forceDeleteTask(taskId); taskService.forceDeleteTask(taskId);
ResponseEntity<TaskResource> result = new ResponseEntity<>(HttpStatus.OK); ResponseEntity<TaskResource> result = new ResponseEntity<>(HttpStatus.OK);
return result; return result;
} }
@RequestMapping(method = RequestMethod.POST) @RequestMapping(method = RequestMethod.POST)
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ResponseEntity<TaskResource> createTask(@RequestBody TaskResource taskResource) public ResponseEntity<TaskResource> createTask(@RequestBody TaskResource taskResource)
throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException, throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException,
TaskAlreadyExistException, InvalidWorkbasketException, InvalidArgumentException { TaskAlreadyExistException, InvalidWorkbasketException, InvalidArgumentException {
Task createdTask = taskService.createTask(taskResourceAssembler.toModel(taskResource)); Task createdTask = taskService.createTask(taskResourceAssembler.toModel(taskResource));
ResponseEntity<TaskResource> result = new ResponseEntity<>(taskResourceAssembler.toResource(createdTask), ResponseEntity<TaskResource> result = new ResponseEntity<>(taskResourceAssembler.toResource(createdTask),
HttpStatus.CREATED); HttpStatus.CREATED);
return result; return result;
} }
@RequestMapping(path = "/{taskId}/transfer/{workbasketKey}") @RequestMapping(path = "/{taskId}/transfer/{workbasketId}")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ResponseEntity<TaskResource> transferTask(@PathVariable String taskId, @PathVariable String workbasketKey) public ResponseEntity<TaskResource> transferTask(@PathVariable String taskId, @PathVariable String workbasketId)
throws TaskNotFoundException, WorkbasketNotFoundException, NotAuthorizedException, InvalidWorkbasketException, throws TaskNotFoundException, WorkbasketNotFoundException, NotAuthorizedException, InvalidWorkbasketException,
InvalidStateException { InvalidStateException {
Task updatedTask = taskService.transfer(taskId, workbasketKey); Task updatedTask = taskService.transfer(taskId, workbasketId);
ResponseEntity<TaskResource> result = new ResponseEntity<>(taskResourceAssembler.toResource(updatedTask), ResponseEntity<TaskResource> result = new ResponseEntity<>(taskResourceAssembler.toResource(updatedTask),
HttpStatus.OK); HttpStatus.OK);
return result; return result;
} }
@PutMapping(path = "/{taskId}") @PutMapping(path = "/{taskId}")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ResponseEntity<TaskResource> updateTask( public ResponseEntity<TaskResource> updateTask(
@PathVariable(value = "taskId") String taskId, @PathVariable(value = "taskId") String taskId,
@RequestBody TaskResource taskResource) throws TaskNotFoundException, WorkbasketNotFoundException, @RequestBody TaskResource taskResource) throws TaskNotFoundException, WorkbasketNotFoundException,
ClassificationNotFoundException, InvalidArgumentException, ConcurrencyException, InvalidWorkbasketException, ClassificationNotFoundException, InvalidArgumentException, ConcurrencyException, InvalidWorkbasketException,
NotAuthorizedException, AttachmentPersistenceException { NotAuthorizedException, AttachmentPersistenceException {
ResponseEntity<TaskResource> result; ResponseEntity<TaskResource> result;
if (taskId.equals(taskResource.getTaskId())) { if (taskId.equals(taskResource.getTaskId())) {
Task task = taskResourceAssembler.toModel(taskResource); Task task = taskResourceAssembler.toModel(taskResource);
task = taskService.updateTask(task); task = taskService.updateTask(task);
result = ResponseEntity.ok(taskResourceAssembler.toResource(task)); result = ResponseEntity.ok(taskResourceAssembler.toResource(task));
} else { } else {
throw new InvalidArgumentException( throw new InvalidArgumentException(
"TaskId ('" + taskId "TaskId ('" + taskId
+ "') is not identical with the taskId of to object in the payload which should be updated. ID=('" + "') is not identical with the taskId of to object in the payload which should be updated. ID=('"
+ taskResource.getTaskId() + "')"); + taskResource.getTaskId() + "')");
} }
return result; return result;
} }
private TaskQuery applyFilterParams(TaskQuery taskQuery, MultiValueMap<String, String> params) private TaskQuery applyFilterParams(TaskQuery taskQuery, MultiValueMap<String, String> params)
throws NotAuthorizedException, InvalidArgumentException { throws NotAuthorizedException, InvalidArgumentException {
// apply filters // apply filters
if (params.containsKey(NAME)) { if (params.containsKey(NAME)) {
String[] names = extractCommaSeparatedFields(params.get(NAME)); String[] names = extractCommaSeparatedFields(params.get(NAME));
taskQuery.nameIn(names); taskQuery.nameIn(names);
params.remove(NAME); params.remove(NAME);
} }
if (params.containsKey(PRIORITY)) { if (params.containsKey(PRIORITY)) {
String[] prioritesInString = extractCommaSeparatedFields(params.get(PRIORITY)); String[] prioritesInString = extractCommaSeparatedFields(params.get(PRIORITY));
int[] priorites = extractPriorities(prioritesInString); int[] priorites = extractPriorities(prioritesInString);
taskQuery.priorityIn(priorites); taskQuery.priorityIn(priorites);
params.remove(PRIORITY); params.remove(PRIORITY);
} }
if (params.containsKey(STATE)) { if (params.containsKey(STATE)) {
TaskState[] states = extractStates(params); TaskState[] states = extractStates(params);
taskQuery.stateIn(states); taskQuery.stateIn(states);
params.remove(STATE); params.remove(STATE);
} }
if (params.containsKey(CLASSIFICATION_KEY)) { if (params.containsKey(CLASSIFICATION_KEY)) {
String[] classificationKeys = extractCommaSeparatedFields(params.get(CLASSIFICATION_KEY)); String[] classificationKeys = extractCommaSeparatedFields(params.get(CLASSIFICATION_KEY));
taskQuery.classificationKeyIn(classificationKeys); taskQuery.classificationKeyIn(classificationKeys);
params.remove(CLASSIFICATION_KEY); params.remove(CLASSIFICATION_KEY);
} }
if (params.containsKey(WORKBASKET_ID)) { if (params.containsKey(WORKBASKET_ID)) {
String[] workbaskets = extractCommaSeparatedFields(params.get(WORKBASKET_ID)); String[] workbaskets = extractCommaSeparatedFields(params.get(WORKBASKET_ID));
taskQuery.workbasketIdIn(workbaskets); taskQuery.workbasketIdIn(workbaskets);
params.remove(WORKBASKET_ID); params.remove(WORKBASKET_ID);
} }
if (params.containsKey(WORKBASKET_KEY)) { if (params.containsKey(WORKBASKET_KEY)) {
String[] domains = null; String[] domains = null;
if (params.get(DOMAIN) != null) { if (params.get(DOMAIN) != null) {
domains = extractCommaSeparatedFields(params.get(DOMAIN)); domains = extractCommaSeparatedFields(params.get(DOMAIN));
} }
if (domains == null || domains.length != 1) { if (domains == null || domains.length != 1) {
throw new InvalidArgumentException("workbasket-key requires excactly one domain as second parameter."); throw new InvalidArgumentException("workbasket-key requires excactly one domain as second parameter.");
} }
String[] workbasketKeys = extractCommaSeparatedFields(params.get(WORKBASKET_KEY)); String[] workbasketKeys = extractCommaSeparatedFields(params.get(WORKBASKET_KEY));
KeyDomain[] keyDomains = new KeyDomain[workbasketKeys.length]; KeyDomain[] keyDomains = new KeyDomain[workbasketKeys.length];
for (int i = 0; i < workbasketKeys.length; i++) { for (int i = 0; i < workbasketKeys.length; i++) {
keyDomains[i] = new KeyDomain(workbasketKeys[i], domains[0]); keyDomains[i] = new KeyDomain(workbasketKeys[i], domains[0]);
} }
taskQuery.workbasketKeyDomainIn(keyDomains); taskQuery.workbasketKeyDomainIn(keyDomains);
params.remove(WORKBASKET_KEY); params.remove(WORKBASKET_KEY);
params.remove(DOMAIN); params.remove(DOMAIN);
} }
if (params.containsKey(OWNER)) { if (params.containsKey(OWNER)) {
String[] owners = extractCommaSeparatedFields(params.get(OWNER)); String[] owners = extractCommaSeparatedFields(params.get(OWNER));
taskQuery.ownerIn(owners); taskQuery.ownerIn(owners);
params.remove(OWNER); params.remove(OWNER);
} }
if (params.containsKey(POR_COMPANY)) { if (params.containsKey(POR_COMPANY)) {
String[] companies = extractCommaSeparatedFields(params.get(POR_COMPANY)); String[] companies = extractCommaSeparatedFields(params.get(POR_COMPANY));
taskQuery.primaryObjectReferenceCompanyIn(companies); taskQuery.primaryObjectReferenceCompanyIn(companies);
params.remove(POR_COMPANY); params.remove(POR_COMPANY);
} }
if (params.containsKey(POR_SYSTEM)) { if (params.containsKey(POR_SYSTEM)) {
String[] systems = extractCommaSeparatedFields(params.get(POR_SYSTEM)); String[] systems = extractCommaSeparatedFields(params.get(POR_SYSTEM));
taskQuery.primaryObjectReferenceSystemIn(systems); taskQuery.primaryObjectReferenceSystemIn(systems);
params.remove(POR_SYSTEM); params.remove(POR_SYSTEM);
} }
if (params.containsKey(POR_SYSTEM_INSTANCE)) { if (params.containsKey(POR_SYSTEM_INSTANCE)) {
String[] systemInstances = extractCommaSeparatedFields(params.get(POR_SYSTEM_INSTANCE)); String[] systemInstances = extractCommaSeparatedFields(params.get(POR_SYSTEM_INSTANCE));
taskQuery.primaryObjectReferenceSystemInstanceIn(systemInstances); taskQuery.primaryObjectReferenceSystemInstanceIn(systemInstances);
params.remove(POR_SYSTEM_INSTANCE); params.remove(POR_SYSTEM_INSTANCE);
} }
if (params.containsKey(POR_TYPE)) { if (params.containsKey(POR_TYPE)) {
String[] types = extractCommaSeparatedFields(params.get(POR_TYPE)); String[] types = extractCommaSeparatedFields(params.get(POR_TYPE));
taskQuery.primaryObjectReferenceTypeIn(types); taskQuery.primaryObjectReferenceTypeIn(types);
params.remove(POR_TYPE); params.remove(POR_TYPE);
} }
if (params.containsKey(POR_VALUE)) { if (params.containsKey(POR_VALUE)) {
String[] values = extractCommaSeparatedFields(params.get(POR_VALUE)); String[] values = extractCommaSeparatedFields(params.get(POR_VALUE));
taskQuery.primaryObjectReferenceValueIn(values); taskQuery.primaryObjectReferenceValueIn(values);
params.remove(POR_VALUE); params.remove(POR_VALUE);
} }
return taskQuery; return taskQuery;
} }
private TaskQuery applySortingParams(TaskQuery taskQuery, MultiValueMap<String, String> params) private TaskQuery applySortingParams(TaskQuery taskQuery, MultiValueMap<String, String> params)
throws NotAuthorizedException, InvalidArgumentException { throws NotAuthorizedException, InvalidArgumentException {
// sorting // sorting
String sortBy = params.getFirst(SORT_BY); String sortBy = params.getFirst(SORT_BY);
if (sortBy != null) { if (sortBy != null) {
SortDirection sortDirection; SortDirection sortDirection;
if (params.getFirst(SORT_DIRECTION) != null && "desc".equals(params.getFirst(SORT_DIRECTION))) { if (params.getFirst(SORT_DIRECTION) != null && "desc".equals(params.getFirst(SORT_DIRECTION))) {
sortDirection = SortDirection.DESCENDING; sortDirection = SortDirection.DESCENDING;
} else { } else {
sortDirection = SortDirection.ASCENDING; sortDirection = SortDirection.ASCENDING;
} }
switch (sortBy) { switch (sortBy) {
case (CLASSIFICATION_KEY): case (CLASSIFICATION_KEY):
taskQuery = taskQuery.orderByClassificationKey(sortDirection); taskQuery = taskQuery.orderByClassificationKey(sortDirection);
break; break;
case (POR_TYPE): case (POR_TYPE):
taskQuery = taskQuery.orderByPrimaryObjectReferenceType(sortDirection); taskQuery = taskQuery.orderByPrimaryObjectReferenceType(sortDirection);
break; break;
case (POR_VALUE): case (POR_VALUE):
taskQuery = taskQuery.orderByPrimaryObjectReferenceValue(sortDirection); taskQuery = taskQuery.orderByPrimaryObjectReferenceValue(sortDirection);
break; break;
case (STATE): case (STATE):
taskQuery = taskQuery.orderByState(sortDirection); taskQuery = taskQuery.orderByState(sortDirection);
break; break;
case (NAME): case (NAME):
taskQuery = taskQuery.orderByName(sortDirection); taskQuery = taskQuery.orderByName(sortDirection);
break; break;
case (DUE): case (DUE):
taskQuery = taskQuery.orderByDue(sortDirection); taskQuery = taskQuery.orderByDue(sortDirection);
break; break;
case (PLANNED): case (PLANNED):
taskQuery = taskQuery.orderByPlanned(sortDirection); taskQuery = taskQuery.orderByPlanned(sortDirection);
break; break;
case (PRIORITY): case (PRIORITY):
taskQuery = taskQuery.orderByPriority(sortDirection); taskQuery = taskQuery.orderByPriority(sortDirection);
break; break;
default: default:
throw new InvalidArgumentException("Unknown filter attribute: " + sortBy); throw new InvalidArgumentException("Unknown filter attribute: " + sortBy);
} }
} }
params.remove(SORT_BY); params.remove(SORT_BY);
params.remove(SORT_DIRECTION); params.remove(SORT_DIRECTION);
return taskQuery; return taskQuery;
} }
private int[] extractPriorities(String[] prioritesInString) { private int[] extractPriorities(String[] prioritesInString) {
int[] priorites = new int[prioritesInString.length]; int[] priorites = new int[prioritesInString.length];
for (int i = 0; i < prioritesInString.length; i++) { for (int i = 0; i < prioritesInString.length; i++) {
priorites[i] = Integer.getInteger(prioritesInString[i]); priorites[i] = Integer.getInteger(prioritesInString[i]);
} }
return priorites; return priorites;
} }
private TaskState[] extractStates(MultiValueMap<String, String> params) throws InvalidArgumentException { private TaskState[] extractStates(MultiValueMap<String, String> params) throws InvalidArgumentException {
List<TaskState> states = new ArrayList<>(); List<TaskState> states = new ArrayList<>();
for (String item : params.get(STATE)) { for (String item : params.get(STATE)) {
for (String state : item.split(",")) { for (String state : item.split(",")) {
switch (state) { switch (state) {
case STATE_VALUE_READY: case STATE_VALUE_READY:
states.add(TaskState.READY); states.add(TaskState.READY);
break; break;
case STATE_VALUE_COMPLETED: case STATE_VALUE_COMPLETED:
states.add(TaskState.COMPLETED); states.add(TaskState.COMPLETED);
break; break;
case STATE_VALUE_CLAIMED: case STATE_VALUE_CLAIMED:
states.add(TaskState.CLAIMED); states.add(TaskState.CLAIMED);
break; break;
default: default:
throw new InvalidArgumentException("Unknown status '" + state + "'"); throw new InvalidArgumentException("Unknown status '" + state + "'");
} }
} }
} }
return states.toArray(new TaskState[0]); return states.toArray(new TaskState[0]);
} }
} }

View File

@ -17,9 +17,6 @@ import {WorkbasketResource} from '../../models/workbasket-resource';
@Injectable() @Injectable()
export class WorkbasketService { export class WorkbasketService {
workbasketKey: string;
workbasketName: string;
public workBasketSelected = new Subject<string>(); public workBasketSelected = new Subject<string>();
public workBasketSaved = new Subject<number>(); public workBasketSaved = new Subject<number>();

View File

@ -17,7 +17,7 @@
</div> </div>
<div *ngIf="currentRoute === 'tasks'" class="center-block no-detail"> <div *ngIf="currentRoute === 'tasks'" class="center-block no-detail">
<h3 class="grey">Select a Task</h3> <h3 class="grey">Select a Task</h3>
<!--TODO: ICON for task?--> <span class="glyphicon glyphicon-object-align-bottom"></span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,16 +4,31 @@ import {HttpClient} from '@angular/common/http';
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {environment} from 'app/../environments/environment'; import {environment} from 'app/../environments/environment';
import {TaskResource} from 'app/workplace/models/task-resource'; import {TaskResource} from 'app/workplace/models/task-resource';
import {Subject} from 'rxjs/Subject';
@Injectable() @Injectable()
export class TaskService { export class TaskService {
url = `${environment.taskanaRestUrl}/v1/tasks`; url = `${environment.taskanaRestUrl}/v1/tasks`;
taskChangedSource = new Subject<Task>();
taskChangedStream = this.taskChangedSource.asObservable();
taskDeletedSource = new Subject<Task>();
taskDeletedStream = this.taskDeletedSource.asObservable();
publishUpdatedTask(task: Task) {
this.taskChangedSource.next(task);
}
publishDeletedTask(task: Task) {
this.taskDeletedSource.next(task);
}
constructor(private httpClient: HttpClient) { constructor(private httpClient: HttpClient) {
} }
findTasksWithWorkbasket(basketKey: string): Observable<TaskResource> { findTasksWithWorkbasket(basketId: string): Observable<TaskResource> {
return this.httpClient.get<TaskResource>(`${this.url}?workbasket-id=${basketKey}`); return this.httpClient.get<TaskResource>(`${this.url}?workbasket-id=${basketId}`);
} }
getTask(id: string): Observable<Task> { getTask(id: string): Observable<Task> {
@ -28,11 +43,15 @@ export class TaskService {
return this.httpClient.post<Task>(`${this.url}/${id}/claim`, 'test'); return this.httpClient.post<Task>(`${this.url}/${id}/claim`, 'test');
} }
transferTask(taskId: string, workbasketKey: string): Observable<Task> { transferTask(taskId: string, workbasketId: string): Observable<Task> {
return this.httpClient.post<Task>(`${this.url}/${taskId}/transfer/${workbasketKey}`, ''); return this.httpClient.post<Task>(`${this.url}/${taskId}/transfer/${workbasketId}`, '');
} }
updateTask(task: Task): Observable<Task> { updateTask(task: Task): Observable<Task> {
return this.httpClient.put<Task>(`${this.url}/${task.taskId}`, task); return this.httpClient.put<Task>(`${this.url}/${task.taskId}`, task);
} }
deleteTask(task: Task): Observable<Task> {
return this.httpClient.delete<Task>(`${this.url}/${task.taskId}`);
}
} }

View File

@ -1,22 +1,32 @@
<taskana-spinner [isRunning]="requestInProgress" ></taskana-spinner> <taskana-spinner [isRunning]="requestInProgress"></taskana-spinner>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<div class="pull-right"> <div class="pull-right">
<div class="col-md-6"> <button type="button" (click)="navigateBack()" class="btn btn-default"><span title="Cancel and return to task detail"
<input class="form-control" auto-complete [(ngModel)]="workbasket" [source]="autoCompleteData" class="glyphicon glyphicon-arrow-left text-muted"></span>
placeholder="Transfer ..."/> </button>
<div class="dropdown" style="display: inline">
<button type="button" data-toggle="dropdown" aria-expanded="true" class="btn btn-default dropdown-toggle">
<span
title="Transfer task to another workbasket"
class="glyphicon glyphicon-transfer text-muted"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li *ngFor="let workbasket of workbaskets">
<a class="dropdown-item" (click)="transferTask(workbasket)">
<label>{{workbasket.name}}</label>
</a>
</li>
</ul>
</div> </div>
<button (click)="transferTask()" class="btn-link"><span title="Transfer task to another workbasket" <button type="button" (click)="completeTask()" class="btn btn-default"><span
class="glyphicon glyphicon-new-window text-muted"></span> title="Complete task and return to task list"
</button> class="glyphicon glyphicon-ok blue text-success"></span>
<button (click)="cancelTask()" class="btn-link"><span title="Cancel task and return to task list"
class="glyphicon glyphicon-remove-circle text-muted"></span>
</button>
<button (click)="completeTask()" class="btn-link"><span title="Complete task and return to task list"
class="glyphicon glyphicon-ok-circle text-success"></span>
</button> </button>
</div> </div>
<h4 class="panel-header"><b>{{task?.name}}</b></h4> <div class="panel-header"><h4><b>{{task?.name}}</b></h4></div>
</div> </div>
<div class="panel-body" *ngIf="task"> <div class="panel-body" *ngIf="task">

View File

@ -1,10 +1,11 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnDestroy, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {Task} from 'app/workplace/models/task'; import {Task} from 'app/workplace/models/task';
import {Workbasket} from 'app/models/workbasket'; import {Workbasket} from 'app/models/workbasket';
import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser'; import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser';
import {TaskService} from 'app/workplace/services/task.service'; import {TaskService} from 'app/workplace/services/task.service';
import {WorkbasketService} from 'app/services/workbasket/workbasket.service'; import {WorkbasketService} from 'app/services/workbasket/workbasket.service';
import {Subscription} from 'rxjs/Subscription';
@Component({ @Component({
@ -12,15 +13,17 @@ import {WorkbasketService} from 'app/services/workbasket/workbasket.service';
templateUrl: './task.component.html', templateUrl: './task.component.html',
styleUrls: ['./task.component.scss'] styleUrls: ['./task.component.scss']
}) })
export class TaskComponent implements OnInit { export class TaskComponent implements OnInit, OnDestroy {
task: Task = null;
routeSubscription: Subscription;
requestInProgress = false;
address = 'https://bing.com'; address = 'https://bing.com';
link: SafeResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.address); link: SafeResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.address);
autoCompleteData: string[] = [];
workbasket: string = null; task: Task = null;
workbasketKey: string;
workbaskets: Workbasket[]; workbaskets: Workbasket[];
requestInProgress = false;
constructor(private taskService: TaskService, constructor(private taskService: TaskService,
private workbasketService: WorkbasketService, private workbasketService: WorkbasketService,
@ -30,8 +33,10 @@ export class TaskComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
const id = this.route.snapshot.params['id']; this.routeSubscription = this.route.params.subscribe(params => {
this.getTask(id); const id = params['id'];
this.getTask(id);
});
} }
getTask(id: string) { getTask(id: string) {
@ -41,36 +46,35 @@ export class TaskComponent implements OnInit {
this.requestInProgress = false; this.requestInProgress = false;
this.task = task; this.task = task;
this.link = this.sanitizer.bypassSecurityTrustResourceUrl(`${this.address}/?q=${this.task.name}`); this.link = this.sanitizer.bypassSecurityTrustResourceUrl(`${this.address}/?q=${this.task.name}`);
this.workbasketService.getAllWorkBaskets().subscribe(workbaskets => { this.getWorkbaskets();
this.workbaskets = workbaskets._embedded ? workbaskets._embedded.workbaskets : [];
this.workbaskets.forEach(workbasket => {
if (workbasket.key !== this.task.workbasketSummaryResource.key) {
this.autoCompleteData.push(workbasket.name);
}
});
});
}); });
} }
transferTask() { getWorkbaskets() {
if (this.workbasket) { this.requestInProgress = true;
this.workbaskets.forEach(workbasket => { this.workbasketService.getAllWorkBaskets().subscribe(workbaskets => {
if (workbasket.name === this.workbasket) { this.requestInProgress = false;
this.workbasketKey = workbasket.key; this.workbaskets = workbaskets._embedded ? workbaskets._embedded.workbaskets : [];
let index = -1;
for (let i = 0; i < this.workbaskets.length; i++) {
if (this.workbaskets[i].name === this.task.workbasketSummaryResource.name) {
index = i;
} }
}); }
if (index !== -1) {
this.requestInProgress = true; this.workbaskets.splice(index, 1);
this.taskService.transferTask(this.task.taskId, this.workbasketKey).subscribe( }
task => { });
this.requestInProgress = false;
this.task = task
});
this.navigateBack();
}
} }
cancelTask() { transferTask(workbasket: Workbasket) {
this.requestInProgress = true;
this.taskService.transferTask(this.task.taskId, workbasket.workbasketId).subscribe(
task => {
this.requestInProgress = false;
this.task = task
});
this.navigateBack(); this.navigateBack();
} }
@ -79,12 +83,19 @@ export class TaskComponent implements OnInit {
this.taskService.completeTask(this.task.taskId).subscribe( this.taskService.completeTask(this.task.taskId).subscribe(
task => { task => {
this.requestInProgress = false; this.requestInProgress = false;
this.task = task this.task = task;
this.taskService.publishUpdatedTask(task);
this.navigateBack();
}); });
this.navigateBack();
} }
private navigateBack() { navigateBack() {
this.router.navigate([{outlets: {detail: `taskdetail/${this.task.taskId}`}}], {relativeTo: this.route.parent}); this.router.navigate([{outlets: {detail: `taskdetail/${this.task.taskId}`}}], {relativeTo: this.route.parent});
} }
ngOnDestroy(): void {
if (this.routeSubscription) {
this.routeSubscription.unsubscribe();
}
}
} }

View File

@ -1,14 +1,17 @@
<taskana-spinner [isRunning]="requestInProgress" ></taskana-spinner> <taskana-spinner [isRunning]="requestInProgress"></taskana-spinner>
<div class="panel panel-default" *ngIf="task"> <div class="panel panel-default" *ngIf="task && !requestInProgress">
<div class="panel-heading"> <div class="panel-heading">
<div class="pull-right"> <div class="pull-right">
<button type="button" title="Open task to work on it" class="btn btn-default" aria-label="Left Align" <button type="button" title="Open task to work on it" class="btn btn-default" aria-label="Left Align"
(click)="openTask(task.taskId)"> [disabled]="workOnTaskDisabled()" (click)="openTask(task.taskId)">
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
</button> </button>
<button type="button" title="Update Task" class="btn btn-default" (click)="updateTask()"> <button type="button" title="Update Task" class="btn btn-default btn-primary" (click)="updateTask()">
<span class="glyphicon glyphicon-saved" aria-hidden="true"></span> <span class="glyphicon glyphicon-saved" aria-hidden="true"></span>
</button> </button>
<button type="button" title="Delete Task" class="btn btn-default btn-danger" (click)="deleteTask()">
<span class="glyphicon glyphicon-remove"></span>
</button>
</div> </div>
<h4 class="panel-header"><b>{{task?.name}}</b></h4> <h4 class="panel-header"><b>{{task?.name}}</b></h4>
</div> </div>
@ -18,9 +21,9 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group"> <div class="form-group">
<label for="task-description" class="control-label">Description</label> <label for="task-description" class="control-label">Description</label>
<input type="text" class="form-control" id="task-description" placeholder="Description" <textarea type="text" class="form-control" id="task-description" placeholder="Description"
[(ngModel)]="task.description" [(ngModel)]="task.description"
name="task.description"> name="task.description"></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="task-owner" class="control-label">Owner</label> <label for="task-owner" class="control-label">Owner</label>
@ -63,7 +66,7 @@
name="task.due"> name="task.due">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="task-priority" class="control-label">Priority</label> <label for="task-priority" disabled class="control-label">Priority</label>
<input type="text" class="form-control" id="task-priority" placeholder="no priotity set" <input type="text" class="form-control" id="task-priority" placeholder="no priotity set"
[(ngModel)]="task.priority" [(ngModel)]="task.priority"
name="task.priority"> name="task.priority">

View File

@ -1,6 +1,6 @@
.list-group { .list-group {
max-height: 84vh; max-height: 85vh;
margin-bottom: 10px; margin-bottom: 10px;
overflow:scroll; overflow: hidden;
-webkit-overflow-scrolling: touch; overflow-y: scroll;
} }

View File

@ -1,15 +1,16 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnDestroy, OnInit} from '@angular/core';
import {Task} from '../models/task'; import {Task} from 'app/workplace/models/task';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {TaskService} from '../services/task.service'; import {TaskService} from 'app/workplace/services/task.service';
import {Subscription} from 'rxjs/Subscription'; import {Subscription} from 'rxjs/Subscription';
import {Location} from '@angular/common';
@Component({ @Component({
selector: 'taskana-task-details', selector: 'taskana-task-details',
templateUrl: './taskdetails.component.html', templateUrl: './taskdetails.component.html',
styleUrls: ['./taskdetails.component.scss'] styleUrls: ['./taskdetails.component.scss']
}) })
export class TaskdetailsComponent implements OnInit { export class TaskdetailsComponent implements OnInit, OnDestroy {
task: Task = null; task: Task = null;
requestInProgress = false; requestInProgress = false;
@ -17,7 +18,8 @@ export class TaskdetailsComponent implements OnInit {
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private taskService: TaskService, private taskService: TaskService,
private router: Router) { private router: Router,
private location: Location) {
} }
ngOnInit() { ngOnInit() {
@ -40,10 +42,28 @@ export class TaskdetailsComponent implements OnInit {
this.taskService.updateTask(this.task).subscribe(task => { this.taskService.updateTask(this.task).subscribe(task => {
this.requestInProgress = false; this.requestInProgress = false;
this.task = task; this.task = task;
this.taskService.publishUpdatedTask(task);
}); });
} }
openTask(taskId: string) { openTask(taskId: string) {
this.router.navigate([{outlets: {detail: `task/${taskId}`}}], {relativeTo: this.route.parent}); this.router.navigate([{outlets: {detail: `task/${taskId}`}}], {relativeTo: this.route.parent});
} }
workOnTaskDisabled(): boolean {
return this.task ? this.task.state === 'COMPLETED' : false;
}
deleteTask(): void {
this.taskService.deleteTask(this.task).subscribe();
this.taskService.publishDeletedTask(this.task);
this.task = null;
this.router.navigate([`/workplace/tasks`]);
}
ngOnDestroy(): void {
if (this.routeSubscription) {
this.routeSubscription.unsubscribe();
}
}
} }

View File

@ -1,9 +1,11 @@
<div class="task-list-full-height"> <div class="task-list-full-height">
<taskana-workbasket-selector (tasksChanged)="loadTasks($event)"></taskana-workbasket-selector> <taskana-workbasket-selector (tasksChanged)="loadTasks($event)"></taskana-workbasket-selector>
<!--TODO: add toolbar for sorting, also add pagination--> <!--TODO: add toolbar for sorting, also add pagination-->
<div> <div>
<ul #taskList id="task-list-container" class="list-group"> <ul #taskList id="task-list-container" class="list-group">
<li class="list-group-item" *ngIf="tasks === undefined || tasks.length === 0" type="text"> There are no Tasks for this workbasket </li> <li class="list-group-item" *ngIf="tasks === undefined || tasks.length === 0" type="text">
<b>This Workbasket has no Tasks</b>
</li>
<li class="list-group-item" *ngFor="let task of tasks" [class.active]="task.taskId == selectedId" <li class="list-group-item" *ngFor="let task of tasks" [class.active]="task.taskId == selectedId"
type="text" (click)="selectTask(task.taskId)"> type="text" (click)="selectTask(task.taskId)">
<div class="row"> <div class="row">

View File

@ -1,8 +1,8 @@
.clickable { .clickable {
cursor: pointer; cursor: pointer;
} }
.task-list-full-height{ .task-list-full-height {
height: calc(100vh - 55px); height: calc(100vh - 55px);
} }
@ -18,14 +18,15 @@
ul { ul {
max-height: 90vh; max-height: 90vh;
margin-bottom: 10px; margin-bottom: 10px;
overflow:scroll; overflow: hidden;
-webkit-overflow-scrolling: touch; overflow-y: scroll;
} }
a > label{ a > label {
height: 2em; height: 2em;
width: 100%; width: 100%;
} }
dd, dt { dd, dt {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@ -42,6 +43,7 @@ dt > i {
li > div.row > dl { li > div.row > dl {
margin-bottom: 0px; margin-bottom: 0px;
} }
li > div.row > dl:first-child { li > div.row > dl:first-child {
margin-left: 10px; margin-left: 10px;
} }

View File

@ -1,22 +1,41 @@
import {Component, Input, OnInit} from '@angular/core'; import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {Task} from '../models/task'; import {Task} from 'app/workplace/models/task';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {TaskService} from 'app/workplace/services/task.service';
import {Subscription} from 'rxjs/Subscription';
@Component({ @Component({
selector: 'taskana-task-list', selector: 'taskana-task-list',
templateUrl: './tasklist.component.html', templateUrl: './tasklist.component.html',
styleUrls: ['./tasklist.component.scss'] styleUrls: ['./tasklist.component.scss']
}) })
export class TasklistComponent implements OnInit { export class TasklistComponent implements OnInit, OnDestroy {
private columnForOrdering: string; private columnForOrdering: string;
private taskChangeSubscription: Subscription;
private taskDeletedSubscription: Subscription;
selectedId = ''; selectedId = '';
@Input() tasks: Task[]; @Input() tasks: Task[];
constructor(private router: Router, constructor(private router: Router,
private route: ActivatedRoute) { private route: ActivatedRoute,
private taskService: TaskService) {
this.columnForOrdering = 'id'; // default: order tasks by id this.columnForOrdering = 'id'; // default: order tasks by id
this.taskChangeSubscription = this.taskService.taskChangedStream.subscribe(task => {
for (let i = 0; i < this.tasks.length; i++) {
if (this.tasks[i].taskId === task.taskId) {
this.tasks[i] = task;
}
}
});
this.taskDeletedSubscription = this.taskService.taskDeletedStream.subscribe(task => {
for (let i = 0; i < this.tasks.length; i++) {
if (this.tasks[i].taskId === task.taskId) {
this.tasks.splice(i, 1);
}
}
})
} }
ngOnInit() { ngOnInit() {
@ -34,4 +53,9 @@ export class TasklistComponent implements OnInit {
this.selectedId = taskId; this.selectedId = taskId;
this.router.navigate([{outlets: {detail: `taskdetail/${this.selectedId}`}}], {relativeTo: this.route}); this.router.navigate([{outlets: {detail: `taskdetail/${this.selectedId}`}}], {relativeTo: this.route});
} }
ngOnDestroy(): void {
this.taskChangeSubscription.unsubscribe();
this.taskDeletedSubscription.unsubscribe();
}
} }

View File

@ -1,16 +1,14 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="input-group"> <div class="input-group">
<input class="form-control dropdown-toggle" auto-complete [(ngModel)]="result" [source]="autoCompleteData" <input [(ngModel)]="result" [typeahead]="workbasketNames" class="form-control"
(typeaheadOnSelect)="workbasketSelected = true" (typeaheadNoResults)="workbasketSelected = false"
placeholder="Search for Workbasket ..."/> placeholder="Search for Workbasket ..."/>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-primary" type="button" (click)="searchBasket()" <button class="btn btn-primary" type="button" (click)="searchBasket()"
[disabled]="result.length==0">Go!</button> [disabled]="!workbasketSelected">Go!</button>
</span> </span>
</div> </div>
<!-- /input-group -->
</div> </div>
<!-- /.col-lg-18 -->
</div> </div>
<!-- /.row -->

View File

@ -14,11 +14,13 @@ export class SelectorComponent implements OnInit {
tasksChanged = new EventEmitter<Task[]>(); tasksChanged = new EventEmitter<Task[]>();
tasks: Task[] = []; tasks: Task[] = [];
workbasketNames: string[] = [];
autoCompleteData: string[] = [];
result = ''; result = '';
resultKey: string; resultId = '';
workbaskets: Workbasket[]; workbaskets: Workbasket[];
currentBasket: Workbasket;
workbasketSelected = false;
constructor(private taskService: TaskService, constructor(private taskService: TaskService,
private workbasketService: WorkbasketService) { private workbasketService: WorkbasketService) {
@ -28,34 +30,37 @@ export class SelectorComponent implements OnInit {
this.workbasketService.getAllWorkBaskets().subscribe(workbaskets => { this.workbasketService.getAllWorkBaskets().subscribe(workbaskets => {
this.workbaskets = workbaskets._embedded ? workbaskets._embedded.workbaskets : []; this.workbaskets = workbaskets._embedded ? workbaskets._embedded.workbaskets : [];
this.workbaskets.forEach(workbasket => { this.workbaskets.forEach(workbasket => {
this.autoCompleteData.push(workbasket.name); this.workbasketNames.push(workbasket.name);
}); });
}); });
if (this.workbasketService.workbasketKey) {
this.getTasks(this.workbasketService.workbasketKey);
this.result = this.workbasketService.workbasketName;
}
} }
searchBasket() { searchBasket() {
if (this.workbaskets) { if (this.workbaskets) {
this.workbaskets.forEach(workbasket => { this.workbaskets.forEach(workbasket => {
if (workbasket.name === this.result) { if (workbasket.name === this.result) {
this.resultKey = workbasket.workbasketId; this.resultId = workbasket.workbasketId;
this.currentBasket = workbasket;
} }
}); });
this.getTasks(this.resultKey);
this.workbasketService.workbasketKey = this.resultKey; if (this.resultId.length > 0) {
this.workbasketService.workbasketName = this.result; this.getTasks(this.resultId);
this.tasksChanged.emit(this.tasks); this.tasksChanged.emit(this.tasks);
} else {
this.tasks = [];
this.tasksChanged.emit(this.tasks);
}
} }
this.resultId = '';
} }
getTasks(workbasketKey: string) { getTasks(workbasketId: string) {
this.taskService.findTasksWithWorkbasket(workbasketKey).subscribe( this.taskService.findTasksWithWorkbasket(workbasketId).subscribe(
tasks => { tasks => {
this.tasks.length = 0;
if (!tasks || tasks._embedded === undefined) { if (!tasks || tasks._embedded === undefined) {
this.tasks.length = 0;
return; return;
} }
tasks._embedded.tasks.forEach(e => this.tasks.push(e)); tasks._embedded.tasks.forEach(e => this.tasks.push(e));

View File

@ -1,20 +1,19 @@
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 {Ng2AutoCompleteModule} from 'ng2-auto-complete';
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http'; import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {AngularSvgIconModule} from 'angular-svg-icon'; import {AngularSvgIconModule} from 'angular-svg-icon';
import {WorkplaceRoutingModule} from './workplace-routing.module'; import {WorkplaceRoutingModule} from './workplace-routing.module';
import {AlertModule} from 'ngx-bootstrap'; import {AlertModule, TypeaheadModule} from 'ngx-bootstrap';
import { SelectorComponent } from './workbasket-selector/workbasket-selector.component'; import {SelectorComponent} from './workbasket-selector/workbasket-selector.component';
import { TasklistComponent } from './tasklist/tasklist.component'; import {TasklistComponent} from './tasklist/tasklist.component';
import { TaskdetailsComponent } from './taskdetails/taskdetails.component'; import {TaskdetailsComponent} from './taskdetails/taskdetails.component';
import { TaskComponent } from './task/task.component'; import {TaskComponent} from './task/task.component';
import { CodeComponent } from './components/code/code.component'; import {CodeComponent} from './components/code/code.component';
import { OrderTasksByPipe } from './util/orderTasksBy.pipe'; import {OrderTasksByPipe} from './util/orderTasksBy.pipe';
import {TaskService} from './services/task.service'; import {TaskService} from './services/task.service';
import {WorkbasketService} from 'app/services/workbasket/workbasket.service'; import {WorkbasketService} from 'app/services/workbasket/workbasket.service';
@ -23,9 +22,9 @@ import {CustomHttpClientInterceptor} from './services/custom-http-interceptor/cu
const MODULES = [ const MODULES = [
TypeaheadModule.forRoot(),
CommonModule, CommonModule,
FormsModule, FormsModule,
Ng2AutoCompleteModule,
HttpClientModule, HttpClientModule,
AngularSvgIconModule, AngularSvgIconModule,
WorkplaceRoutingModule, WorkplaceRoutingModule,