Merge branch '76-students-tags-filter' into 'dev'

Resolve "Фильтр задач по типу (тегам)"

Closes #77, #75, and #76

See merge request romanov73/ng-tracker!61
This commit is contained in:
Anton Romanov 2019-04-24 09:36:03 +00:00
commit bb2518e5f4
13 changed files with 180 additions and 33 deletions

View File

@ -5,13 +5,16 @@ import org.springframework.ui.ModelMap;
import org.springframework.validation.Errors; import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
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.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import ru.ulstu.deadline.model.Deadline; import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.students.model.Task; import ru.ulstu.students.model.Task;
import ru.ulstu.students.model.TaskDto; import ru.ulstu.students.model.TaskDto;
import ru.ulstu.students.model.TaskFilterDto;
import ru.ulstu.students.service.TaskService; import ru.ulstu.students.service.TaskService;
import ru.ulstu.tags.model.Tag;
import springfox.documentation.annotations.ApiIgnore; import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid; import javax.validation.Valid;
@ -35,16 +38,16 @@ public class TaskController {
this.taskService = taskService; this.taskService = taskService;
} }
@GetMapping("/tasks")
public void getTasks(ModelMap modelMap) {
modelMap.put("tasks", taskService.findAllDto());
}
@GetMapping("/dashboard") @GetMapping("/dashboard")
public void getDashboard(ModelMap modelMap) { public void getDashboard(ModelMap modelMap) {
modelMap.put("tasks", taskService.findAllDto()); modelMap.put("tasks", taskService.findAllDto());
} }
@GetMapping("/tasks")
public void getTask(ModelMap modelMap) {
modelMap.put("filteredTasks", new TaskFilterDto(taskService.findAllDto(), null, null, null));
}
@GetMapping("/task") @GetMapping("/task")
public void getTask(ModelMap modelMap, @RequestParam(value = "id") Integer id) { public void getTask(ModelMap modelMap, @RequestParam(value = "id") Integer id) {
if (id != null && id > 0) { if (id != null && id > 0) {
@ -54,6 +57,14 @@ public class TaskController {
} }
} }
@PostMapping("/tasks")
public void filterTasks(@Valid TaskFilterDto taskFilterDto, ModelMap modelMap) {
modelMap.put("filteredTasks", new TaskFilterDto(taskService.filter(taskFilterDto),
taskFilterDto.getStatus(),
taskFilterDto.getTag(),
taskFilterDto.getOrder()));
}
@PostMapping(value = "/task", params = "save") @PostMapping(value = "/task", params = "save")
public String save(@Valid TaskDto taskDto, Errors errors) throws IOException { public String save(@Valid TaskDto taskDto, Errors errors) throws IOException {
filterEmptyDeadlines(taskDto); filterEmptyDeadlines(taskDto);
@ -77,11 +88,23 @@ public class TaskController {
return TASK_PAGE; return TASK_PAGE;
} }
@GetMapping("/delete/{task-id}")
public String delete(@PathVariable("task-id") Integer taskId) throws IOException {
taskService.delete(taskId);
return String.format(REDIRECT_TO, TASKS_PAGE);
}
@ModelAttribute("allStatuses") @ModelAttribute("allStatuses")
public List<Task.TaskStatus> getTaskStatuses() { public List<Task.TaskStatus> getTaskStatuses() {
return taskService.getTaskStatuses(); return taskService.getTaskStatuses();
} }
@ModelAttribute("allTags")
public List<Tag> getTags() {
return taskService.getTags();
}
private void filterEmptyDeadlines(TaskDto taskDto) { private void filterEmptyDeadlines(TaskDto taskDto) {
taskDto.setDeadlines(taskDto.getDeadlines().stream() taskDto.setDeadlines(taskDto.getDeadlines().stream()
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription())) .filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))

View File

@ -50,7 +50,7 @@ public class Task extends BaseEntity {
private String description; private String description;
@Enumerated(value = EnumType.STRING) @Enumerated(value = EnumType.STRING)
private ru.ulstu.students.model.Task.TaskStatus status = TaskStatus.IN_WORK; private TaskStatus status = TaskStatus.IN_WORK;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "task_id", unique = true) @JoinColumn(name = "task_id", unique = true)

View File

@ -26,7 +26,7 @@ public class TaskDto {
private Date createDate; private Date createDate;
private Date updateDate; private Date updateDate;
private Set<Integer> tagIds; private Set<Integer> tagIds;
private List<Tag> tags; private List<Tag> tags = new ArrayList<>();
public TaskDto() { public TaskDto() {
deadlines.add(new Deadline()); deadlines.add(new Deadline());

View File

@ -0,0 +1,54 @@
package ru.ulstu.students.model;
import java.util.List;
public class TaskFilterDto {
private List<TaskDto> tasks;
private Task.TaskStatus status;
private Integer tagId;
private String order;
public TaskFilterDto(List<TaskDto> tasks, Task.TaskStatus status, Integer tagId, String order) {
this.tasks = tasks;
this.status = status;
this.tagId = tagId;
this.order = order;
}
public TaskFilterDto() {
}
public List<TaskDto> getTasks() {
return tasks;
}
public void setTasks(List<TaskDto> tasks) {
this.tasks = tasks;
}
public Task.TaskStatus getStatus() {
return status;
}
public void setStatus(Task.TaskStatus status) {
this.status = status;
}
public Integer getTag() {
return tagId;
}
public void setTag(Integer tagId) {
this.tagId = tagId;
}
public String getOrder() {
return order;
}
public void setOrder(String order) {
this.order = order;
}
}

View File

@ -1,7 +1,18 @@
package ru.ulstu.students.repository; package ru.ulstu.students.repository;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ru.ulstu.students.model.Task; import ru.ulstu.students.model.Task;
import ru.ulstu.tags.model.Tag;
import java.util.List;
public interface TaskRepository extends JpaRepository<Task, Integer> { public interface TaskRepository extends JpaRepository<Task, Integer> {
@Query("SELECT t FROM Task t WHERE (t.status = :status OR :status IS NULL) AND (:tag IS NULL OR :tag MEMBER OF t.tags) ORDER BY create_date DESC")
List<Task> filterNew(@Param("status") Task.TaskStatus status, @Param("tag") Tag tag);
@Query("SELECT t FROM Task t WHERE (t.status = :status OR :status IS NULL) AND (:tag IS NULL OR :tag MEMBER OF t.tags) ORDER BY create_date ASC")
List<Task> filterOld(@Param("status") Task.TaskStatus status, @Param("tag") Tag tag);
} }

View File

@ -1,12 +1,15 @@
package ru.ulstu.students.service; package ru.ulstu.students.service;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ru.ulstu.deadline.service.DeadlineService; import ru.ulstu.deadline.service.DeadlineService;
import ru.ulstu.students.model.Task; import ru.ulstu.students.model.Task;
import ru.ulstu.students.model.TaskDto; import ru.ulstu.students.model.TaskDto;
import ru.ulstu.students.model.TaskFilterDto;
import ru.ulstu.students.repository.TaskRepository; import ru.ulstu.students.repository.TaskRepository;
import ru.ulstu.tags.model.Tag;
import ru.ulstu.tags.service.TagService; import ru.ulstu.tags.service.TagService;
import java.io.IOException; import java.io.IOException;
@ -27,15 +30,15 @@ public class TaskService {
private final DeadlineService deadlineService; private final DeadlineService deadlineService;
private final TagService tagService; private final TagService tagService;
public TaskService(TaskRepository grantRepository, public TaskService(TaskRepository taskRepository,
DeadlineService deadlineService, TagService tagService) { DeadlineService deadlineService, TagService tagService) {
this.taskRepository = grantRepository; this.taskRepository = taskRepository;
this.deadlineService = deadlineService; this.deadlineService = deadlineService;
this.tagService = tagService; this.tagService = tagService;
} }
public List<Task> findAll() { public List<Task> findAll() {
return taskRepository.findAll(); return taskRepository.findAll(new Sort(Sort.Direction.DESC, "createDate"));
} }
public List<TaskDto> findAllDto() { public List<TaskDto> findAllDto() {
@ -48,6 +51,18 @@ public class TaskService {
return new TaskDto(taskRepository.findOne(id)); return new TaskDto(taskRepository.findOne(id));
} }
public List<TaskDto> filter(TaskFilterDto filterDto) {
if (filterDto.getOrder().compareTo("new") == 0) {
return convert(taskRepository.filterNew(
filterDto.getStatus(),
filterDto.getTag() == null ? null : tagService.findById(filterDto.getTag())), TaskDto::new);
} else {
return convert(taskRepository.filterOld(
filterDto.getStatus(),
filterDto.getTag() == null ? null : tagService.findById(filterDto.getTag())), TaskDto::new);
}
}
@Transactional @Transactional
public Integer create(TaskDto taskDto) throws IOException { public Integer create(TaskDto taskDto) throws IOException {
Task newTask = copyFromDto(new Task(), taskDto); Task newTask = copyFromDto(new Task(), taskDto);
@ -76,8 +91,10 @@ public class TaskService {
@Transactional @Transactional
public void delete(Integer taskId) throws IOException { public void delete(Integer taskId) throws IOException {
Task task = taskRepository.findOne(taskId); if (taskRepository.exists(taskId)) {
taskRepository.delete(task); taskRepository.delete(taskId);
}
} }
public void save(TaskDto taskDto) throws IOException { public void save(TaskDto taskDto) throws IOException {
@ -92,4 +109,8 @@ public class TaskService {
return Arrays.asList(Task.TaskStatus.values()); return Arrays.asList(Task.TaskStatus.values());
} }
public List<Tag> getTags() {
return tagService.getTags();
}
} }

View File

@ -50,4 +50,12 @@ public class TagService {
return newTag; return newTag;
} }
public List<Tag> getTags() {
return tagRepository.findAll();
}
public Tag findById(Integer tagId) {
return tagRepository.findOne(tagId);
}
} }

View File

@ -13,6 +13,21 @@
cursor: text; cursor: text;
} }
.filter .bootstrap-select{
margin-bottom: 10px;
}
.filter-option-inner-inner{
font-size: 12px;
text-transform: uppercase;
font-weight: normal;
line-height: 25px;
}
.sorting .bootstrap-select{
margin-bottom: 10px;
}
.input-tag-name { .input-tag-name {
border: none; border: none;
box-shadow: none; box-shadow: none;

View File

@ -90,7 +90,7 @@ $(document).ready(function () {
' <div class="modal-dialog modal-sm">\n' + ' <div class="modal-dialog modal-sm">\n' +
' <div class="modal-content">\n' + ' <div class="modal-content">\n' +
' <div class="modal-header">\n' + ' <div class="modal-header">\n' +
' <h8 class="modal-title" id="myModalLabel">Удалить статью?</h8>\n' + ' <h8 class="modal-title" id="myModalLabel">Удалить задачу?</h8>\n' +
' <button type="button" class="close" data-dismiss="modal" aria-label="Закрыть"><span\n' + ' <button type="button" class="close" data-dismiss="modal" aria-label="Закрыть"><span\n' +
' aria-hidden="true">&times;</span></button>\n' + ' aria-hidden="true">&times;</span></button>\n' +
' </div>\n' + ' </div>\n' +

View File

@ -242,6 +242,8 @@
/*]]>*/ /*]]>*/
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
function updateAuthors() { function updateAuthors() {

View File

@ -14,10 +14,10 @@
</div> </div>
<hr/> <hr/>
<div class="row justify-content-center" id="dashboard"> <div class="row justify-content-center" id="dashboard">
<div th:replace="students/fragments/taskDashboardFragment"/> <th:block th:each="task : ${tasks}">
<!--<th:block th:each="task : ${tasks}">--> <div th:replace="students/fragments/taskDashboardFragment :: taskDashboard(task=${task})"/>
<!--<div th:replace="students/fragments/taskDashboardFragment :: taskDashboard(task=${task})"/>--> </th:block>
<!--</th:block>-->
</div> </div>
</div> </div>
</section> </section>

View File

@ -6,9 +6,6 @@
<body> <body>
<span th:fragment="taskStatus (taskStatus)" class="fa-stack fa-1x"> <span th:fragment="taskStatus (taskStatus)" class="fa-stack fa-1x">
<th:block th:switch="${taskStatus.name()}"> <th:block th:switch="${taskStatus.name()}">
<!--<div th:case="'ATTENTION'">-->
<!--<i class="fa fa-circle fa-stack-2x text-warning"></i>-->
<!--</div>-->
<div th:case="'IN_WORK'"> <div th:case="'IN_WORK'">
<i class="fa fa-circle fa-stack-2x text-primary"></i> <i class="fa fa-circle fa-stack-2x text-primary"></i>
</div> </div>

View File

@ -7,7 +7,7 @@
<body> <body>
<div class="container" layout:fragment="content"> <div class="container" layout:fragment="content">
<form id="tasks-form" method="post" th:action="@{'/tasks/tasks'}"> <form id="tasks-form" method="post" th:action="@{'/students/tasks'}">
<input th:type="hidden" name="taskDeleteId" id="taskDeleteId"/> <input th:type="hidden" name="taskDeleteId" id="taskDeleteId"/>
<section id="tasks"> <section id="tasks">
<div class="container"> <div class="container">
@ -20,27 +20,43 @@
<hr/> <hr/>
<div class="row"> <div class="row">
<div class="col-md-9 col-sm-12"> <div class="col-md-9 col-sm-12">
<th:block th:each="task : ${tasks}"> <th:block th:each="task : ${filteredTasks.tasks}">
<div th:replace="students/fragments/taskLineFragment :: taskLine(task=${task})"/> <div th:replace="students/fragments/taskLineFragment :: taskLine(task=${task})"/>
</th:block> </th:block>
</div> </div>
<div class="col-md-3 col-sm-12"> <div class="col-md-3 col-sm-12">
<div class="sorting">
<h5>Сортировать:</h5>
<select class="form-control selectpicker" size="auto" th:field="${filteredTasks.order}"
id="order"
onchange="this.form.submit();">
<option th:value="new">
Сначала новые
</option>
<option th:value="old">
Сначала старые
</option>
</select>
</div>
<div class="filter"> <div class="filter">
<h5>Фильтр:</h5> <h5>Фильтр:</h5>
<select class="form-control" id="status" <select class="form-control selectpicker" size="auto" th:field="${filteredTasks.status}"
id="status"
onchange="this.form.submit();"> onchange="this.form.submit();">
<option value="">Все статусы</option> <option value="">Все статусы</option>
<!--<option th:each="author: ${allAuthors}" th:value="${author.id}"--> <option th:each="status: ${allStatuses}" th:value="${status}"
<!--th:text="${author.lastName}">lastName--> th:text="${status.statusName}">
<!--</option>--> status
</option>
</select>
<select class="form-control selectpicker" size="auto" data-live-search="true"
th:field="${filteredTasks.tag}" id="tags"
onchange="this.form.submit();">
<option value="">Все теги</option>
<option th:each="tag: ${allTags}" th:value="${tag.id}"
th:text="${tag.tagName}">tag
</option>
</select> </select>
<select class="form-control" id="tags"
onchange="this.form.submit();">
<option value="">Все типы</option>
<!--<option th:each="year: ${allYears}" th:value="${year}"-->
<!--th:text="${year}">year-->
<!--</option>-->
</select>
</div> </div>
</div> </div>
</div> </div>