Merge branch '96-edit-project-page' into 'dev'

Resolve "Страница редактирования проекта"

Closes #96

See merge request romanov73/ng-tracker!57
This commit is contained in:
Anton Romanov 2019-04-18 09:32:48 +00:00
commit 8474fb492f
11 changed files with 361 additions and 13 deletions

View File

@ -2,20 +2,16 @@ package ru.ulstu.project.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import ru.ulstu.deadline.model.Deadline;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import ru.ulstu.project.model.Project;
import ru.ulstu.project.model.ProjectDto;
import ru.ulstu.project.service.ProjectService;
import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import static liquibase.util.StringUtils.isEmpty;
@Controller()
@RequestMapping(value = "/projects")
@ -36,4 +32,18 @@ public class ProjectController {
public void getProjects(ModelMap modelMap) {
modelMap.put("projects", projectService.findAllDto());
}
@GetMapping("/project")
public void getProject(ModelMap modelMap, @RequestParam(value = "id") Integer id) {
if (id != null && id > 0) {
modelMap.put("projectDto", projectService.findOneDto(id));
} else {
modelMap.put("projectDto", new ProjectDto());
}
}
@ModelAttribute("allStatuses")
public List<Project.ProjectStatus> getProjectStatuses() {
return projectService.getProjectStatuses();
}
}

View File

