diff --git a/.gitlab/issue_templates/feature.md b/.gitlab/issue_templates/feature.md index 932d386..bb1e9f3 100644 --- a/.gitlab/issue_templates/feature.md +++ b/.gitlab/issue_templates/feature.md @@ -1,31 +1,29 @@ ## Краткое описание задачи -``` + Что требуется сделать -``` + ## `Опционально` Список верстаемых страниц -``` + Будут затронуты страницы: * page1.html * page2.html * page3.html -``` ## `Опционально` Список затрагиваемых модулей -``` + При реализации задачи потребуется также реализовать методы контроллера -``` + ## `Опционально` Список реализуемых функций -``` + После выполнения задачи станет доступным: * просмотр `entity_name` * редактирование `entity_name` * валидация `entity_name` -``` ## `Опционально` Сценарии работы -``` + Сценарий просмотра: 1. Зайти на главную страницу приложения 2. Перейти в раздел `section_name` @@ -38,13 +36,11 @@ 3. Перейти к списку `entity_name` 4. Выбрать нужную `entity_name` и нажать на нее 5. Внести нужные правки в поля и сохранить -``` ## Описание конечного результата, дающего возможность проверки выполнения задачи: компоненты проекта, сценарии работы -``` + * Сверстаны страницы page1.hml, page2.hml, page3.hml * Реализован контроллер для обслуживания страниц * Сохранение в БД еще не реализовано * Валидация происходит по полям `field1, field2` * Сценарий просмотра проверяется при ручном внечении записей в БД -``` \ No newline at end of file diff --git a/src/main/java/ru/ulstu/conference/controller/ConferenceController.java b/src/main/java/ru/ulstu/conference/controller/ConferenceController.java index 7a229ca..823f853 100644 --- a/src/main/java/ru/ulstu/conference/controller/ConferenceController.java +++ b/src/main/java/ru/ulstu/conference/controller/ConferenceController.java @@ -5,6 +5,7 @@ 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.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -12,10 +13,13 @@ import ru.ulstu.conference.model.ConferenceDto; import ru.ulstu.conference.model.ConferenceFilterDto; import ru.ulstu.conference.service.ConferenceService; import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.paper.model.Paper; import springfox.documentation.annotations.ApiIgnore; import javax.validation.Valid; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.stream.Collectors; import static org.springframework.util.StringUtils.isEmpty; @@ -43,9 +47,13 @@ public class ConferenceController { @GetMapping("/conference") public void getConference(ModelMap modelMap, @RequestParam(value = "id") Integer id) { if (id != null && id > 0) { - modelMap.put("conferenceDto", conferenceService.findOneDto(id)); + ConferenceDto conferenceDto = conferenceService.findOneDto(id); + conferenceDto.setNotSelectedPapers(getNotSelectPapers(conferenceDto.getPaperIds())); + modelMap.put("conferenceDto", conferenceDto); } else { - modelMap.put("conferenceDto", new ConferenceDto()); + ConferenceDto conferenceDto = new ConferenceDto(); + conferenceDto.setNotSelectedPapers(getNotSelectPapers(new ArrayList())); + modelMap.put("conferenceDto", conferenceDto); } } @@ -60,6 +68,12 @@ public class ConferenceController { } + @GetMapping("/delete/{conference-id}") + public String delete(@PathVariable("conference-id") Integer conferenceId) throws IOException { + conferenceService.delete(conferenceId); + return String.format(REDIRECT_TO, CONFERENCES_PAGE); + } + @PostMapping(value = "/conference", params = "addDeadline") public String addDeadline(@Valid ConferenceDto conferenceDto, Errors errors) { filterEmptyDeadlines(conferenceDto); @@ -80,6 +94,20 @@ public class ConferenceController { return CONFERENCE_PAGE; } + @PostMapping(value = "/conference", params = "removePaper") + public String removePaper(@Valid ConferenceDto conferenceDto, Errors errors, + @RequestParam(value = "removePaper") Integer paperIndex) throws IOException { + if (errors.hasErrors()) { + return CONFERENCE_PAGE; + } + conferenceService.removePaper(conferenceDto, paperIndex); + return CONFERENCE_PAGE; + } + + public List getNotSelectPapers(List paperIds) { + return conferenceService.getConferencePapers(paperIds); + } + private void filterEmptyDeadlines(ConferenceDto conferenceDto) { conferenceDto.setDeadlines(conferenceDto.getDeadlines().stream() .filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription())) diff --git a/src/main/java/ru/ulstu/conference/model/Conference.java b/src/main/java/ru/ulstu/conference/model/Conference.java index faed15c..b6affcd 100644 --- a/src/main/java/ru/ulstu/conference/model/Conference.java +++ b/src/main/java/ru/ulstu/conference/model/Conference.java @@ -60,7 +60,7 @@ public class Conference extends BaseEntity { @JoinTable(name = "paper_conference", joinColumns = {@JoinColumn(name = "conference_id")}, inverseJoinColumns = {@JoinColumn(name = "paper_id")}) - private Set papers = new HashSet<>(); + private List papers = new ArrayList<>(); @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "users_conference", @@ -124,11 +124,11 @@ public class Conference extends BaseEntity { this.deadlines = deadlines; } - public Set getPapers() { + public List getPapers() { return papers; } - public void setPapers(Set papers) { + public void setPapers(List papers) { this.papers = papers; } diff --git a/src/main/java/ru/ulstu/conference/model/ConferenceDto.java b/src/main/java/ru/ulstu/conference/model/ConferenceDto.java index e5efcde..ec9ec92 100644 --- a/src/main/java/ru/ulstu/conference/model/ConferenceDto.java +++ b/src/main/java/ru/ulstu/conference/model/ConferenceDto.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.hibernate.validator.constraints.NotEmpty; import org.springframework.format.annotation.DateTimeFormat; import ru.ulstu.deadline.model.Deadline; -import ru.ulstu.paper.model.PaperDto; +import ru.ulstu.paper.model.Paper; import ru.ulstu.user.model.UserDto; import javax.persistence.Temporal; @@ -39,8 +39,11 @@ public class ConferenceDto { private List deadlines = new ArrayList<>(); private List removedDeadlineIds = new ArrayList<>(); private Set userIds = new HashSet<>(); - private Set paperIds = new HashSet<>(); - private Set papers = new HashSet<>(); + private List paperIds = new ArrayList<>(); + private List papers = new ArrayList<>(); + + + private List notSelectedPapers = new ArrayList<>(); private Set users = new HashSet<>(); private Integer filterUserId; @@ -57,9 +60,10 @@ public class ConferenceDto { @JsonProperty("endDate") Date endDate, @JsonProperty("deadlines") List deadlines, @JsonProperty("userIds") Set userIds, - @JsonProperty("paperIds") Set paperIds, + @JsonProperty("paperIds") List paperIds, @JsonProperty("users") Set users, - @JsonProperty("papers") Set papers) { + @JsonProperty("papers") List papers, + @JsonProperty("notSelectedPapers") List notSelectedPapers) { this.id = id; this.title = title; this.description = description; @@ -72,6 +76,7 @@ public class ConferenceDto { this.paperIds = paperIds; this.users = users; this.papers = papers; + this.notSelectedPapers = notSelectedPapers; } public ConferenceDto(Conference conference) { @@ -86,7 +91,7 @@ public class ConferenceDto { this.userIds = convert(conference.getUsers(), user -> user.getId()); this.paperIds = convert(conference.getPapers(), paper -> paper.getId()); this.users = convert(conference.getUsers(), UserDto::new); - this.papers = convert(conference.getPapers(), PaperDto::new); + this.papers = conference.getPapers(); } @@ -162,19 +167,19 @@ public class ConferenceDto { this.userIds = userIds; } - public Set getPaperIds() { + public List getPaperIds() { return paperIds; } - public void setPaperIds(Set paperIds) { + public void setPaperIds(List paperIds) { this.paperIds = paperIds; } - public Set getPapers() { + public List getPapers() { return papers; } - public void setPapers(Set papers) { + public void setPapers(List papers) { this.papers = papers; } @@ -202,4 +207,12 @@ public class ConferenceDto { this.removedDeadlineIds = removedDeadlineIds; } + public List getNotSelectedPapers() { + return notSelectedPapers; + } + + public void setNotSelectedPapers(List notSelectedPapers) { + this.notSelectedPapers = notSelectedPapers; + } + } diff --git a/src/main/java/ru/ulstu/conference/service/ConferenceService.java b/src/main/java/ru/ulstu/conference/service/ConferenceService.java index 9c40295..4d267d1 100644 --- a/src/main/java/ru/ulstu/conference/service/ConferenceService.java +++ b/src/main/java/ru/ulstu/conference/service/ConferenceService.java @@ -7,6 +7,8 @@ import ru.ulstu.conference.model.Conference; import ru.ulstu.conference.model.ConferenceDto; import ru.ulstu.conference.repository.ConferenceRepository; import ru.ulstu.deadline.service.DeadlineService; +import ru.ulstu.paper.model.Paper; +import ru.ulstu.paper.service.PaperService; import java.io.IOException; import java.util.List; @@ -20,11 +22,14 @@ public class ConferenceService { private final ConferenceRepository conferenceRepository; private final DeadlineService deadlineService; + private final PaperService paperService; public ConferenceService(ConferenceRepository conferenceRepository, - DeadlineService deadlineService) { + DeadlineService deadlineService, + PaperService paperService) { this.conferenceRepository = conferenceRepository; this.deadlineService = deadlineService; + this.paperService = paperService; } public List findAll() { @@ -64,6 +69,13 @@ public class ConferenceService { return conference.getId(); } + @Transactional + public void delete(Integer conferenceId) { + if (conferenceRepository.exists(conferenceId)) { + conferenceRepository.delete(conferenceId); + } + } + public void removeDeadline(ConferenceDto conferenceDto, Integer deadlineIndex) throws IOException { if (conferenceDto.getDeadlines().get(deadlineIndex).getId() != null) { conferenceDto.getRemovedDeadlineIds().add(conferenceDto.getDeadlines().get(deadlineIndex).getId()); @@ -71,6 +83,15 @@ public class ConferenceService { conferenceDto.getDeadlines().remove((int) deadlineIndex); } + public void removePaper(ConferenceDto conferenceDto, Integer paperIndex) throws IOException { + Paper removedPaper = conferenceDto.getPapers().remove((int) paperIndex); + conferenceDto.getNotSelectedPapers().add(removedPaper); + } + + public List getConferencePapers(List paperIds) { + return paperService.findAllNotSelect(paperIds); + } + private Conference copyFromDto(Conference conference, ConferenceDto conferenceDto) throws IOException { conference.setTitle(conferenceDto.getTitle()); conference.setDescription(conferenceDto.getDescription()); @@ -78,7 +99,12 @@ public class ConferenceService { conference.setPing(0); conference.setBeginDate(conferenceDto.getBeginDate()); conference.setEndDate(conferenceDto.getEndDate()); + conference.setPapers(conferenceDto.getPapers()); conference.setDeadlines(deadlineService.saveOrCreate(conferenceDto.getDeadlines())); + if (conferenceDto.getPaperIds() != null && !conferenceDto.getPaperIds().isEmpty()) { + conferenceDto.getPaperIds().forEach(paperId -> + conference.getPapers().add(paperService.findEntityById(paperId))); + } return conference; } diff --git a/src/main/java/ru/ulstu/paper/repository/PaperRepository.java b/src/main/java/ru/ulstu/paper/repository/PaperRepository.java index 3951f53..343e9e2 100644 --- a/src/main/java/ru/ulstu/paper/repository/PaperRepository.java +++ b/src/main/java/ru/ulstu/paper/repository/PaperRepository.java @@ -12,4 +12,6 @@ public interface PaperRepository extends JpaRepository { @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 filter(@Param("author") User author, @Param("year") Integer year); + + List findByIdNotIn(List paperIds); } diff --git a/src/main/java/ru/ulstu/paper/service/PaperService.java b/src/main/java/ru/ulstu/paper/service/PaperService.java index a93c9c6..6c27c6e 100644 --- a/src/main/java/ru/ulstu/paper/service/PaperService.java +++ b/src/main/java/ru/ulstu/paper/service/PaperService.java @@ -215,6 +215,19 @@ public class PaperService { return new PaperDto(paperRepository.findOne(paperId)); } + public Paper findEntityById(Integer paperId) { + return paperRepository.findOne(paperId); + } + + public List findAllNotSelect(List paperIds) { + if (!paperIds.isEmpty()) { + return sortPapers(paperRepository.findByIdNotIn(paperIds)); + } else { + return sortPapers(paperRepository.findAll()); + } + + } + public List getPaperAuthors() { return userService.findAll(); } diff --git a/src/main/java/ru/ulstu/students/controller/Navigation.java b/src/main/java/ru/ulstu/students/controller/Navigation.java new file mode 100644 index 0000000..3804a8b --- /dev/null +++ b/src/main/java/ru/ulstu/students/controller/Navigation.java @@ -0,0 +1,16 @@ +package ru.ulstu.students.controller; + +import org.springframework.validation.Errors; + +public class Navigation { + public static final String REDIRECT_TO = "redirect:%s"; + public static final String TASKS_PAGE = "/students/tasks"; + public static final String TASK_PAGE = "/students/task"; + + public static String hasErrors(Errors errors, String page) { + if (errors.hasErrors()) { + return page; + } + return null; + } +} diff --git a/src/main/java/ru/ulstu/students/controller/TaskController.java b/src/main/java/ru/ulstu/students/controller/TaskController.java new file mode 100644 index 0000000..8fd9575 --- /dev/null +++ b/src/main/java/ru/ulstu/students/controller/TaskController.java @@ -0,0 +1,84 @@ +package ru.ulstu.students.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 ru.ulstu.students.model.Task; +import ru.ulstu.students.model.TaskDto; +import ru.ulstu.students.service.TaskService; +import springfox.documentation.annotations.ApiIgnore; + +import javax.validation.Valid; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +import static org.springframework.util.StringUtils.isEmpty; +import static ru.ulstu.students.controller.Navigation.*; + +@Controller() +@RequestMapping(value = "/students") +@ApiIgnore +public class TaskController { + + private final TaskService taskService; + + public TaskController(TaskService taskService) { + this.taskService = taskService; + } + + @GetMapping("/tasks") + public void getTasks(ModelMap modelMap) { + modelMap.put("tasks", taskService.findAllDto()); + } + + @GetMapping("/dashboard") + public void getDashboard(ModelMap modelMap) { + modelMap.put("tasks", taskService.findAllDto()); + } + + @GetMapping("/task") + public void getTask(ModelMap modelMap, @RequestParam(value = "id") Integer id) { + if (id != null && id > 0) { + modelMap.put("taskDto", taskService.findOneDto(id)); + } else { + modelMap.put("taskDto", new TaskDto()); + } + } + + @PostMapping(value = "/task", params = "save") + public String save(@Valid TaskDto taskDto, Errors errors) throws IOException { + filterEmptyDeadlines(taskDto); + if (taskDto.getDeadlines().isEmpty()) { + errors.rejectValue("deadlines", "errorCode", "Не может быть пустым"); + } + if (errors.hasErrors()) { + return TASK_PAGE; + } + taskService.save(taskDto); + return String.format(REDIRECT_TO, TASKS_PAGE); + } + + @PostMapping(value = "/task", params = "addDeadline") + public String addDeadline(@Valid TaskDto taskDto, Errors errors) { + filterEmptyDeadlines(taskDto); + if (errors.hasErrors()) { + return TASK_PAGE; + } + taskDto.getDeadlines().add(new Deadline()); + return TASK_PAGE; + } + + @ModelAttribute("allStatuses") + public List getTaskStatuses() { + return taskService.getTaskStatuses(); + } + + private void filterEmptyDeadlines(TaskDto taskDto) { + taskDto.setDeadlines(taskDto.getDeadlines().stream() + .filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription())) + .collect(Collectors.toList())); + } +} diff --git a/src/main/java/ru/ulstu/students/model/Task.java b/src/main/java/ru/ulstu/students/model/Task.java new file mode 100644 index 0000000..be1b1c6 --- /dev/null +++ b/src/main/java/ru/ulstu/students/model/Task.java @@ -0,0 +1,118 @@ +package ru.ulstu.students.model; + +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.validator.constraints.NotBlank; +import ru.ulstu.core.model.BaseEntity; +import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.tags.model.Tag; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Entity +public class Task extends BaseEntity { + + public enum TaskStatus { + IN_WORK("В работе"), + COMPLETED("Завершен"), + FAILED("Провалены сроки"), + LOADED_FROM_KIAS("Загружен автоматически"); + + private String statusName; + + TaskStatus(String name) { + this.statusName = name; + } + + public String getStatusName() { + return statusName; + } + } + + @NotBlank + private String title; + + private String description; + + @Enumerated(value = EnumType.STRING) + private ru.ulstu.students.model.Task.TaskStatus status = TaskStatus.IN_WORK; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "task_id", unique = true) + @Fetch(FetchMode.SUBSELECT) + @OrderBy("date") + private List deadlines = new ArrayList<>(); + + @Column(name = "create_date") + @Temporal(TemporalType.TIMESTAMP) + private Date createDate = new Date(); + + @Column(name = "update_date") + @Temporal(TemporalType.TIMESTAMP) + private Date updateDate = new Date(); + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "task_tags", + joinColumns = {@JoinColumn(name = "task_id")}, + inverseJoinColumns = {@JoinColumn(name = "tag_id")}) + private List tags = new ArrayList<>(); + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public TaskStatus getStatus() { + return status; + } + + public void setStatus(TaskStatus status) { + this.status = status; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getDeadlines() { + return deadlines; + } + + public void setDeadlines(List deadlines) { + this.deadlines = deadlines; + } + + 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 getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } +} diff --git a/src/main/java/ru/ulstu/students/model/TaskDto.java b/src/main/java/ru/ulstu/students/model/TaskDto.java new file mode 100644 index 0000000..941a6df --- /dev/null +++ b/src/main/java/ru/ulstu/students/model/TaskDto.java @@ -0,0 +1,144 @@ +package ru.ulstu.students.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang3.StringUtils; +import org.hibernate.validator.constraints.NotEmpty; +import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.tags.model.Tag; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class TaskDto { + + private final static int MAX_TAGS_LENGTH = 50; + + private Integer id; + @NotEmpty + private String title; + private String description; + private Task.TaskStatus status; + private List deadlines = new ArrayList<>(); + private Date createDate; + private Date updateDate; + private Set tagIds; + private List tags; + + public TaskDto() { + deadlines.add(new Deadline()); + } + + @JsonCreator + public TaskDto(@JsonProperty("id") Integer id, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("createDate") Date createDate, + @JsonProperty("updateDate") Date updateDate, + @JsonProperty("status") Task.TaskStatus status, + @JsonProperty("deadlines") List deadlines, + @JsonProperty("tagIds") Set tagIds, + @JsonProperty("tags") List tags) { + this.id = id; + this.title = title; + this.status = status; + this.deadlines = deadlines; + this.createDate = createDate; + this.updateDate = updateDate; + this.description = description; + this.tags = tags; + } + + public TaskDto(Task task) { + this.id = task.getId(); + this.title = task.getTitle(); + this.status = task.getStatus(); + this.deadlines = task.getDeadlines(); + this.createDate = task.getCreateDate(); + this.updateDate = task.getUpdateDate(); + this.description = task.getDescription(); + this.tags = task.getTags(); + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Task.TaskStatus getStatus() { + return status; + } + + public void setStatus(Task.TaskStatus status) { + this.status = status; + } + + public List getDeadlines() { + return deadlines; + } + + public void setDeadlines(List deadlines) { + this.deadlines = deadlines; + } + + 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 Set getTagIds() { + return tagIds; + } + + public void setTagIds(Set tagIds) { + this.tagIds = tagIds; + } + + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + public String getTagsString() { + return StringUtils.abbreviate(tags + .stream() + .map(tag -> tag.getTagName()) + .collect(Collectors.joining(", ")), MAX_TAGS_LENGTH); + } +} diff --git a/src/main/java/ru/ulstu/students/repository/TaskRepository.java b/src/main/java/ru/ulstu/students/repository/TaskRepository.java new file mode 100644 index 0000000..6f48d1f --- /dev/null +++ b/src/main/java/ru/ulstu/students/repository/TaskRepository.java @@ -0,0 +1,7 @@ +package ru.ulstu.students.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.students.model.Task; + +public interface TaskRepository extends JpaRepository { +} diff --git a/src/main/java/ru/ulstu/students/service/TaskService.java b/src/main/java/ru/ulstu/students/service/TaskService.java new file mode 100644 index 0000000..7b67a44 --- /dev/null +++ b/src/main/java/ru/ulstu/students/service/TaskService.java @@ -0,0 +1,95 @@ +package ru.ulstu.students.service; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.ulstu.deadline.service.DeadlineService; +import ru.ulstu.students.model.Task; +import ru.ulstu.students.model.TaskDto; +import ru.ulstu.students.repository.TaskRepository; +import ru.ulstu.tags.service.TagService; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import static org.springframework.util.ObjectUtils.isEmpty; +import static ru.ulstu.core.util.StreamApiUtils.convert; +import static ru.ulstu.students.model.Task.TaskStatus.IN_WORK; + +@Service +public class TaskService { + + private final static int MAX_DISPLAY_SIZE = 40; + + private final TaskRepository taskRepository; + private final DeadlineService deadlineService; + private final TagService tagService; + + public TaskService(TaskRepository grantRepository, + DeadlineService deadlineService, TagService tagService) { + this.taskRepository = grantRepository; + this.deadlineService = deadlineService; + this.tagService = tagService; + } + + public List findAll() { + return taskRepository.findAll(); + } + + public List findAllDto() { + List tasks = convert(findAll(), TaskDto::new); + tasks.forEach(taskDto -> taskDto.setTitle(StringUtils.abbreviate(taskDto.getTitle(), MAX_DISPLAY_SIZE))); + return tasks; + } + + public TaskDto findOneDto(Integer id) { + return new TaskDto(taskRepository.findOne(id)); + } + + @Transactional + public Integer create(TaskDto taskDto) throws IOException { + Task newTask = copyFromDto(new Task(), taskDto); + newTask = taskRepository.save(newTask); + return newTask.getId(); + } + + private Task copyFromDto(Task task, TaskDto taskDto) throws IOException { + task.setTitle(taskDto.getTitle()); + task.setDescription(taskDto.getDescription()); + task.setStatus(taskDto.getStatus() == null ? IN_WORK : taskDto.getStatus()); + task.setDeadlines(deadlineService.saveOrCreate(taskDto.getDeadlines())); + task.setCreateDate(task.getCreateDate() == null ? new Date() : task.getCreateDate()); + task.setUpdateDate(new Date()); + task.getTags().clear(); + task.setTags(tagService.saveOrCreate(taskDto.getTags())); + return task; + } + + @Transactional + public Integer update(TaskDto taskDto) throws IOException { + Task task = taskRepository.findOne(taskDto.getId()); + taskRepository.save(copyFromDto(task, taskDto)); + return task.getId(); + } + + @Transactional + public void delete(Integer taskId) throws IOException { + Task task = taskRepository.findOne(taskId); + taskRepository.delete(task); + } + + public void save(TaskDto taskDto) throws IOException { + if (isEmpty(taskDto.getId())) { + create(taskDto); + } else { + update(taskDto); + } + } + + public List getTaskStatuses() { + return Arrays.asList(Task.TaskStatus.values()); + } + +} diff --git a/src/main/java/ru/ulstu/tags/model/Tag.java b/src/main/java/ru/ulstu/tags/model/Tag.java new file mode 100644 index 0000000..aed000f --- /dev/null +++ b/src/main/java/ru/ulstu/tags/model/Tag.java @@ -0,0 +1,44 @@ +package ru.ulstu.tags.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.hibernate.validator.constraints.NotEmpty; +import ru.ulstu.core.model.BaseEntity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.Size; + +@Entity +@Table(name = "tag") +public class Tag extends BaseEntity { + + @NotEmpty + @Size(max = 50) + @Column(name = "tag_name") + private String tagName; + + public Tag() { + + } + + @JsonCreator + public Tag(@JsonProperty("id") Integer id, + @JsonProperty("tag_name") String tagName) { + this.setId(id); + this.tagName = tagName; + } + + public Tag(String name) { + this.tagName = name; + } + + public String getTagName() { + return tagName; + } + + public void setTagName(String tagName) { + this.tagName = tagName; + } +} diff --git a/src/main/java/ru/ulstu/tags/repository/TagRepository.java b/src/main/java/ru/ulstu/tags/repository/TagRepository.java new file mode 100644 index 0000000..213abae --- /dev/null +++ b/src/main/java/ru/ulstu/tags/repository/TagRepository.java @@ -0,0 +1,12 @@ +package ru.ulstu.tags.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import ru.ulstu.tags.model.Tag; + +public interface TagRepository extends JpaRepository { + + @Query("SELECT t FROM Tag t WHERE (t.tagName = :tagName)") + Tag findByName(@Param("tagName") String tagName); +} diff --git a/src/main/java/ru/ulstu/tags/service/TagService.java b/src/main/java/ru/ulstu/tags/service/TagService.java new file mode 100644 index 0000000..4e88304 --- /dev/null +++ b/src/main/java/ru/ulstu/tags/service/TagService.java @@ -0,0 +1,53 @@ +package ru.ulstu.tags.service; + +import org.springframework.stereotype.Service; +import ru.ulstu.tags.model.Tag; +import ru.ulstu.tags.repository.TagRepository; + +import javax.transaction.Transactional; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class TagService { + + private final TagRepository tagRepository; + + + public TagService(TagRepository tagRepository) { + + this.tagRepository = tagRepository; + } + + public List saveOrCreate(List tags) { + return tags + .stream() + .map(tag -> { + if (tag.getId() != null) { + return getExistById(tag); + } else { + Tag existTag = isExistByName(tag.getTagName()); + return existTag != null ? existTag : create(tag); + } + }).collect(Collectors.toList()); + } + + @Transactional + public Tag getExistById(Tag tag) { + return tagRepository.findOne(tag.getId()); + } + + @Transactional + public Tag isExistByName(String tagName) { + return tagRepository.findByName(tagName); + } + + @Transactional + public Tag create(Tag tag) { + Tag newTag = new Tag(); + newTag.setTagName(tag.getTagName()); + newTag = tagRepository.save(newTag); + return newTag; + } + +} diff --git a/src/main/resources/db/changelog-20190410_000000-schema.xml b/src/main/resources/db/changelog-20190410_000000-schema.xml new file mode 100644 index 0000000..b8db7f9 --- /dev/null +++ b/src/main/resources/db/changelog-20190410_000000-schema.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog-master.xml b/src/main/resources/db/changelog-master.xml index 800b2f1..67d3c40 100644 --- a/src/main/resources/db/changelog-master.xml +++ b/src/main/resources/db/changelog-master.xml @@ -23,5 +23,6 @@ + \ No newline at end of file diff --git a/src/main/resources/public/css/agency.css b/src/main/resources/public/css/agency.css index e1cc226..fb3ec24 100644 --- a/src/main/resources/public/css/agency.css +++ b/src/main/resources/public/css/agency.css @@ -1,853 +1,839 @@ -body { - overflow-x: hidden; - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -p { - line-height: 1.75; -} - -a { - color: #000000; -} - -a:hover { - color: #fec503; -} - -.text-draft { - color: rgba(0, 0, 0, 0.48) !important; -} - -.text-primary { - color: #228bba !important; -} - -.text-warning { - color: #940000 !important; -} - -.text-review { - color: #94028d !important; -} - -.text-success { - color: #007741 !important; -} - -.text-accepted { - color: #fec503 !important; -} - -.text-not-accepted { - color: #A38831 !important; -} - -.text-failed { - color: #A38831 !important; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: 700; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -section { - padding: 100px 0; -} - -section h2.section-heading { - font-size: 2.5vw; - margin-top: 0; - margin-bottom: 15px; -} - -section h3.section-subheading { - font-size: 16px; - font-weight: 400; - font-style: italic; - margin-bottom: 75px; - text-transform: none; - font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -@media (min-width: 768px) { - section { - padding: 100px 0; - } -} - -.btn { - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-weight: 700; -} - -.btn-xl { - font-size: 18px; - padding: 20px 40px; -} - -.btn-success { - background-color: #28a745; - border-color: #28a745; -} - -.btn-primary { - background-color: #fed136; - border-color: #fed136; -} - -.btn-default { - background-color: #ffffff; - border-color: #ced4da;; -} - -.btn-info { - background-color: #5bc0de; - border-color: #5bc0de; -} - -.progress-bar { - display: flex; - -ms-flex-direction: column; - flex-direction: column; - -ms-flex-pack: center; - justify-content: center; - color: #fff; - text-align: center; - white-space: nowrap; - background-color: #fed136; - transition: width .6s ease; -} - -.btn-primary:active, .btn-primary:focus, .btn-primary:hover { - background-color: #fec810 !important; - border-color: #fec810 !important; - color: white; -} - -.btn-primary:active, .btn-primary:focus { - box-shadow: 0 0 0 0.2rem rgba(254, 209, 55, 0.5) !important; -} - -::-moz-selection { - background: #fed136; - text-shadow: none; -} - -::selection { - background: #fed136; - text-shadow: none; -} - -img::selection { - background: transparent; -} - -img::-moz-selection { - background: transparent; -} - -#mainNav { - background-color: #212529; -} - -#mainNav .navbar-toggler { - font-size: 12px; - right: 0; - padding: 13px; - text-transform: uppercase; - color: white; - border: 0; - background-color: #fed136; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -#mainNav .navbar-brand { - color: #fed136; - font-family: 'Kaushan Script', 'Helvetica Neue', Helvetica, Arial, cursive; -} - -#mainNav .navbar-brand.active, #mainNav .navbar-brand:active, #mainNav .navbar-brand:focus, #mainNav .navbar-brand:hover { - color: #fec503; -} - -#mainNav .navbar-nav .nav-item .nav-link { - font-size: 90%; - font-weight: 400; - padding: 0.75em 0; - letter-spacing: 1px; - color: white; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -#mainNav .navbar-nav .nav-item .nav-link.active, #mainNav .navbar-nav .nav-item .nav-link:hover { - color: #fed136; -} - -@media (min-width: 992px) { - #mainNav { - padding-top: 25px; - padding-bottom: 25px; - -webkit-transition: padding-top 0.3s, padding-bottom 0.3s; - -moz-transition: padding-top 0.3s, padding-bottom 0.3s; - transition: padding-top 0.3s, padding-bottom 0.3s; - border: none; - background-color: transparent; - } - - #mainNav .navbar-brand { - font-size: 1.75em; - -webkit-transition: all 0.3s; - -moz-transition: all 0.3s; - transition: all 0.3s; - } - - #mainNav .navbar-nav .nav-item .nav-link { - padding: 1.1em 1em !important; - } - - #mainNav.navbar-shrink { - padding-top: 0; - padding-bottom: 0; - background-color: #212529; - } - - #mainNav.navbar-shrink .navbar-brand { - font-size: 1.25em; - padding: 12px 0; - } -} - -header.masthead { - text-align: center; - color: white; - background-image: url("../img/header-bg.jpg"); - background-repeat: no-repeat; - background-attachment: scroll; - background-position: center center; - -webkit-background-size: cover; - -moz-background-size: cover; - -o-background-size: cover; - background-size: cover; -} - -header.masthead .intro-text { - padding-top: 150px; - padding-bottom: 100px; -} - -header.masthead .intro-text .intro-lead-in { - font-size: 22px; - font-style: italic; - line-height: 22px; - margin-bottom: 25px; - font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -header.masthead .intro-text .intro-heading { - font-size: 50px; - font-weight: 700; - line-height: 50px; - margin-bottom: 25px; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -@media (min-width: 768px) { - header.masthead .intro-text { - padding-top: 300px; - padding-bottom: 200px; - } - - header.masthead .intro-text .intro-lead-in { - font-size: 40px; - font-style: italic; - line-height: 40px; - margin-bottom: 25px; - font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; - } - - header.masthead .intro-text .intro-heading { - font-size: 75px; - font-weight: 700; - line-height: 75px; - margin-bottom: 50px; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; - } -} - -.service-heading { - margin: 15px 0; - text-transform: none; -} - -#portfolio .portfolio-item { - right: 0; - margin: 0 0 15px; -} - -#portfolio .portfolio-item .portfolio-link { - position: relative; - display: block; - max-width: 400px; - margin: 0 auto; - cursor: pointer; -} - -#portfolio .portfolio-item .portfolio-link .portfolio-hover { - position: absolute; - width: 100%; - height: 100%; - -webkit-transition: all ease 0.5s; - -moz-transition: all ease 0.5s; - transition: all ease 0.5s; - opacity: 0; - background: rgba(254, 209, 54, 0.9); -} - -#portfolio .portfolio-item .portfolio-link .portfolio-hover:hover { - opacity: 1; -} - -#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content { - font-size: 20px; - position: absolute; - top: 50%; - width: 100%; - height: 20px; - margin-top: -12px; - text-align: center; - color: white; -} - -#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content i { - margin-top: -12px; -} - -#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h3, -#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h4 { - margin: 0; -} - -#portfolio .portfolio-item .portfolio-caption { - max-width: 400px; - margin: 0 auto; - padding: 25px; - text-align: center; - background-color: #fff; -} - -#portfolio .portfolio-item .portfolio-caption h4 { - margin: 0; - text-transform: none; -} - -#portfolio .portfolio-item .portfolio-caption p { - font-size: 16px; - font-style: italic; - margin: 0; - font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -#portfolio * { - z-index: 2; -} - -@media (min-width: 767px) { - #portfolio .portfolio-item { - margin: 0 0 30px; - } -} - -.portfolio-modal { - padding-right: 0px !important; -} - -.portfolio-modal .modal-dialog { - margin: 1rem; - max-width: 100vw; -} - -.portfolio-modal .modal-content { - padding: 100px 0; - text-align: center; -} - -.portfolio-modal .modal-content h2 { - font-size: 3em; - margin-bottom: 15px; -} - -.portfolio-modal .modal-content p { - margin-bottom: 30px; -} - -.portfolio-modal .modal-content p.item-intro { - font-size: 16px; - font-style: italic; - margin: 20px 0 30px; - font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -.portfolio-modal .modal-content ul.list-inline { - margin-top: 0; - margin-bottom: 30px; -} - -.portfolio-modal .modal-content img { - margin-bottom: 30px; -} - -.portfolio-modal .modal-content button { - cursor: pointer; -} - -.portfolio-modal .close-modal { - position: absolute; - top: 25px; - right: 25px; - width: 75px; - height: 75px; - cursor: pointer; - background-color: transparent; -} - -.portfolio-modal .close-modal:hover { - opacity: 0.3; -} - -.portfolio-modal .close-modal .lr { - /* Safari and Chrome */ - z-index: 1051; - width: 1px; - height: 75px; - margin-left: 35px; - /* IE 9 */ - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); - background-color: #212529; -} - -.portfolio-modal .close-modal .lr .rl { - /* Safari and Chrome */ - z-index: 1052; - width: 1px; - height: 75px; - /* IE 9 */ - -webkit-transform: rotate(90deg); - -ms-transform: rotate(90deg); - transform: rotate(90deg); - background-color: #212529; -} - -.timeline { - position: relative; - padding: 0; - list-style: none; -} - -.timeline:before { - position: absolute; - top: 0; - bottom: 0; - left: 40px; - width: 2px; - margin-left: -1.5px; - content: ''; - background-color: #e9ecef; -} - -.timeline > li { - position: relative; - min-height: 50px; - margin-bottom: 50px; -} - -.timeline > li:after, .timeline > li:before { - display: table; - content: ' '; -} - -.timeline > li:after { - clear: both; -} - -.timeline > li .timeline-panel { - position: relative; - float: right; - width: 100%; - padding: 0 20px 0 100px; - text-align: left; -} - -.timeline > li .timeline-panel:before { - right: auto; - left: -15px; - border-right-width: 15px; - border-left-width: 0; -} - -.timeline > li .timeline-panel:after { - right: auto; - left: -14px; - border-right-width: 14px; - border-left-width: 0; -} - -.timeline > li .timeline-image { - position: absolute; - z-index: 100; - left: 0; - width: 80px; - height: 80px; - margin-left: 0; - text-align: center; - color: white; - border: 7px solid #e9ecef; - border-radius: 100%; - background-color: #fed136; -} - -.timeline > li .timeline-image h4 { - font-size: 10px; - line-height: 14px; - margin-top: 12px; -} - -.timeline > li.timeline-inverted > .timeline-panel { - float: right; - padding: 0 20px 0 100px; - text-align: left; -} - -.timeline > li.timeline-inverted > .timeline-panel:before { - right: auto; - left: -15px; - border-right-width: 15px; - border-left-width: 0; -} - -.timeline > li.timeline-inverted > .timeline-panel:after { - right: auto; - left: -14px; - border-right-width: 14px; - border-left-width: 0; -} - -.timeline > li:last-child { - margin-bottom: 0; -} - -.timeline .timeline-heading h4 { - margin-top: 0; - color: inherit; -} - -.timeline .timeline-heading h4.subheading { - text-transform: none; -} - -.timeline .timeline-body > ul, -.timeline .timeline-body > p { - margin-bottom: 0; -} - -@media (min-width: 768px) { - .timeline:before { - left: 50%; - } - - .timeline > li { - min-height: 100px; - margin-bottom: 100px; - } - - .timeline > li .timeline-panel { - float: left; - width: 41%; - padding: 0 20px 20px 30px; - text-align: right; - } - - .timeline > li .timeline-image { - left: 50%; - width: 100px; - height: 100px; - margin-left: -50px; - } - - .timeline > li .timeline-image h4 { - font-size: 13px; - line-height: 18px; - margin-top: 16px; - } - - .timeline > li.timeline-inverted > .timeline-panel { - float: right; - padding: 0 30px 20px 20px; - text-align: left; - } -} - -@media (min-width: 992px) { - .timeline > li { - min-height: 150px; - } - - .timeline > li .timeline-panel { - padding: 0 20px 20px; - } - - .timeline > li .timeline-image { - width: 150px; - height: 150px; - margin-left: -75px; - } - - .timeline > li .timeline-image h4 { - font-size: 18px; - line-height: 26px; - margin-top: 30px; - } - - .timeline > li.timeline-inverted > .timeline-panel { - padding: 0 20px 20px; - } -} - -@media (min-width: 1200px) { - .timeline > li { - min-height: 170px; - } - - .timeline > li .timeline-panel { - padding: 0 20px 20px 100px; - } - - .timeline > li .timeline-image { - width: 170px; - height: 170px; - margin-left: -85px; - } - - .timeline > li .timeline-image h4 { - margin-top: 40px; - } - - .timeline > li.timeline-inverted > .timeline-panel { - padding: 0 100px 20px 20px; - } -} - -.team-member { - margin-bottom: 50px; - text-align: center; -} - -.team-member img { - width: 225px; - height: 225px; - border: 7px solid #fff; -} - -.team-member h4 { - margin-top: 25px; - margin-bottom: 0; - text-transform: none; -} - -.team-member p { - margin-top: 0; -} - -section#contact { - background-color: #212529; - background-image: url("../img/map-image.png"); - background-repeat: no-repeat; - background-position: center; -} - -section#contact .section-heading { - color: #fff; -} - -section#contact .form-group { - margin-bottom: 25px; -} - -section#contact .form-group input, -section#contact .form-group textarea { - padding: 20px; -} - -section#contact .form-group input.form-control { - height: auto; -} - -section#contact .form-group textarea.form-control { - height: 248px; -} - -section#contact .form-control:focus { - border-color: #fed136; - box-shadow: none; -} - -section#contact ::-webkit-input-placeholder { - font-weight: 700; - color: #ced4da; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -section#contact :-moz-placeholder { - font-weight: 700; - color: #ced4da; - /* Firefox 18- */ - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -section#contact ::-moz-placeholder { - font-weight: 700; - color: #ced4da; - /* Firefox 19+ */ - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -section#contact :-ms-input-placeholder { - font-weight: 700; - color: #ced4da; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -footer { - padding: 25px 0; - text-align: center; -} - -footer span.copyright { - font-size: 90%; - line-height: 40px; - text-transform: none; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -footer ul.quicklinks { - font-size: 90%; - line-height: 40px; - margin-bottom: 0; - text-transform: none; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -ul.social-buttons { - margin-bottom: 0; -} - -ul.social-buttons li a { - font-size: 20px; - line-height: 40px; - display: block; - width: 40px; - height: 40px; - -webkit-transition: all 0.3s; - -moz-transition: all 0.3s; - transition: all 0.3s; - color: white; - border-radius: 100%; - outline: none; - background-color: #212529; -} - -ul.social-buttons li a:active, ul.social-buttons li a:focus, ul.social-buttons li a:hover { - background-color: #fed136; -} - -.dashboard-card { - background: #ffffff none repeat scroll 0 0; - margin: 15px; - padding: 20px; - border: 0 solid #e7e7e7; - border-radius: 5px; - box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05); -} - -@media (min-width: 1500px) { - .col-xl-3 { - max-width: 20%; - } - - .container { - max-width: 1340px; - } -} - -.toolbar-button { - width: 100%; - margin: 5px; -} - -.img-fluid { - width: 100% !important; -} - -/* --------------------------------------------------- - FEEDBACK STYLE ------------------------------------------------------ */ -.feedback-panel { - list-style-type: none; - padding: 5px; -} - -.feedback-panel .alert { - padding: 5px; - margin-bottom: 5px; - cursor: pointer; -} - -/* --------------------------------------------------- - MEDIAQUERIES ------------------------------------------------------ */ -@media (min-width: 768px) { - section h2.section-heading { - font-size: 3.5vw; - margin-top: 0; - margin-bottom: 15px; - } -} - -@media (min-width: 1500px) { - section h2.section-heading { - font-size: 1.5vw; - margin-top: 0; - margin-bottom: 15px; - } -} - -@media (min-width: 768px) { - .feedback-panel { - position: fixed; - overflow-y: hidden; - max-height: calc(100vh - 60px); - width: 250px; - top: 50px; - right: 15px; - opacity: .8; - z-index: 10000; - } - - .feedback-panel .alert { - padding: 10px; - margin-bottom: 10px; - overflow: hidden; - } - - .feedback-panel .alert:hover { - border-color: #888; - } +body { + overflow-x: hidden; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +p { + line-height: 1.75; +} + +a { + color: #000000; +} + +a:hover { + color: #fec503; +} + +.text-draft { + color: rgba(0, 0, 0, 0.48) !important; +} + +.text-primary { + color: #228bba !important; +} + +.text-warning { + color: #940000 !important; +} + +.text-review { + color: #94028d !important; +} + +.text-success { + color: #007741 !important; +} + +.text-accepted { + color: #fec503 !important; +} + +.text-not-accepted { + color: #A38831 !important; +} + +.text-failed { + color: #A38831 !important; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 700; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +section { + padding: 100px 0; +} + +section h2.section-heading { + font-size: 2.5vw; + margin-top: 0; + margin-bottom: 15px; +} + +section h3.section-subheading { + font-size: 16px; + font-weight: 400; + font-style: italic; + margin-bottom: 75px; + text-transform: none; + font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +@media (min-width: 768px) { + section { + padding: 100px 0; + } +} + +.btn { + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-weight: 700; +} + +.btn-xl { + font-size: 18px; + padding: 20px 40px; +} + +.btn-success { + background-color: #28a745; + border-color: #28a745; +} + +.btn-primary { + background-color: #fed136; + border-color: #fed136; +} + +.btn-default { + background-color: #ffffff; + border-color: #ced4da;; +} + +.btn-info { + background-color: #5bc0de; + border-color: #5bc0de; +} + +.progress-bar { + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-pack: center; + justify-content: center; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #fed136; + transition: width .6s ease; +} + +.btn-primary:active, .btn-primary:focus, .btn-primary:hover { + background-color: #fec810 !important; + border-color: #fec810 !important; + color: white; +} + +.btn-primary:active, .btn-primary:focus { + box-shadow: 0 0 0 0.2rem rgba(254, 209, 55, 0.5) !important; +} + +::-moz-selection { + background: #fed136; + text-shadow: none; +} + +::selection { + background: #fed136; + text-shadow: none; +} + +img::selection { + background: transparent; +} + +img::-moz-selection { + background: transparent; +} + +#mainNav { + background-color: #212529; +} + +#mainNav .navbar-toggler { + font-size: 12px; + right: 0; + padding: 13px; + text-transform: uppercase; + color: white; + border: 0; + background-color: #fed136; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +#mainNav .navbar-brand { + color: #fed136; + font-family: 'Kaushan Script', 'Helvetica Neue', Helvetica, Arial, cursive; +} + +#mainNav .navbar-brand.active, #mainNav .navbar-brand:active, #mainNav .navbar-brand:focus, #mainNav .navbar-brand:hover { + color: #fec503; +} + +#mainNav .navbar-nav .nav-item .nav-link { + font-size: 90%; + font-weight: 400; + padding: 0.75em 0; + letter-spacing: 1px; + color: white; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +#mainNav .navbar-nav .nav-item .nav-link.active, #mainNav .navbar-nav .nav-item .nav-link:hover { + color: #fed136; +} + +@media (min-width: 992px) { + #mainNav { + padding-top: 25px; + padding-bottom: 25px; + -webkit-transition: padding-top 0.3s, padding-bottom 0.3s; + -moz-transition: padding-top 0.3s, padding-bottom 0.3s; + transition: padding-top 0.3s, padding-bottom 0.3s; + border: none; + background-color: transparent; + } + + #mainNav .navbar-brand { + font-size: 1.75em; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; + } + + #mainNav .navbar-nav .nav-item .nav-link { + padding: 1.1em 1em !important; + } + + #mainNav.navbar-shrink { + padding-top: 0; + padding-bottom: 0; + background-color: #212529; + } + + #mainNav.navbar-shrink .navbar-brand { + font-size: 1.25em; + padding: 12px 0; + } +} + +header.masthead { + text-align: center; + color: white; + background-image: url("../img/header-bg.jpg"); + background-repeat: no-repeat; + background-attachment: scroll; + background-position: center center; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} + +header.masthead .intro-text { + padding-top: 150px; + padding-bottom: 100px; +} + +header.masthead .intro-text .intro-lead-in { + font-size: 22px; + font-style: italic; + line-height: 22px; + margin-bottom: 25px; + font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +header.masthead .intro-text .intro-heading { + font-size: 50px; + font-weight: 700; + line-height: 50px; + margin-bottom: 25px; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +@media (min-width: 768px) { + header.masthead .intro-text { + padding-top: 300px; + padding-bottom: 200px; + } + + header.masthead .intro-text .intro-lead-in { + font-size: 40px; + font-style: italic; + line-height: 40px; + margin-bottom: 25px; + font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; + } + + header.masthead .intro-text .intro-heading { + font-size: 75px; + font-weight: 700; + line-height: 75px; + margin-bottom: 50px; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; + } +} + +.service-heading { + margin: 15px 0; + text-transform: none; +} + +#portfolio .portfolio-item { + right: 0; + margin: 0 0 15px; +} + +#portfolio .portfolio-item .portfolio-link { + position: relative; + display: block; + max-width: 400px; + margin: 0 auto; + cursor: pointer; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover { + position: absolute; + width: 100%; + height: 100%; + -webkit-transition: all ease 0.5s; + -moz-transition: all ease 0.5s; + transition: all ease 0.5s; + opacity: 0; + background: rgba(254, 209, 54, 0.9); +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover:hover { + opacity: 1; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content { + font-size: 20px; + position: absolute; + top: 50%; + width: 100%; + height: 20px; + margin-top: -12px; + text-align: center; + color: white; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content i { + margin-top: -12px; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h3, +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h4 { + margin: 0; +} + +#portfolio .portfolio-item .portfolio-caption { + max-width: 400px; + margin: 0 auto; + padding: 25px; + text-align: center; + background-color: #fff; +} + +#portfolio .portfolio-item .portfolio-caption h4 { + margin: 0; + text-transform: none; +} + +#portfolio .portfolio-item .portfolio-caption p { + font-size: 16px; + font-style: italic; + margin: 0; + font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +#portfolio * { + z-index: 2; +} + +@media (min-width: 767px) { + #portfolio .portfolio-item { + margin: 0 0 30px; + } +} + +.portfolio-modal { + padding-right: 0px !important; +} + +.portfolio-modal .modal-dialog { + margin: 1rem; + max-width: 100vw; +} + +.portfolio-modal .modal-content { + padding: 100px 0; + text-align: center; +} + +.portfolio-modal .modal-content h2 { + font-size: 3em; + margin-bottom: 15px; +} + +.portfolio-modal .modal-content p { + margin-bottom: 30px; +} + +.portfolio-modal .modal-content p.item-intro { + font-size: 16px; + font-style: italic; + margin: 20px 0 30px; + font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +.portfolio-modal .modal-content ul.list-inline { + margin-top: 0; + margin-bottom: 30px; +} + +.portfolio-modal .modal-content img { + margin-bottom: 30px; +} + +.portfolio-modal .modal-content button { + cursor: pointer; +} + +.portfolio-modal .close-modal { + position: absolute; + top: 25px; + right: 25px; + width: 75px; + height: 75px; + cursor: pointer; + background-color: transparent; +} + +.portfolio-modal .close-modal:hover { + opacity: 0.3; +} + +.portfolio-modal .close-modal .lr { + /* Safari and Chrome */ + z-index: 1051; + width: 1px; + height: 75px; + margin-left: 35px; + /* IE 9 */ + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + background-color: #212529; +} + +.portfolio-modal .close-modal .lr .rl { + /* Safari and Chrome */ + z-index: 1052; + width: 1px; + height: 75px; + /* IE 9 */ + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); + background-color: #212529; +} + +.timeline { + position: relative; + padding: 0; + list-style: none; +} + +.timeline:before { + position: absolute; + top: 0; + bottom: 0; + left: 40px; + width: 2px; + margin-left: -1.5px; + content: ''; + background-color: #e9ecef; +} + +.timeline > li { + position: relative; + min-height: 50px; + margin-bottom: 50px; +} + +.timeline > li:after, .timeline > li:before { + display: table; + content: ' '; +} + +.timeline > li:after { + clear: both; +} + +.timeline > li .timeline-panel { + position: relative; + float: right; + width: 100%; + padding: 0 20px 0 100px; + text-align: left; +} + +.timeline > li .timeline-panel:before { + right: auto; + left: -15px; + border-right-width: 15px; + border-left-width: 0; +} + +.timeline > li .timeline-panel:after { + right: auto; + left: -14px; + border-right-width: 14px; + border-left-width: 0; +} + +.timeline > li .timeline-image { + position: absolute; + z-index: 100; + left: 0; + width: 80px; + height: 80px; + margin-left: 0; + text-align: center; + color: white; + border: 7px solid #e9ecef; + border-radius: 100%; + background-color: #fed136; +} + +.timeline > li .timeline-image h4 { + font-size: 10px; + line-height: 14px; + margin-top: 12px; +} + +.timeline > li.timeline-inverted > .timeline-panel { + float: right; + padding: 0 20px 0 100px; + text-align: left; +} + +.timeline > li.timeline-inverted > .timeline-panel:before { + right: auto; + left: -15px; + border-right-width: 15px; + border-left-width: 0; +} + +.timeline > li.timeline-inverted > .timeline-panel:after { + right: auto; + left: -14px; + border-right-width: 14px; + border-left-width: 0; +} + +.timeline > li:last-child { + margin-bottom: 0; +} + +.timeline .timeline-heading h4 { + margin-top: 0; + color: inherit; +} + +.timeline .timeline-heading h4.subheading { + text-transform: none; +} + +.timeline .timeline-body > ul, +.timeline .timeline-body > p { + margin-bottom: 0; +} + +@media (min-width: 768px) { + .timeline:before { + left: 50%; + } + + .timeline > li { + min-height: 100px; + margin-bottom: 100px; + } + + .timeline > li .timeline-panel { + float: left; + width: 41%; + padding: 0 20px 20px 30px; + text-align: right; + } + + .timeline > li .timeline-image { + left: 50%; + width: 100px; + height: 100px; + margin-left: -50px; + } + + .timeline > li .timeline-image h4 { + font-size: 13px; + line-height: 18px; + margin-top: 16px; + } + + .timeline > li.timeline-inverted > .timeline-panel { + float: right; + padding: 0 30px 20px 20px; + text-align: left; + } +} + +@media (min-width: 992px) { + .timeline > li { + min-height: 150px; + } + + .timeline > li .timeline-panel { + padding: 0 20px 20px; + } + + .timeline > li .timeline-image { + width: 150px; + height: 150px; + margin-left: -75px; + } + + .timeline > li .timeline-image h4 { + font-size: 18px; + line-height: 26px; + margin-top: 30px; + } + + .timeline > li.timeline-inverted > .timeline-panel { + padding: 0 20px 20px; + } +} + +@media (min-width: 1200px) { + .timeline > li { + min-height: 170px; + } + + .timeline > li .timeline-panel { + padding: 0 20px 20px 100px; + } + + .timeline > li .timeline-image { + width: 170px; + height: 170px; + margin-left: -85px; + } + + .timeline > li .timeline-image h4 { + margin-top: 40px; + } + + .timeline > li.timeline-inverted > .timeline-panel { + padding: 0 100px 20px 20px; + } +} + +.team-member { + margin-bottom: 50px; + text-align: center; +} + +.team-member img { + width: 225px; + height: 225px; + border: 7px solid #fff; +} + +.team-member h4 { + margin-top: 25px; + margin-bottom: 0; + text-transform: none; +} + +.team-member p { + margin-top: 0; +} + +section#contact { + background-color: #212529; + background-image: url("../img/map-image.png"); + background-repeat: no-repeat; + background-position: center; +} + +section#contact .section-heading { + color: #fff; +} + +section#contact .form-group { + margin-bottom: 25px; +} + +section#contact .form-group input, +section#contact .form-group textarea { + padding: 20px; +} + +section#contact .form-group input.form-control { + height: auto; +} + +section#contact .form-group textarea.form-control { + height: 248px; +} + +section#contact .form-control:focus { + border-color: #fed136; + box-shadow: none; +} + +section#contact ::-webkit-input-placeholder { + font-weight: 700; + color: #ced4da; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +section#contact :-moz-placeholder { + font-weight: 700; + color: #ced4da; + /* Firefox 18- */ + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +section#contact ::-moz-placeholder { + font-weight: 700; + color: #ced4da; + /* Firefox 19+ */ + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +section#contact :-ms-input-placeholder { + font-weight: 700; + color: #ced4da; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +footer { + padding: 25px 0; + text-align: center; +} + +footer span.copyright { + font-size: 90%; + line-height: 40px; + text-transform: none; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +footer ul.quicklinks { + font-size: 90%; + line-height: 40px; + margin-bottom: 0; + text-transform: none; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +ul.social-buttons { + margin-bottom: 0; +} + +ul.social-buttons li a { + font-size: 20px; + line-height: 40px; + display: block; + width: 40px; + height: 40px; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; + color: white; + border-radius: 100%; + outline: none; + background-color: #212529; +} + +ul.social-buttons li a:active, ul.social-buttons li a:focus, ul.social-buttons li a:hover { + background-color: #fed136; +} + +.dashboard-card { + background: #ffffff none repeat scroll 0 0; + margin: 15px; + padding: 20px; + border: 0 solid #e7e7e7; + border-radius: 5px; + box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05); +} + +.toolbar-button { + width: 100%; + margin: 5px; +} + +/* --------------------------------------------------- + FEEDBACK STYLE +----------------------------------------------------- */ +.feedback-panel { + list-style-type: none; + padding: 5px; +} + +.feedback-panel .alert { + padding: 5px; + margin-bottom: 5px; + cursor: pointer; +} + +/* --------------------------------------------------- + MEDIAQUERIES +----------------------------------------------------- */ +@media (min-width: 768px) { + section h2.section-heading { + font-size: 3.5vw; + margin-top: 0; + margin-bottom: 15px; + } +} + +@media (min-width: 1500px) { + section h2.section-heading { + font-size: 1.5vw; + margin-top: 0; + margin-bottom: 15px; + } +} + +@media (min-width: 768px) { + .feedback-panel { + position: fixed; + overflow-y: hidden; + max-height: calc(100vh - 60px); + width: 250px; + top: 50px; + right: 15px; + opacity: .8; + z-index: 10000; + } + + .feedback-panel .alert { + padding: 10px; + margin-bottom: 10px; + overflow: hidden; + } + + .feedback-panel .alert:hover { + border-color: #888; + } } \ No newline at end of file diff --git a/src/main/resources/public/css/conference.css b/src/main/resources/public/css/conference.css index 004fe8c..fa3666e 100644 --- a/src/main/resources/public/css/conference.css +++ b/src/main/resources/public/css/conference.css @@ -2,6 +2,15 @@ body { min-width: 400px; } +.conference-row .col:hover { + background-color: #eaeaea; + border-radius: .25rem; +} + +.filter-option-inner-inner { + color: white; +} + @@ -44,6 +53,8 @@ body { .paper { margin: 0; + min-height: 40px; + height: 40px; } .paper-name { @@ -99,13 +110,14 @@ body { float: right; } -@media (max-width: 1199px) { +@media (max-width: 1199px) and (min-width: 768px){ .paper-control { display: block!important; } } @media (max-width: 991px) { + .dates-panel { display: block!important; } diff --git a/src/main/resources/public/css/tasks.css b/src/main/resources/public/css/tasks.css index 2f42db5..6d18c99 100644 --- a/src/main/resources/public/css/tasks.css +++ b/src/main/resources/public/css/tasks.css @@ -1,24 +1,65 @@ -.bootstrap-tagsinput{ - +.tags-container { width: 100%; padding: .375rem .75rem; + background-color: #fff; + border: 1px solid #ccc; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + display: inline-block; + color: #555; + vertical-align: middle; + border-radius: 4px; + max-width: 100%; + line-height: 22px; + cursor: text; +} + +.input-tag-name { + border: none; + box-shadow: none; + outline: none; + background-color: transparent; + padding: 0 6px; + margin: 0; + width: auto; + max-width: inherit; } -.bootstrap-tagsinput .label{ +.tag { + display: inline-block; + padding: .2em .6em .3em; + background-color: orange; + border-radius: .25em; + margin-right: 4px; + margin-bottom: 4px; + + font-size: 75%; + font-weight: 700; + line-height: 1.5; + color: #fff; +} + +.tag-name span[data-role="remove"] { + margin-left: 8px; + cursor: pointer; +} - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: 700; - line-height: 2.5; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; +.tag-name span[data-role="remove"]:after { + content: "x"; + padding: 0px 2px; } -.bootstrap-tagsinput .label-info{ +.tag-name input[type="text"] { + background: transparent; + border: none; + display: inline-flex; + font-size: 100%; + font-weight: 700; + line-height: 1.5; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + outline: none; + cursor: default; - background-color: orange; } diff --git a/src/main/resources/public/js/conference.js b/src/main/resources/public/js/conference.js index 304daae..dd843ca 100644 --- a/src/main/resources/public/js/conference.js +++ b/src/main/resources/public/js/conference.js @@ -1,27 +1,41 @@ $(document).ready(function () { - $('.data-href-js').click( function() { - window.location = $(this).attr('data-href'); - }); + $(".conference-row").mouseenter(function (event) { + var conferenceRow = $(event.target).closest(".conference-row"); + $(conferenceRow).css("background-color", "#f8f9fa"); + $(conferenceRow).find(".remove-conference").removeClass("d-none"); - $('.circle').parent().click( function() { - $(this).children('.circle').toggleClass('circle-active'); }); - - $('.checkbox-js').parent().click( function() { - $(this).children('.checkbox').toggleClass('selected'); + $(".conference-row").mouseleave(function (event) { + var conferenceRow = $(event.target).closest(".conference-row"); + $(conferenceRow).css("background-color", "white"); + $(conferenceRow).closest(".conference-row").find(".remove-conference").addClass("d-none"); }); - $('#select-all-js').click( function() { - $(this).toggleClass('selected'); + $('a[data-confirm]').click(function(ev) { + var href = $(this).attr('href'); + if (!$('#dataConfirmModal').length) { + $('#modalDelete').append(''); + } + $('#dataConfirmModal').find('#myModalLabel').text($(this).attr('data-confirm')); + $('#dataConfirmOK').attr('href', href); + $('#dataConfirmModal').modal({show:true}); + return false; }); }); diff --git a/src/main/resources/public/js/tasks.js b/src/main/resources/public/js/tasks.js index 6bf160c..b333c31 100644 --- a/src/main/resources/public/js/tasks.js +++ b/src/main/resources/public/js/tasks.js @@ -1,5 +1,75 @@ /*") + .attr("id", 'tags' + tagNumber) + .addClass("tag"); + // контейнер id + var idInput = $("") + .attr("type", "hidden") + .attr("id", "tags" + tagNumber + ".id") + .attr("name", "tags[" + tagNumber + "].id") + .attr("value", ''); + // контейнер текста + var conDiv = $("
") + .addClass("tag-name"); + // текст тега + var nameInput = $("") + .attr("type", "text") + .attr("id", "tags" + tagNumber + ".tagName") + .attr("name", "tags[" + tagNumber + "].tagName") + .attr("value", tagName) + .attr("readonly", "true") + .attr("size", tagName.length); + // кнопка удалить тег + var removeSpan = $("") + .attr("data-role", "remove") + .bind("click", removeTag); + + conDiv.append(nameInput); + conDiv.append(removeSpan); + newTagRow.append(idInput); + newTagRow.append(conDiv); + $(this).before(newTagRow); + } + + $(this).val(''); + } + }); + + $("span[data-role=remove]").click(removeTag); $(".task-row").mouseenter(function (event) { var taskRow = $(event.target).closest(".task-row"); $(taskRow).css("background-color", "#f8f9fa"); diff --git a/src/main/resources/templates/conferences/conference.html b/src/main/resources/templates/conferences/conference.html index c93fc39..27c602a 100644 --- a/src/main/resources/templates/conferences/conference.html +++ b/src/main/resources/templates/conferences/conference.html @@ -60,9 +60,7 @@ - + alt="Удалить" name="removeDeadline" th:value="${rowStat.index}"/>
@@ -136,23 +134,30 @@
- + + - +
@@ -85,8 +113,8 @@
Дата создания:
- - + text
@@ -98,8 +126,8 @@
Дата изменения:
- - + text
@@ -111,8 +139,8 @@ + - \ No newline at end of file diff --git a/src/main/resources/templates/students/tasks.html b/src/main/resources/templates/students/tasks.html index 076f7ba..b1177d8 100644 --- a/src/main/resources/templates/students/tasks.html +++ b/src/main/resources/templates/students/tasks.html @@ -20,8 +20,8 @@
- -
+ +
@@ -37,21 +37,10 @@ -