Merge branch '46-mvc' into 'master'

Resolve "Перевести на MVC контроллеры"

Closes #46

See merge request romanov73/ng-tracker!22
This commit is contained in:
Anton Romanov 2018-12-18 18:18:25 +00:00
commit 503f7f68bc
25 changed files with 615 additions and 446 deletions

View File

@ -116,6 +116,7 @@ dependencies {
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
compile group: 'org.webjars', name: 'bootstrap', version: '4.1.0'
compile group: 'org.webjars', name: 'bootstrap-select', version: '1.13.3'
compile group: 'org.webjars', name: 'jquery', version: '3.3.1-1'
compile group: 'org.webjars.npm', name: 'jquery.easing', version: '1.4.1'
compile group: 'org.webjars', name: 'font-awesome', version: '4.7.0'

View File

@ -97,10 +97,4 @@ public class AdviceController {
public ResponseExtended<String> handleUserIsUndeadException(Throwable e) {
return handleException(ErrorConstants.USER_UNDEAD_ERROR, e.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseExtended<String> handleUnknownException(Throwable e) {
e.printStackTrace();
return handleException(ErrorConstants.UNKNOWN, e.getMessage());
}
}

View File

@ -2,15 +2,20 @@ package ru.ulstu.deadline.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
public class DeadlineDto {
private final Integer id;
private Integer id;
private final String description;
private String description;
private final Date date;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date date;
public DeadlineDto() {
}
@JsonCreator
public DeadlineDto(@JsonProperty("id") Integer id,
@ -38,4 +43,16 @@ public class DeadlineDto {
public Date getDate() {
return date;
}
public void setId(Integer id) {
this.id = id;
}
public void setDescription(String description) {
this.description = description;
}
public void setDate(Date date) {
this.date = date;
}
}

View File

@ -20,7 +20,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import static java.nio.charset.StandardCharsets.UTF_8;
import static ru.ulstu.paper.controller.PaperController.URL;
import static ru.ulstu.file.FileController.URL;
@RestController
@RequestMapping(URL)

View File

@ -1,70 +1,117 @@
package ru.ulstu.paper.controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.ulstu.configuration.Constants;
import ru.ulstu.core.model.response.Response;
import org.springframework.web.bind.annotation.RequestParam;
import ru.ulstu.deadline.model.DeadlineDto;
import ru.ulstu.paper.model.Paper;
import ru.ulstu.paper.model.PaperDto;
import ru.ulstu.paper.model.PaperFilterDto;
import ru.ulstu.paper.model.PaperStatusDto;
import ru.ulstu.paper.service.PaperService;
import ru.ulstu.user.model.User;
import javax.validation.Valid;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.stream.Collectors;
import static ru.ulstu.paper.controller.PaperController.URL;
import static org.springframework.util.StringUtils.isEmpty;
@RestController
@RequestMapping(URL)
@Controller()
@RequestMapping(value = "/papers")
public class PaperController {
public static final String URL = Constants.API_1_0 + "papers";
private final PaperService paperService;
public PaperController(PaperService paperService) {
this.paperService = paperService;
}
@GetMapping
public Response<List<PaperDto>> getPapers() {
return new Response<>(paperService.findAllDto());
@GetMapping("/papers")
public void getPapers(ModelMap modelMap) {
modelMap.put("filteredPapers", new PaperFilterDto(paperService.findAllDto(), null, null));
}
@GetMapping("/{paper-id}")
public Response<PaperDto> getPaper(@PathVariable("paper-id") Integer paperId){
return new Response(paperService.findPaper(paperId));
@PostMapping("/papers")
public void filterPapers(@Valid PaperFilterDto paperFilterDto, ModelMap modelMap) {
modelMap.put("filteredPapers", new PaperFilterDto(paperService.filter(paperFilterDto),
paperFilterDto.getFilterAuthorId(),
paperFilterDto.getYear()));
}
@PostMapping
public Response<Integer> createPaper(@RequestBody @Valid PaperDto paperDto) throws IOException {
return new Response<>(paperService.create(paperDto));
@GetMapping("/dashboard")
public void getDashboard(ModelMap modelMap) {
modelMap.put("papers", paperService.findAllDto());
}
@PutMapping
public Response<Integer> updatePaper(@RequestBody @Valid PaperDto paperDto) throws IOException {
return new Response<>(paperService.update(paperDto));
@GetMapping("/paper")
public void getPapers(ModelMap modelMap, @RequestParam(value = "id") Integer id) {
if (id != null && id > 0) {
modelMap.put("paperDto", paperService.findOneDto(id));
} else {
modelMap.put("paperDto", new PaperDto());
}
}
@DeleteMapping("/{paper-id}")
public Response<Boolean> delete(@PathVariable("paper-id") Integer paperId) throws IOException {
@PostMapping(value = "/paper", params = "save")
public String save(@Valid PaperDto paperDto, Errors errors) throws IOException {
filterEmptyDeadlines(paperDto);
if (paperDto.getDeadlines().isEmpty()) {
errors.rejectValue("deadlines", "errorCode","Не может быть пустым");
}
if (errors.hasErrors()) {
return "/papers/paper";
}
paperService.save(paperDto);
return "redirect:/papers/papers";
}
@PostMapping(value = "/paper", params = "addDeadline")
public String addDeadline(@Valid PaperDto paperDto, Errors errors) {
filterEmptyDeadlines(paperDto);
if (errors.hasErrors()) {
return "/papers/paper";
}
paperDto.getDeadlines().add(new DeadlineDto());
return "/papers/paper";
}
@GetMapping("/delete/{paper-id}")
public String delete(@PathVariable("paper-id") Integer paperId) throws IOException {
paperService.delete(paperId);
return new Response<>(true);
return "redirect:/papers/papers";
}
@GetMapping("/statuses")
public Response<List<PaperStatusDto>> getPaperStatuses() {
return new Response<>(paperService.getPaperStatuses());
@ModelAttribute("allStatuses")
public List<Paper.PaperStatus> getPaperStatuses() {
return paperService.getPaperStatuses();
}
@PostMapping("/filter")
public Response<List<PaperDto>> filter(@RequestBody @Valid PaperFilterDto paperFilterDto) throws IOException {
return new Response<>(paperService.filter(paperFilterDto));
@ModelAttribute("allAuthors")
public List<User> getAllAuthors() {
return paperService.getPaperAuthors();
}
@ModelAttribute("allYears")
public List<Integer> getAllYears() {
List<Integer> years = new ArrayList<>();
for (int i = Calendar.getInstance().get(Calendar.YEAR); i > 2010; i-- ) {
years.add(i);
}
return years;
}
private void filterEmptyDeadlines(PaperDto paperDto) {
paperDto.setDeadlines(paperDto.getDeadlines().stream()
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))
.collect(Collectors.toList()));
}
}

View File

@ -0,0 +1,64 @@
package ru.ulstu.paper.controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.ulstu.configuration.Constants;
import ru.ulstu.core.model.response.Response;
import ru.ulstu.paper.model.PaperDto;
import ru.ulstu.paper.model.PaperFilterDto;
import ru.ulstu.paper.service.PaperService;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static ru.ulstu.paper.controller.PaperRestController.URL;
@RestController
@RequestMapping(URL)
public class PaperRestController {
public static final String URL = Constants.API_1_0 + "papers";
private final PaperService paperService;
public PaperRestController(PaperService paperService) {
this.paperService = paperService;
}
@GetMapping
public Response<List<PaperDto>> getPapers() {
return new Response<>(paperService.findAllDto());
}
@GetMapping("/{paper-id}")
public Response<PaperDto> getPaper(@PathVariable("paper-id") Integer paperId){
return new Response(paperService.findById(paperId));
}
@PostMapping
public Response<Integer> createPaper(@RequestBody @Valid PaperDto paperDto) throws IOException {
return new Response<>(paperService.create(paperDto));
}
@PutMapping
public Response<Integer> updatePaper(@RequestBody @Valid PaperDto paperDto) throws IOException {
return new Response<>(paperService.update(paperDto));
}
@DeleteMapping("/{paper-id}")
public Response<Boolean> delete(@PathVariable("paper-id") Integer paperId) throws IOException {
paperService.delete(paperId);
return new Response<>(true);
}
@PostMapping("/filter")
public Response<List<PaperDto>> filter(@RequestBody @Valid PaperFilterDto paperFilterDto) throws IOException {
return new Response<>(paperService.filter(paperFilterDto));
}
}

View File

@ -16,6 +16,8 @@ import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.OneToMany;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
@ -36,14 +38,14 @@ public class Paper extends BaseEntity implements UserContainer {
COMPLETED("Завершена"),
FAILED("Провалены сроки");
private String name;
private String statusName;
PaperStatus(String name) {
this.name = name;
this.statusName = name;
}
public String getName() {
return name;
public String getStatusName() {
return statusName;
}
}
@ -54,12 +56,14 @@ public class Paper extends BaseEntity implements UserContainer {
private PaperStatus status = PaperStatus.DRAFT;
@Column(name = "create_date")
@Temporal(TemporalType.TIMESTAMP)
private Date createDate = new Date();
@Column(name = "update_date")
@Temporal(TemporalType.TIMESTAMP)
private Date updateDate = new Date();
@OneToMany(cascade = CascadeType.ALL)
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "paper_id")
private List<Deadline> deadlines = new ArrayList<>();
@ -154,6 +158,7 @@ public class Paper extends BaseEntity implements UserContainer {
public Optional<Deadline> getNextDeadline() {
return deadlines
.stream()
.filter(deadline -> deadline.getDate() != null)
.sorted(Comparator.comparing(Deadline::getDate))
.filter(d -> d.getDate().after(new Date()))
.findFirst();

View File

@ -6,7 +6,8 @@ import org.hibernate.validator.constraints.NotEmpty;
import ru.ulstu.deadline.model.DeadlineDto;
import ru.ulstu.user.model.UserDto;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
@ -15,20 +16,28 @@ import java.util.stream.Collectors;
import static ru.ulstu.core.util.StreamApiUtils.convert;
public class PaperDto {
private final Integer id;
private Integer id;
@NotEmpty
private final String title;
private final Paper.PaperStatus status;
private final Date createDate;
private final Date updateDate;
private final List<DeadlineDto> deadlines;
private final String comment;
private final Boolean locked;
private final String tmpFileName;
private final Integer fileId;
private final String fileName;
private final Date fileCreateDate;
private final Set<UserDto> authors;
@Size(min = 3, max = 100)
private String title;
private Paper.PaperStatus status;
private Date createDate;
private Date updateDate;
@NotEmpty
private List<DeadlineDto> deadlines = new ArrayList<>();
private String comment;
private Boolean locked;
private String tmpFileName;
private Integer fileId;
private String fileName;
private Date fileCreateDate;
private Set<Integer> authorIds;
private Set<UserDto> authors;
private Integer filterAuthorId;
public PaperDto() {
deadlines.add(new DeadlineDto());
}
@JsonCreator
public PaperDto(@JsonProperty("id") Integer id,
@ -40,6 +49,7 @@ public class PaperDto {
@JsonProperty("comment") String comment,
@JsonProperty("locked") Boolean locked,
@JsonProperty("tmpFileName") String tmpFileName,
@JsonProperty("authorIds") Set<Integer> authorIds,
@JsonProperty("authors") Set<UserDto> authors) {
this.id = id;
this.title = title;
@ -69,6 +79,7 @@ public class PaperDto {
this.fileId = paper.getFileData() == null ? null : paper.getFileData().getId();
this.fileName = paper.getFileData() == null ? null : paper.getFileData().getName();
this.fileCreateDate = paper.getFileData() == null ? null : paper.getFileData().getCreateDate();
this.authorIds = convert(paper.getAuthors(), user -> user.getId());
this.authors = convert(paper.getAuthors(), UserDto::new);
}
@ -76,58 +87,126 @@ public class PaperDto {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Paper.PaperStatus getStatus() {
return status;
}
public void setStatus(Paper.PaperStatus status) {
this.status = status;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
public List<DeadlineDto> getDeadlines() {
return deadlines;
}
public void setDeadlines(List<DeadlineDto> deadlines) {
this.deadlines = deadlines;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Boolean getLocked() {
return locked;
}
public void setLocked(Boolean locked) {
this.locked = locked;
}
public String getTmpFileName() {
return tmpFileName;
}
public void setTmpFileName(String tmpFileName) {
this.tmpFileName = tmpFileName;
}
public Integer getFileId() {
return fileId;
}
public void setFileId(Integer fileId) {
this.fileId = fileId;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Date getFileCreateDate() {
return fileCreateDate;
}
public void setFileCreateDate(Date fileCreateDate) {
this.fileCreateDate = fileCreateDate;
}
public Set<UserDto> getAuthors() {
return authors;
}
public void setAuthors(Set<UserDto> authors) {
this.authors = authors;
}
public Set<Integer> getAuthorIds() {
return authorIds;
}
public void setAuthorIds(Set<Integer> authorIds) {
this.authorIds = authorIds;
}
public String getAuthorsString() {
return authors
.stream()
.map(author -> author.getLastName() + author.getFirstName())
.map(author -> author.getLastName())
.collect(Collectors.joining(", "));
}
public Integer getFilterAuthorId() {
return filterAuthorId;
}
public void setFilterAuthorId(Integer filterAuthorId) {
this.filterAuthorId = filterAuthorId;
}
}

View File

@ -1,24 +1,42 @@
package ru.ulstu.paper.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class PaperFilterDto {
private final Integer authorId;
private final Integer year;
private List<PaperDto> papers;
private Integer filterAuthorId;
private Integer year;
@JsonCreator
public PaperFilterDto(@JsonProperty("authorId") Integer authorId,
@JsonProperty("year") Integer year) {
this.authorId = authorId;
public PaperFilterDto() {
}
public PaperFilterDto(List<PaperDto> paperDtos, Integer filterAuthorId, Integer year) {
this.papers = paperDtos;
this.filterAuthorId = filterAuthorId;
this.year = year;
}
public Integer getAuthorId() {
return authorId;
public List<PaperDto> getPapers() {
return papers;
}
public void setPapers(List<PaperDto> papers) {
this.papers = papers;
}
public Integer getFilterAuthorId() {
return filterAuthorId;
}
public void setFilterAuthorId(Integer filterAuthorId) {
this.filterAuthorId = filterAuthorId;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
}

View File

@ -1,19 +0,0 @@
package ru.ulstu.paper.model;
public class PaperStatusDto {
private final String id;
private final String name;
public PaperStatusDto(Paper.PaperStatus status) {
this.id = status.name();
this.name = status.getName();
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}

View File

@ -10,6 +10,6 @@ import java.util.List;
public interface PaperRepository extends JpaRepository<Paper, Integer> {
@Query("SELECT p FROM Paper p WHERE (:author IS NULL OR :author MEMBER OF p.authors) AND YEAR(p.createDate) = :year OR :year IS NULL")
@Query("SELECT p FROM Paper p WHERE (:author IS NULL OR :author MEMBER OF p.authors) AND (YEAR(p.createDate) = :year OR :year IS NULL)")
List<Paper> filter(@Param("author") User author, @Param("year") Integer year);
}

View File

@ -7,7 +7,7 @@ import org.springframework.stereotype.Service;
@Service
public class PaperScheduler {
private final static boolean IS_DEADLINE_NOTIFICATION_BEFORE_WEEK = true;
private final static boolean IS_DEADLINE_NOTIFICATION_AFTER_WEEK = true;
private final Logger log = LoggerFactory.getLogger(PaperScheduler.class);
@ -24,14 +24,14 @@ public class PaperScheduler {
@Scheduled(cron = "0 0 8 * 1 ?")
public void checkDeadlineBeforeWeek() {
log.debug("PaperScheduler.checkDeadlineBeforeWeek started");
paperNotificationService.sendDeadlineNotifications(paperService.findAll(), !IS_DEADLINE_NOTIFICATION_BEFORE_WEEK);
paperNotificationService.sendDeadlineNotifications(paperService.findAll(), !IS_DEADLINE_NOTIFICATION_AFTER_WEEK);
log.debug("PaperScheduler.checkDeadlineBeforeWeek finished");
}
@Scheduled(cron = "0 0 8 * * ?")
public void checkDeadlineAfterWeek() {
log.debug("PaperScheduler.checkDeadlineAfterWeek started");
paperNotificationService.sendDeadlineNotifications(paperService.findAll(), IS_DEADLINE_NOTIFICATION_BEFORE_WEEK);
paperNotificationService.sendDeadlineNotifications(paperService.findAll(), IS_DEADLINE_NOTIFICATION_AFTER_WEEK);
log.debug("PaperScheduler.checkDeadlineAfterWeek finished");
}

View File

@ -8,7 +8,6 @@ import ru.ulstu.file.service.FileService;
import ru.ulstu.paper.model.Paper;
import ru.ulstu.paper.model.PaperDto;
import ru.ulstu.paper.model.PaperFilterDto;
import ru.ulstu.paper.model.PaperStatusDto;
import ru.ulstu.paper.repository.PaperRepository;
import ru.ulstu.user.model.User;
import ru.ulstu.user.service.UserService;
@ -16,9 +15,12 @@ import ru.ulstu.user.service.UserService;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.springframework.util.ObjectUtils.isEmpty;
import static ru.ulstu.core.util.StreamApiUtils.convert;
import static ru.ulstu.paper.model.Paper.PaperStatus.ATTENTION;
import static ru.ulstu.paper.model.Paper.PaperStatus.DRAFT;
@ -60,6 +62,10 @@ public class PaperService {
return convert(findAll(), PaperDto::new);
}
public PaperDto findOneDto(Integer id) {
return new PaperDto(paperRepository.findOne(id));
}
@Transactional
public Integer create(PaperDto paperDto) throws IOException {
Paper newPaper = copyFromDto(new Paper(), paperDto);
@ -79,8 +85,9 @@ public class PaperService {
if (paperDto.getTmpFileName() != null) {
paper.setFileData(fileService.createFileFromTmp(paperDto.getTmpFileName()));
}
if (paperDto.getAuthors() != null && !paperDto.getAuthors().isEmpty()) {
paperDto.getAuthors().forEach(authorIds -> paper.getAuthors().add(userService.findById(authorIds.getId())));
paper.getAuthors().clear();
if (paperDto.getAuthorIds() != null && !paperDto.getAuthorIds().isEmpty()) {
paperDto.getAuthorIds().forEach(authorIds -> paper.getAuthors().add(userService.findById(authorIds)));
}
return paper;
}
@ -89,11 +96,18 @@ public class PaperService {
public Integer update(PaperDto paperDto) throws IOException {
Paper paper = paperRepository.findOne(paperDto.getId());
Paper.PaperStatus oldStatus = paper.getStatus();
Set<User> oldAuthors = new HashSet<>(paper.getAuthors());
if (paperDto.getTmpFileName() != null && paper.getFileData() != null) {
fileService.deleteFile(paper.getFileData());
}
paperRepository.save(copyFromDto(paper, paperDto));
paper.getAuthors().forEach(author -> {
if (!oldAuthors.contains(author)) {
paperNotificationService.sendCreateNotification(paper);
}
});
if (paper.getStatus() != oldStatus) {
paperNotificationService.statusChangeNotification(paper, oldStatus);
}
@ -110,8 +124,8 @@ public class PaperService {
paperRepository.delete(paper);
}
public List<PaperStatusDto> getPaperStatuses() {
return convert(Arrays.asList(Paper.PaperStatus.values()), PaperStatusDto::new);
public List<Paper.PaperStatus> getPaperStatuses() {
return Arrays.asList(Paper.PaperStatus.values());
}
@Transactional
@ -131,10 +145,12 @@ public class PaperService {
}
public List<PaperDto> filter(PaperFilterDto filterDto) {
return convert(paperRepository.filter(userService.findById(filterDto.getAuthorId()), filterDto.getYear()), PaperDto::new);
return convert(paperRepository.filter(
filterDto.getFilterAuthorId() == null ? null : userService.findById(filterDto.getFilterAuthorId()),
filterDto.getYear()), PaperDto::new);
}
public PaperDto findPaper(int id){
public PaperDto findPaper(int id) {
return new PaperDto(paperRepository.getOne(id));
}
@ -154,4 +170,20 @@ public class PaperService {
paperNotificationService.sendFailedNotification(paper, oldStatus);
});
}
public void save(PaperDto paperDto) throws IOException {
if (isEmpty(paperDto.getId())) {
create(paperDto);
} else {
update(paperDto);
}
}
public PaperDto findById(Integer paperId) {
return new PaperDto(paperRepository.findOne(paperId));
}
public List<User> getPaperAuthors() {
return userService.findAll();
}
}

View File

@ -23,4 +23,10 @@
constraintName="fk_event_child_event" referencedTableName="event"
referencedColumnNames="id"/>
</changeSet>
<changeSet author="orion" id="20181126_000000-4">
<modifyDataType
columnName="deadline_date"
newDataType="date"
tableName="paper"/>
</changeSet>
</databaseChangeLog>

View File

@ -11,7 +11,7 @@
</p>
<p>
Статус статьи "<span th:text="${paper.title}">Title</span>" сменился с
"<span th:text="${oldStatus.name}">oldStatus</span>" на "<span th:text="${paper.status.name}">newStatus</span>".
"<span th:text="${oldStatus.statusName}">oldStatus</span>" на "<span th:text="${paper.status.statusName}">newStatus</span>".
</p>
<p>
Regards,

View File

@ -50,7 +50,7 @@ section {
}
section h2.section-heading {
font-size: 40px;
font-size: 3.5vw;
margin-top: 0;
margin-bottom: 15px;
}

View File

@ -1,144 +1,42 @@
var urlPapers = "/api/1.0/papers";
var urlPaperStatuses = "/api/1.0/papers/statuses";
var urlDeletePaper = "/api/1.0/papers/";
var urlFilterPaper = "/api/1.0/papers/filter"
/*<![CDATA[*/
$(document).ready(function () {
$(".paper-row").mouseenter(function (event) {
var paperRow = $(event.target).closest(".paper-row");
$(paperRow).css("background-color", "#f8f9fa");
$(paperRow).find(".remove-paper").removeClass("d-none");
function showPapers(papersElement, paperRowClass) {
getFromRest(urlPapers, function (paperList) {
paperList.forEach(function (paper, index) {
$(papersElement).parent().append("<div class='row text-left paper-row'>" +
" <div class='col'>" +
" <span class='fa-stack fa-1x'>\n" +
" <i class='fa fa-circle fa-stack-2x " + getPaperStatusClass(paper.status) + "'></i>" +
" <i class='fa fa-file-text-o fa-stack-1x fa-inverse'></i>" +
" </span>" +
" <a href='paper?id=" + paper.id + "" +
"'><span>" + (index + 1) + ". " + paper.title + "</span></a>" +
"<span class='remove-paper d-none pull-right' onclick=\"deletePaper(" + paper.id + ",'" + papersElement + "', '" + paperRowClass + "')\">" +
"<i class=\"fa fa-trash\" aria-hidden=\"true\"></i></span>" +
"</div></div>");
});
$(paperRowClass).mouseenter(function (event) {
var paperRow = $(event.target).closest(paperRowClass);
$(paperRow).css("background-color", "#f8f9fa");
$(paperRow).find(".remove-paper").removeClass("d-none");
});
$(paperRowClass).mouseleave(function (event) {
var paperRow = $(event.target).closest(paperRowClass);
$(paperRow).css("background-color", "white");
$(paperRow).closest(paperRowClass).find(".remove-paper").addClass("d-none");
});
});
}
function filterPapers(papersElement, paperRowClass, authorId, year) {
var paperData = JSON.stringify({
"authorId": authorId,
"year": year
});
postToRest(urlFilterPaper, paperData, function (data) {
$(paperRowClass).remove();
if(data.length > 0){
data.forEach(function (paper, index) {
$(papersElement).parent().append("<div class='row text-left paper-row'>" +
" <div class='col-md-11'>" +
" <span class='fa-stack fa-1x'>\n" +
" <i class='fa fa-circle fa-stack-2x " + getPaperStatusClass(paper.status) + "'></i>" +
" <i class='fa fa-file-text-o fa-stack-1x fa-inverse'></i>" +
" </span>" +
" <a href='paper?id=" + paper.id + "" +
"'><span>" + paper.title + "</span></a></div>" +
"<div class='col-md-1'>" +
"<span class='remove-paper d-none' onclick=\"deletePaper(" + paper.id + ",'" + papersElement + "', '" + paperRowClass + "')\">" +
"<i class=\"fa fa-trash\" aria-hidden=\"true\"></i></span>" +
" </div></div>");
});
$(paperRowClass).mouseenter(function (event) {
var paperRow = $(event.target).closest(paperRowClass);
$(paperRow).css("background-color", "#f8f9fa");
$(paperRow).find(".remove-paper").removeClass("d-none");
});
$(paperRowClass).mouseleave(function (event) {
var paperRow = $(event.target).closest(paperRowClass);
$(paperRow).css("background-color", "white");
$(paperRow).closest(paperRowClass).find(".remove-paper").addClass("d-none");
});
}
$(".paper-row").mouseleave(function (event) {
var paperRow = $(event.target).closest(".paper-row");
$(paperRow).css("background-color", "white");
$(paperRow).closest(".paper-row").find(".remove-paper").addClass("d-none");
});
$('a[data-confirm]').click(function(ev) {
var href = $(this).attr('href');
if (!$('#dataConfirmModal').length) {
$('#modalDelete').append('<div class="modal fade" id="dataConfirmModal" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true"\n' +
' >\n' +
' <div class="modal-dialog modal-sm">\n' +
' <div class="modal-content">\n' +
' <div class="modal-header">\n' +
' <h8 class="modal-title" id="myModalLabel">Удалить статью?</h8>\n' +
' <button type="button" class="close" data-dismiss="modal" aria-label="Закрыть"><span\n' +
' aria-hidden="true">&times;</span></button>\n' +
' </div>\n' +
}
function deletePaper(id, papersElement, paperRowClass) {
$("#remove-paper-modal").modal('show');
$("#modal-btn-yes").on("click", function () {
deleteFromRest(urlDeletePaper + id, function () {
showFeedbackMessage("Статья удалена");
$(paperRowClass).remove();
showPapers(papersElement, paperRowClass);
});
$("#remove-paper-modal").modal('hide');
' <div class="modal-footer">\n' +
' <a class="btn btn-primary" id="dataConfirmOK">Да</a>'+
' <button class="btn primary" data-dismiss="modal" aria-hidden="true">Нет</button>'+
' </div>\n' +
' </div>\n' +
' </div>\n' +
' </div>');
}
$('#dataConfirmModal').find('#myModalLabel').text($(this).attr('data-confirm'));
$('#dataConfirmOK').attr('href', href);
$('#dataConfirmModal').modal({show:true});
return false;
});
$("#modal-btn-no").on("click", function () {
$("#remove-paper-modal").modal('hide');
});
}
function addPaper(title, status, datePublish, dateUpdate, deadline, comment, locked, tmpFileName, authors) {
var paperData = JSON.stringify({
"title": title,
"status": status,
"deadlineDate":deadline,
"comment": comment
});
postToRest(urlPapers, paperData, function (data) {
alert(data);
});
}
function getPaperStatusClass(status) {
switch (status) {
case 'DRAFT':
return "text-draft"
case 'ON_PREPARATION':
return "text-primary";
case 'ON_REVIEW':
return "text-primary";
case 'COMPLETED':
return "text-success";
case 'ATTENTION':
return "text-warning";
case 'FAILED':
return "text-failed";
default:
return "";
}
}
function showPaperDashboard(dashboardElement) {
getFromRest(urlPapers, function (paperList) {
paperList.forEach(function (paper, index) {
$(dashboardElement).append("<div class=\"col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3 dashboard-card\">" +
"<div class=\"row\">" +
"<div class=\"col-2\">" +
"<span class=\"fa-stack fa-1x\">" +
"<i class=\"fa fa-circle fa-stack-2x " + getPaperStatusClass(paper.status) + "\"></i>" +
"<i class=\"fa fa-file-text-o fa-stack-1x fa-inverse\"></i>" +
"</span>" +
"</div>" +
"<div class=\"col col-10 text-right\">" +
"<h7 class=\"service-heading\">" + paper.title + "</h7>" +
"<p class=\"text-muted\">" + paper.authorsString + "</p>" +
"</div>" +
"</div>" +
"</div>");
});
});
}
});
/*]]>*/

View File

@ -12,6 +12,7 @@
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="/webjars/bootstrap/4.1.0/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/webjars/bootstrap-select/1.13.3/css/bootstrap-select.min.css"/>
<link rel="stylesheet" href="/webjars/font-awesome/4.7.0/css/font-awesome.min.css"/>
<!-- Custom fonts for this template -->
@ -25,6 +26,7 @@
<!-- Bootstrap core JavaScript -->
<script src="/webjars/jquery/3.3.1-1/jquery.min.js"></script>
<script src="/webjars/bootstrap/4.1.0/js/bootstrap.bundle.min.js"></script>
<script src="/webjars/bootstrap-select/1.13.3/js/bootstrap-select.min.js"></script>
<!-- Plugin JavaScript -->
<script src="/webjars/jquery.easing/1.4.1/jquery.easing.js"></script>
@ -88,8 +90,6 @@
</div>
</footer>
<!-- Custom scripts for this template -->
<script src="/js/agency.js"></script>
<script src="/js/core.js"></script>
<!-- Yandex.Metrika counter -->
<script type="text/javascript" >

View File

@ -175,7 +175,6 @@
</div>
</section>
<script type="text/javascript" src="/js/file-loader.js"></script>
<script src="/js/papers.js"></script>
<script>
/*<![CDATA[*/
$(document).ready(function () {

View File

@ -1,45 +1,25 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="default">
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<div class="container" layout:fragment="content">
<!-- Services -->
<section id="services">
<div class="container">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Статьи</h2>
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./papers" class="btn btn-light toolbar-button"><i class="fa fa-list-alt"></i>
Список</a>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./dashboard" class="btn btn-light toolbar-button"><i class="fa fa-newspaper-o"
aria-hidden="true"></i> Панель управления</a>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./paper" class="btn btn-light toolbar-button"><i class="fa fa-plus-circle" aria-hidden="true"></i>
Добавить статью</a>
</div>
</div>
<div th:replace="papers/fragments/paperNavigationFragment"/>
</div>
<div class="row justify-content-center" id="dashboard">
<th:block th:each="paper : ${papers}">
<div th:replace="papers/fragments/paperDashboardFragment :: titleLine(paper=${paper})"/>
</th:block>
</div>
</div>
</section>
<script src="/js/papers.js"></script>
<script>
$(document).ready(function () {
showPaperDashboard("#dashboard");
});
</script>
</div>
</body>
</html>

View File

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerfiles">
<meta charset="UTF-8"/>
</head>
<body>
<div th:fragment="titleLine (paper)" class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3 dashboard-card">
<div class="row">
<div class="col-2">
<span class="fa-stack fa-1x">
<th:block th:switch="${paper.status.name()}">
<div th:case="'ATTENTION'">
<i class="fa fa-circle fa-stack-2x text-warning"></i>
</div>
<div th:case="'DRAFT'">
<i class="fa fa-circle fa-stack-2x text-draft"></i>
</div>
<div th:case="'ON_PREPARATION'">
<i class="fa fa-circle fa-stack-2x text-primary"></i>
</div>
<div th:case="'ON_REVIEW'">
<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>
</div>
<div class="col col-10 text-right">
<h7 class="service-heading" th:text="${paper.title}"> title</h7>
<p class="text-muted" th:text="${paper.authorsString}">authors</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerfiles">
<meta charset="UTF-8"/>
</head>
<body>
<div th:fragment="titleLine (paper)" class="row text-left paper-row" style="background-color: white;">
<div class="col">
<span class="fa-stack fa-1x">
<th:block th:switch="${paper.status.name()}">
<div th:case="'ATTENTION'">
<i class="fa fa-circle fa-stack-2x text-warning"></i>
</div>
<div th:case="'DRAFT'">
<i class="fa fa-circle fa-stack-2x text-draft"></i>
</div>
<div th:case="'ON_PREPARATION'">
<i class="fa fa-circle fa-stack-2x text-primary"></i>
</div>
<div th:case="'ON_REVIEW'">
<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>
<a th:href="@{'paper?id='+${paper.id}}">
<span class="h6" th:text="${paper.title}"/>
<span class="text-muted" th:text="${paper.authorsString}"/>
</a>
<input class="id-class" type="hidden" th:value="${paper.id}"/>
<a class="remove-paper pull-right d-none" th:href="@{'/papers/delete/'+${paper.id}}"
data-confirm="Удалить статью?">
<i class="fa fa-trash" aria-hidden="true"></i>
</a>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerfiles">
<meta charset="UTF-8"/>
</head>
<body>
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./papers" class="btn btn-light toolbar-button"><i class="fa fa-list-alt"></i>
Список</a>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./dashboard" class="btn btn-light toolbar-button"><i class="fa fa-newspaper-o"
aria-hidden="true"></i> Панель
управления</a>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./paper?id=0" class="btn btn-light toolbar-button"><i class="fa fa-plus-circle"
aria-hidden="true"></i>
Добавить статью</a>
</div>
</div>
</body>
</html>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="default">
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html">
<head>
</head>
@ -14,127 +14,100 @@
<div class="row">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Редактирование статьи</h2>
</div>
</div>
<div class="row">
<div class="col-lg-10 text-center">
</div>
<div class="col-lg-2 text-center">
<div th:replace="papers/fragments/paperNavigationFragment"/>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<form id="contactForm" name="sentMessage" novalidate="">
<form id="paper-form" method="post" th:action="@{'/papers/paper?id='+ *{id == null ? '' : id} + ''}"
th:object="${paperDto}">
<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="name">Название:</label>
<input class="form-control" id="name" type="text" placeholder="Название статьи"
required="" data-validation-required-message="Введите название статьи"/>
<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" id="status">
<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="comment">Комментарий:</label>
<textarea class="form-control" rows="5" id="comment"></textarea>
<textarea class="form-control" rows="5" id="comment"
th:field="*{comment}"></textarea>
</div>
<div class="form-group">
<label>Дедлайн:</label>
<input type="date" class="form-control" name="deadline" id="deadline"/>
<label>Дедлайны:</label>
<div class="row" th:each="deadline, rowStat : *{deadlines}">
<input type="hidden" th:field="*{deadlines[__${rowStat.index}__].id}"/>
<div class="col-6">
<input type="date" class="form-control" name="deadline"
th:field="*{deadlines[__${rowStat.index}__].date}"/>
</div>
<div class="col-4">
<input class="form-control" type="text" placeholder="Описание"
th:field="*{deadlines[__${rowStat.index}__].description}"/>
</div>
<div class="col-2">
<a class="btn btn-danger float-right"
th:onclick="|$('#deadlines${rowStat.index}\\.description').val('');
$('#deadlines${rowStat.index}\\.date').val('');
$('#addDeadline').click();|"><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-check">
<input type="checkbox" class="form-check-input" id="locked"/>
<input type="checkbox" class="form-check-input" id="locked"
th:field="*{locked}"/>
<label class="form-check-label" for="locked">Заблокирована</label>
</div>
<div class="form-group">
<label for="loader" id="loader">Загрузить статью:</label>
<!--<div id="loader">
<label for="loader">Загрузить статью:</label>
<div id="loader">
</div>-->
</div>
</div>
<div class="form-group">
<label>Дата публикации:</label>
<input type="date" class="form-control" name="date-publish" id="date-publish"/>
<label>Дата создания:</label>
<span th:text="${paperDto.createDate == null ? '' : #dates.format(paperDto.createDate, 'dd.MM.yyyy HH:mm')}">text</span>
</div>
<div class="form-group">
<label>Последнее изменение:</label>
<input type="date" class="form-control" name="date-update" id="date-update"/>
<span th:text="${paperDto.updateDate == null ? '' : #dates.format(paperDto.updateDate, 'dd.MM.yyyy HH:mm')}">text</span>
</div>
<p><a href="#myModal1" class="btn btn-primary" data-toggle="modal">Редактировать авторов
статьи</a></p>
<div id="myModal1" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">×
</button>
<h4 class="modal-title">Авторы статьи</h4>
</div>
<div class="modal-body">
<table class="table">
<thead>
<tr>
<th>Фамилия</th>
<th>Имя</th>
<th>Отчество</th>
</tr>
</thead>
<tbody>
<tr>
<td>Иванов</td>
<td>Иван</td>
<td>Иванович</td>
<td>
<span class="table-remove"><button type="button"
class="btn btn-danger btn-rounded btn-sm my-0">Remove</button></span>
</td>
</tr>
<tr>
<td>text</td>
<td>text</td>
<td>text</td>
<td>
<span class="table-remove"><button type="button"
class="btn btn-danger btn-rounded btn-sm my-0">Remove</button></span>
</td>
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle"
type="button" data-toggle="dropdown">Выберите автора
<span class="caret"></span></button>
<ul class="dropdown-menu">
<input class="form-control" id="myInput" type="text"
placeholder="Search.."/>
<li><a href="#">Иванов</a></li>
<li><a href="#">Смирнов</a></li>
<li><a href="#">Кузнецов</a></li>
</ul>
</div>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary">Сохранить изменения
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">
Отмена
</button>
</div>
</div>
</div>
<div class="form-group">
<label>Редактировать авторов статьи:</label>
<select class="selectpicker form-control" multiple="true" data-live-search="true"
title="-- Выберите авторов --"
th:field="*{authorIds}">
<option th:each="author: ${allAuthors}" th:value="${author.id}"
th:text="${author.lastName}">Status
</option>
</select>
<p th:if="${#fields.hasErrors('authorIds')}" th:errors="*{authorIds}"
class="alert alert-danger">Incorrect title</p>
</div>
</div>
<div class="col-md-3 offset-md-3 col-sm-12 offset-sm-0">
@ -148,14 +121,15 @@
<div class="clearfix"></div>
<div class="col-lg-12">
<div class="form-group">
<button id="sendMessageButton" class="btn btn-success text-uppercase"
type="button" onclick="savePaper();" >
<button id="sendMessageButton" name="save" class="btn btn-success text-uppercase"
type="submit">
Сохранить
</button>
<button id="cancelButton" class="btn btn-default text-uppercase" type="button">
<a id="cancelButton" class="btn btn-default text-uppercase" href="/papers/papers">
Отмена
</button>
</a>
</div>
</div>
</div>
</form>
@ -164,7 +138,6 @@
</div>
</section>
<script type="text/javascript" src="/js/file-loader.js"></script>
<script src="/js/papers.js"></script>
<script>
/*<![CDATA[*/
$(document).ready(function () {
@ -178,25 +151,8 @@
console.debug(response);
}
});
getFromRest(urlPaperStatuses, function (response) {
fillSelect($("#status"), response);
});
$('.selectpicker').selectpicker();
});
function savePaper(){
var title = document.getElementById("name").value;
var status = document.getElementById("status").value;
var datePublish = document.getElementById("date-publish").value;
var dateUpdate = document.getElementById("date-update").value;
var deadline = document.getElementById("deadline").value;
var comment = document.getElementById("comment").value;
var locked = document.getElementById("locked").value;
var tmpFileName = document.getElementById("loader").value;
var authors = document.getElementById("myInput").value;
addPaper(title,status,datePublish,dateUpdate,deadline,comment,locked,tmpFileName,authors);
}
/*]]>*/
</script>
</div>

View File

@ -1,82 +1,58 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="default">
layout:decorator="default" xmlns:th="">
<head>
</head>
<body>
<div class="container" layout:fragment="content">
<section id="papers">
<div class="container">
<div class="row" id="paper-list">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Статьи</h2>
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./papers" class="btn btn-light toolbar-button"><i class="fa fa-list-alt"></i>
Список</a>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./dashboard" class="btn btn-light toolbar-button"><i class="fa fa-newspaper-o"
aria-hidden="true"></i> Панель управления</a>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
<a href="./paper" class="btn btn-light toolbar-button"><i class="fa fa-plus-circle" aria-hidden="true"></i>
Добавить статью</a>
</div>
<form id="papers-form" method="post" th:action="@{'/papers/papers'}">
<input th:type="hidden" name="paperDeleteId" id="paperDeleteId"/>
<section id="papers">
<div class="container">
<div class="row" id="paper-list">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Статьи</h2>
<div th:replace="papers/fragments/paperNavigationFragment"/>
</div>
<div class = "filter">
<h5>Фильтровать по:</h5>
<select id = "author">
<option value = "1">Алёна</option>
<option value = "2">Маша</option>
<option value = "3">Петя</option>
</select>
<select id = "index score">
<option >РИНЦ</option>
<option>Scopus</option>
<option>Web of science</option>
</select>
<select id = "year">
<option>2018</option>
<option>2017</option>
<option>2016</option>
</select>
</div>
<div class="row">
<div class="col-md-9 col-sm-12">
<th:block th:each="paper : ${filteredPapers.papers}">
<div th:replace="papers/fragments/paperLineFragment :: titleLine(paper=${paper})"/>
</th:block>
</div>
<div class="col-md-3 col-sm-12">
<div class="filter">
<h5>Фильтр:</h5>
<select class="form-control" th:field="${filteredPapers.filterAuthorId}" id="author"
onchange="this.form.submit();">
<option value="">Все авторы </option>
<option th:each="author: ${allAuthors}" th:value="${author.id}"
th:text="${author.lastName}">lastName
</option>
</select>
<select class="form-control" id="year" th:field="${filteredPapers.year}" 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>
</section>
</section>
<div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true" id="remove-paper-modal">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h8 class="modal-title" id="myModalLabel">Удалить статью?</h8>
<button type="button" class="close" data-dismiss="modal" aria-label="Закрыть"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" id="modal-btn-yes">Да</button>
<button type="button" class="btn btn-primary" id="modal-btn-no">Нет</button>
</div>
</div>
</div>
</div>
<div id="modalDelete"/>
</form>
<script src="/js/papers.js"></script>
<script>
$(document).ready(function () {
showPapers("#paper-list", ".paper-row");
jQuery('.filter').on('change','#year',function(){
filterPapers("#paper-list", ".paper-row",'1','2018');
});
});
<script type="text/javascript">
</script>
</div>
</body>