@ -3,24 +3,60 @@ package ru.ulstu.project.model;
import org.hibernate.validator.constraints.NotBlank;
import ru.ulstu.core.model.BaseEntity;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.grant.model.Grant;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Project extends BaseEntity {
public enum ProjectStatus {
APPLICATION("Заявка"),
ON_COMPETITION("Отправлен на конкурс"),
SUCCESSFUL_PASSAGE("Успешное прохождение"),
IN_WORK("В работе"),
COMPLETED("Завершен"),
FAILED("Провалены сроки");
private String statusName;
ProjectStatus(String statusName) {
this.statusName = statusName;
}
public String getStatusName() {
return statusName;
}
}
@NotBlank
private String title;
@Enumerated(value = EnumType.STRING)
private ProjectStatus status = ProjectStatus.APPLICATION;
@NotNull
private String description;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "project_id")
private List<Deadline> deadlines = new ArrayList<>();
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "grant_id")
private Grant grant;
@NotNull
private String repository;
public String getTitle() {
return title;
}
@ -29,6 +65,38 @@ public class Project extends BaseEntity {
this.title = title;
}
public ProjectStatus getStatus() {
return status;
}
public void setStatus(ProjectStatus status) {
this.status = status;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Grant getGrant() {
return grant;
}
public void setGrant(Grant grant) {
this.grant = grant;
}
public String getRepository() {
return repository;
}
public void setRepository(String repository) {
this.repository = repository;
}
public List<Deadline> getDeadlines() {
return deadlines;
}

View File

@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;
import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.grant.model.GrantDto;
import java.util.ArrayList;
import java.util.List;
@ -13,8 +14,11 @@ public class ProjectDto {
@NotEmpty
private String title;
private Project.ProjectStatus status;
private String description;
private List<Deadline> deadlines = new ArrayList<>();
private GrantDto grant;
private String repository;
public ProjectDto() {
}
@ -26,9 +30,17 @@ public class ProjectDto {
@JsonCreator
public ProjectDto(@JsonProperty("id") Integer id,
@JsonProperty("title") String title,
@JsonProperty("status") Project.ProjectStatus status,
@JsonProperty("description") String description,
@JsonProperty("grant") GrantDto grant,
@JsonProperty("repository") String repository,
@JsonProperty("deadlines") List<Deadline> deadlines) {
this.id = id;
this.title = title;
this.status = status;
this.description = description;
this.grant = grant;
this.repository = repository;
this.deadlines = deadlines;
}
@ -36,6 +48,10 @@ public class ProjectDto {
public ProjectDto(Project project) {
this.id = project.getId();
this.title = project.getTitle();
this.status = project.getStatus();
this.description = project.getDescription();
this.grant = project.getGrant() == null ? null : new GrantDto(project.getGrant());
this.repository = project.getRepository();
this.deadlines = project.getDeadlines();
}
@ -55,6 +71,38 @@ public class ProjectDto {
this.title = title;
}
public Project.ProjectStatus getStatus() {
return status;
}
public void setStatus(Project.ProjectStatus status) {
this.status = status;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public GrantDto getGrant() {
return grant;
}
public void setGrant(GrantDto grant) {
this.grant = grant;
}
public String getRepository() {
return repository;
}
public void setRepository(String repository) {
this.repository = repository;
}
public List<Deadline> getDeadlines() {
return deadlines;
}

View File

@ -8,6 +8,7 @@ import ru.ulstu.project.model.Project;
import ru.ulstu.project.model.ProjectDto;
import ru.ulstu.project.repository.ProjectRepository;
import java.util.Arrays;
import java.util.List;
import static org.springframework.util.ObjectUtils.isEmpty;
@ -36,6 +37,14 @@ public class ProjectService {
return projects;
}
public ProjectDto findOneDto(Integer id) {
return new ProjectDto(projectRepository.findOne(id));
}
public List<Project.ProjectStatus> getProjectStatuses() {
return Arrays.asList(Project.ProjectStatus.values());
}
@Transactional
public Project create(ProjectDto projectDto) {
Project newProject = copyFromDto(new Project(), projectDto);

View File

@ -0,0 +1,22 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="anton" id="20190418_000000-1">
<addColumn tableName="project">
<column name="status" type="varchar(255)"/>
</addColumn>
<addColumn tableName="project">
<column name="description" type="varchar(255)"/>
</addColumn>
<addColumn tableName="project">
<column name="grant_id" type="integer"/>
</addColumn>
<addForeignKeyConstraint baseTableName="project" baseColumnNames="grant_id"
constraintName="fk_project_grant_id" referencedTableName="grants"
referencedColumnNames="id"/>
<addColumn tableName="project">
<column name="repository" type="varchar(255)"/>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@ -24,5 +24,6 @@
<include file="db/changelog-20190331_000000-schema.xml"/>
<include file="db/changelog-20190331_000010-schema.xml"/>
<include file="db/changelog-20190410_000000-schema.xml"/>
<include file="db/changelog-20190418_000000-schema.xml"/>
<include file="db/common/changelog-20190312_130000-schema.xml"/>
</databaseChangeLog>

View File

@ -0,0 +1,18 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerfiles">
<meta charset="UTF-8"/>
</head>
<body>
<div th:fragment="projectLine (project)" class="row text-left project-row" style="background-color: white;">
<div class="col">
<span th:replace="projects/fragments/projectStatusFragment :: projectStatus(projectStatus=${project.status})"/>
<a th:href="@{'project?id='+${project.id}}">
<span class="h6" th:text="${project.title}"/>
<span class="text-muted" th:text="${project.description}"/>
</a>
<input class="id-class" type="hidden" th:value="${project.id}"/>
</div>
</div>
</body>
</html>

View File

@ -16,7 +16,7 @@
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="javascript:void(0)" class="btn btn-light toolbar-button"><i class="fa fa-plus-circle"
<a href="./project?id=0" class="btn btn-light toolbar-button"><i class="fa fa-plus-circle"
aria-hidden="true"></i>
Добавить проект</a>

View File

@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerfiles">
<meta charset="UTF-8"/>
</head>
<body>
<span th:fragment="paperStatus (paperStatus)" class="fa-stack fa-1x">
<th:block th:switch="${projectStatus.name()}">
<div th:case="'APPLICATION'">
<i class="fa fa-circle fa-stack-2x text-draft"></i>
</div>
<div th:case="'ON_COMPETITION'">
<i class="fa fa-circle fa-stack-2x text-review"></i>
</div>
<div th:case="'SUCCESSFUL_PASSAGE'">
<i class="fa fa-circle fa-stack-2x text-accepted"></i>
</div>
<div th:case="'IN_WORK'">
<i class="fa fa-circle fa-stack-2x text-primary"></i>
</div>
<div th:case="'COMPLETED'">
<i class="fa fa-circle fa-stack-2x text-success"></i>
</div>
<div th:case="'FAILED'">
<i class="fa fa-circle fa-stack-2x text-failed"></i>
</div>
</th:block>
<i class="fa fa-file-text-o fa-stack-1x fa-inverse"></i>
</span>
</body>
</html>

View File

@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html">
<head>
</head>
<body>
<div class="container" layout:fragment="content">
<section id="paper">
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Редактирование проекта</h2>
<div th:replace="projects/fragments/projectNavigationFragment"/>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<form id="project-form" method="post" th:action="@{'/projects/project?id='+ *{id == null ? '' : id} + ''}"
th:object="${projectDto}">
<div class="row">
<div class="col-md-6 col-sm-12">
<input type="hidden" name="id" th:field="*{id}"/>
<div class="form-group">
<label for="title">Название:</label>
<input class="form-control" id="title" type="text"
placeholder="Название проекта"
th:field="*{title}"/>
<p th:if="${#fields.hasErrors('title')}" th:errors="*{title}"
class="alert alert-danger">Incorrect title</p>
<p class="help-block text-danger"></p>
</div>
<div class="form-group">
<label for="status">Статус:</label>
<select class="form-control" th:field="*{status}" id="status">
<option th:each="status : ${allStatuses}" th:value="${status}"
th:text="${status.statusName}">Status
</option>
</select>
</div>
<div class="form-group">
<label for="description">Описание:</label>
<textarea class="form-control" rows="3" id="description"
th:field="*{description}"></textarea>
</div>
<div class="form-group">
<label for="createGrant">Добавить грант:</label>
<div th:if="*{grant} == null">
<input type="submit" id="createGrant" name="createGrant" class="btn btn-primary"
value="Добавить грант"/>
</div>
<input type = "hidden" th:field="*{grant.id}"/>
</div>
<div class="form-group">
<label for="repository">Ссылка на репозиторий:</label>
<input class="form-control" id="repository" type="text"
placeholder="http://"
th:field="*{repository}"/>
<p th:if="${#fields.hasErrors('repository')}" th:errors="*{repository}"
class="alert alert-danger">Incorrect repository link</p>
<p class="help-block text-danger"></p>
</div>
<div class="form-group">
<label>Дедлайны показателей:</label>
<div class="row">
<input type="hidden"/>
<div class="col-6">
<input type="date" class="form-control" name="deadline"/>
</div>
<div class="col-4">
<input class="form-control" type="text" placeholder="Описание"/>
</div>
<div class="col-2">
<a class="btn btn-danger float-right"><span
aria-hidden="true"><i class="fa fa-times"/></span>
</a>
</div>
</div>
<p th:if="${#fields.hasErrors('deadlines')}" th:errors="*{deadlines}"
class="alert alert-danger">Incorrect title</p>
</div>
<div class="form-group">
<input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary" value="Добавить
дедлайн"/>
</div>
<div class="form-group">
<label for="loader">Загрузить файл:</label>
<div id="loader">
</div>
</div>
</div>
<div class="clearfix"></div>
<div class="col-lg-12">
<div class="form-group">
<button id="sendMessageButton" name="save" class="btn btn-success text-uppercase"
type="submit">
Сохранить
</button>
<button id="cancelButton" class="btn btn-default text-uppercase" href="/projects/projects">
Отмена
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</section>
<script type="text/javascript" src="/js/file-loader.js"></script>
<script>
/*<![CDATA[*/
$(document).ready(function () {
new FileLoader({
div: "loader",
url: urlFileUpload,
maxSize: 2,
extensions: ["doc", "docx", "xls", "jpg", "png", "pdf", "txt"],
callback: function (response) {
showFeedbackMessage("Файл успешно загружен");
console.debug(response);
}
});
$('.selectpicker').selectpicker();
});
/*]]>*/
</script>
</div>
</body>
</html>

View File

@ -18,8 +18,8 @@
</div>
<div class="row">
<div class="col-md-9 col-sm-12">
<th:block>
<div/>
<th:block th:each="project : ${projects}">
<div th:replace="projects/fragments/projectLineFragment :: projectLine(project=${project})"/>
</th:block>
</div>
<div class="col-md-3 col-sm-12">