diff --git a/CHANGELOG.md b/CHANGELOG.md
index f38ff0239e67d106fa4bb78a87c9f49bd1a2cf30..19a7a7f5abce9c9ed6a22323441d87b5a83c8c45 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Button added to switch to dark theme. @rgiedryte
+- Allow filtering by job page by quarter @cedric-dev
### Changed
diff --git a/src/main/java/nl/tudelft/tam/controller/ApplicationController.java b/src/main/java/nl/tudelft/tam/controller/ApplicationController.java
index 87d200f408988493e8ff2c3510721bf7db26759e..66e2a1b6a3fe5258b5b6e4bfb67314e04cb19003 100644
--- a/src/main/java/nl/tudelft/tam/controller/ApplicationController.java
+++ b/src/main/java/nl/tudelft/tam/controller/ApplicationController.java
@@ -30,7 +30,9 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.util.UriComponentsBuilder;
+import nl.tudelft.labracore.api.dto.AcademicPeriodSummaryDTO;
import nl.tudelft.labracore.api.dto.ProgramDetailsDTO;
import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson;
import nl.tudelft.labracore.lib.security.user.Person;
@@ -61,6 +63,9 @@ public class ApplicationController {
@Autowired
ProgramService programService;
+ @Autowired
+ JobOfferService jobOfferService;
+
// endregion
// region view pages
@@ -80,8 +85,8 @@ public class ApplicationController {
@PageableDefault(value = 25) Pageable pageable,
Model model) {
List<ProgramDetailsDTO> programs = programService.getCoordinatingPrograms(person.getId());
- if (filter.getProgram() == null) {
- filter.setProgram(programs.get(0).getId());
+ if (filter.getPrograms() == null) {
+ filter.setPrograms(programs.stream().map(ProgramDetailsDTO::getId).toList());
}
List<Application> applications = applicationService
@@ -89,6 +94,7 @@ public class ApplicationController {
model.addAttribute("applications", PageUtil.toPage(pageable, applications));
model.addAttribute("programs", programs);
+ model.addAttribute("periods", jobOfferService.getAllAcademicPeriods());
return "application/all";
}
@@ -108,8 +114,8 @@ public class ApplicationController {
@PageableDefault(value = 25) Pageable pageable,
Model model) {
List<ProgramDetailsDTO> programs = programService.getCoordinatingPrograms(person.getId());
- if (filter.getProgram() == null) {
- filter.setProgram(programs.get(0).getId());
+ if (filter.getPrograms() == null) {
+ filter.setPrograms(programs.stream().map(ProgramDetailsDTO::getId).toList());
}
filter.setStatuses(List.of(Status.ACCEPTED));
@@ -118,8 +124,9 @@ public class ApplicationController {
model.addAttribute("applications", PageUtil.toPage(pageable, applications));
model.addAttribute("exports",
- applicationService.getExportsForPersonAndProgram(person, filter.getProgram()));
+ applicationService.getExportsForPersonAndPrograms(person, filter.getPrograms()));
model.addAttribute("programs", programs);
+ model.addAttribute("periods", jobOfferService.getAllAcademicPeriods());
return "application/all_flexdelft";
}
@@ -134,13 +141,16 @@ public class ApplicationController {
*/
@GetMapping("my")
public String getApplications(@AuthenticatedPerson Person person,
+ @RequestParam(required = false, name = "period") List<Long> filterPeriods,
@RequestParam(required = false) String q, Model model) {
List<ApplicationDetailsJobOfferDTO> applications = applicationService
- .getFilteredApplicationsForPerson(person.getId(), q)
- .stream().filter(a -> a.getJobOffer().getEdition().getEndDate().isAfter(LocalDateTime.now()))
+ .getFilteredApplicationsForPerson(person.getId(), q).stream()
+ .filter(a -> a.getJobOffer().getEdition().getEndDate().isAfter(LocalDateTime.now()))
+ .filter(a -> filterPeriods == null || a.getJobOffer().getEdition().getPeriod() == null
+ || filterPeriods.contains(a.getJobOffer().getEdition().getPeriod().getId()))
.toList();
-
model.addAttribute("applications", applications);
+ model.addAttribute("periods", jobOfferService.getAllAcademicPeriods());
return "application/view_many";
}
@@ -187,11 +197,20 @@ public class ApplicationController {
@PostMapping("submit")
@PreAuthorize("@authorisationService.canSubmitApplication(#create.jobOffer.id)")
public String submitApplication(@AuthenticatedPerson Person person, ApplicationCreateDTO create,
- @RequestParam(required = false) String page, @RequestParam(required = false) Long program) {
+ @RequestParam(required = false) String page,
+ @RequestParam(required = false) String program,
+ @RequestParam(required = false, name = "period") List<AcademicPeriodSummaryDTO> filterPeriods) {
applicationService.submit(person.getId(), create);
String url = "applications".equals(page) ? "application/my" : "job-offer/open";
- return "redirect:/"
- + (url + (program == null ? "" : "?program=" + program));
+
+ Map<String, List<String>> params = new HashMap<>();
+ if (program != null)
+ params.put("program", List.of(program));
+ if (filterPeriods != null)
+ params.put("period", filterPeriods.stream()
+ .map(AcademicPeriodSummaryDTO::getName).toList());
+
+ return "redirect:/" + url + buildURLParams(params);
}
/**
@@ -206,21 +225,22 @@ public class ApplicationController {
public String retractApplication(@AuthenticatedPerson Person person, @PathVariable Long id,
@RequestParam(required = false) String page,
@RequestParam(required = false) String q,
- @RequestParam(required = false) String tab,
- @RequestParam(required = false) String program) {
+ @RequestParam(required = false) String program,
+ @RequestParam(required = false, name = "period") List<AcademicPeriodSummaryDTO> filterPeriods) {
applicationService.retract(person.getId(), id);
String url = "applications".equals(page) ? "application/my" : "job-offer/open";
- Map<String, String> params = new HashMap<>();
+ Map<String, List<String>> params = new HashMap<>();
if (q != null)
- params.put("q", q);
- if (tab != null)
- params.put("tab", tab);
+ params.put("q", List.of(q));
if (program != null)
- params.put("program", program);
+ params.put("program", List.of(program));
+ if (filterPeriods != null)
+ params.put("period", filterPeriods.stream().map(AcademicPeriodSummaryDTO::getName)
+ .toList());
- return "redirect:/" + url + getURLParamsString(params);
+ return "redirect:/" + url + buildURLParams(params);
}
/**
@@ -324,31 +344,31 @@ public class ApplicationController {
public String acceptApplication(@AuthenticatedPerson Person person, @PathVariable Long id,
@RequestParam(required = false) String page,
@RequestParam(required = false) String q,
- @RequestParam(required = false) String tab,
- @RequestParam(required = false) String program) {
+ @RequestParam(required = false) String program,
+ @RequestParam(required = false) List<AcademicPeriodSummaryDTO> filterPeriods) {
applicationService.accept(person.getId(), id);
String url = "applications".equals(page) ? "application/my" : "job-offer/open";
- Map<String, String> params = new HashMap<>();
+ Map<String, List<String>> params = new HashMap<>();
if (q != null)
- params.put("q", q);
- if (tab != null)
- params.put("tab", tab);
+ params.put("q", List.of(q));
if (program != null)
- params.put("program", program);
+ params.put("program", List.of(program));
+ if (filterPeriods != null)
+ params.put("period", filterPeriods.stream().map(AcademicPeriodSummaryDTO::getName).toList());
- return "redirect:/" + url + getURLParamsString(params);
+ return "redirect:/" + url + buildURLParams(params);
}
- private static String getURLParamsString(Map<String, String> params) {
- StringBuilder result = new StringBuilder();
- char sep = '?';
- for (String param : params.keySet()) {
- result.append(sep).append(param).append("=").append(params.get(param));
- sep = '&';
+ private String buildURLParams(Map<String, List<String>> params) {
+ UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance();
+ for (Map.Entry<String, List<String>> entry : params.entrySet()) {
+ for (String value : entry.getValue()) {
+ uriBuilder.queryParam(entry.getKey(), value);
+ }
}
- return result.toString();
+ return uriBuilder.toUriString();
}
// endregion
@@ -360,7 +380,7 @@ public class ApplicationController {
* @return The export file as a resource
*/
@GetMapping("export")
- @PreAuthorize("@authorisationService.isCoordinatorForProgram(#filter.program)")
+ @PreAuthorize("@authorisationService.isCoordinatorForPrograms(#filter.programs)")
public ResponseEntity<Resource> exportApplications(@AuthenticatedPerson Person person,
ApplicationFilterDTO filter)
throws IOException {
@@ -375,16 +395,18 @@ public class ApplicationController {
* @return The export file as a resource
*/
@GetMapping("flex-delft-export")
- @PreAuthorize("@authorisationService.isCoordinatorForProgram(#filter.program)")
+ @PreAuthorize("@authorisationService.isCoordinatorForPrograms(#filter.programs)")
public ResponseEntity<Resource> exportApplicationsForFlexDelft(@AuthenticatedPerson Person person,
- ApplicationFilterDTO filter, @RequestParam Integer batch)
+ ApplicationFilterDTO filter,
+ @RequestParam Integer batch)
throws IOException {
filter.setStatuses(List.of(Status.ACCEPTED));
- List<Application> appList = applicationService.getFilteredCoordinatingApplications(person.getId(),
- filter);
+ List<Application> appList = applicationService
+ .getFilteredCoordinatingApplications(person.getId(), filter).stream()
+ .toList();
return csvService
.getResponse(
- applicationService.exportApplicationsForFlexDelft(person, filter.getProgram(), batch,
+ applicationService.exportApplicationsForFlexDelft(person, filter.getPrograms(), batch,
"applications", appList, filter.getUpdatedSince()));
}
diff --git a/src/main/java/nl/tudelft/tam/controller/JobOfferController.java b/src/main/java/nl/tudelft/tam/controller/JobOfferController.java
index 5b932159a411076627785dc0c037dec6f41efa34..940e386e106800c43e7306e306237171287a3767 100644
--- a/src/main/java/nl/tudelft/tam/controller/JobOfferController.java
+++ b/src/main/java/nl/tudelft/tam/controller/JobOfferController.java
@@ -129,7 +129,6 @@ public class JobOfferController {
* Gets all open job offers
*
* @param person The currently logged in person
- * @param tab The active tab
* @param q The search query
* @param model The model to add the data to
* @return The job offer page with all open job offers (or all applications) (depends on tab)
@@ -137,7 +136,7 @@ public class JobOfferController {
@GetMapping("open")
public String getOpenJobOffers(@AuthenticatedPerson Person person,
@RequestParam(required = false) Long program,
- @RequestParam(required = false) String tab,
+ @RequestParam(required = false, name = "period") List<Long> filterPeriods,
@RequestParam(required = false) String q, Model model) {
List<ApplicationDetailsJobOfferDTO> applications = applicationService
.getFilteredApplicationsForPerson(person.getId(), q);
@@ -159,7 +158,10 @@ public class JobOfferController {
.collect(Collectors.toSet());
List<JobOfferSummaryDTO> offers = offerDetails.stream()
.filter(o -> coursesInProgram.contains(o.getEdition().getCourse().getId()))
- .map(x -> (JobOfferSummaryDTO) x).collect(Collectors.toList());
+ // Assume that edition without period always gets displayed...
+ .filter(o -> filterPeriods == null || o.getEdition().getPeriod() == null
+ || filterPeriods.contains(o.getEdition().getPeriod().getId()))
+ .map(x -> (JobOfferSummaryDTO) x).toList();
// Sort editions based on Cohort, exclude masters and add them to the end.
Comparator<EditionDetailsOffersWorkDTO> editionComparator = Comparator
@@ -174,13 +176,9 @@ public class JobOfferController {
Map<Long, ApplicationStatusSummaryDTO> appForOffer = applicationService
.getMapWithStatusAppOpenByIdForPerson(
person.getId(),
- offerDetails.stream().map(JobOfferDetailsDTO::getId).collect(Collectors.toList()));
+ offerDetails.stream().map(JobOfferDetailsDTO::getId).toList());
- if (tab == null || !List.of("offers", "applications").contains(tab)) {
- tab = "offers";
- }
-
- model.addAttribute("tab", tab);
+ model.addAttribute("periods", jobOfferService.getAllAcademicPeriods());
model.addAttribute("applications", applications);
model.addAttribute("programs", programsWithJobOffers.stream()
.sorted(Comparator.comparing(ProgramSummaryDTO::getName)).toList());
@@ -502,5 +500,4 @@ public class JobOfferController {
return "redirect:/job-offer/" + id;
}
// endregion
-
}
diff --git a/src/main/java/nl/tudelft/tam/dto/util/ApplicationFilterDTO.java b/src/main/java/nl/tudelft/tam/dto/util/ApplicationFilterDTO.java
index 472b020695aacd11a7ab50de615cedc603568f81..729d50db1c68bc7f7896775d808c9d30fe09ec31 100644
--- a/src/main/java/nl/tudelft/tam/dto/util/ApplicationFilterDTO.java
+++ b/src/main/java/nl/tudelft/tam/dto/util/ApplicationFilterDTO.java
@@ -35,7 +35,7 @@ import nl.tudelft.tam.enums.Status;
@AllArgsConstructor
public class ApplicationFilterDTO {
- private Long program;
+ private List<Long> programs;
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime updatedSince;
@@ -46,6 +46,8 @@ public class ApplicationFilterDTO {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate contractStartBefore;
+ private List<Long> academicPeriods;
+
private List<Status> statuses;
}
diff --git a/src/main/java/nl/tudelft/tam/repository/ApplicationExportRepository.java b/src/main/java/nl/tudelft/tam/repository/ApplicationExportRepository.java
index d012466e1fcd50c31582670680f8ba24550dfb7d..a9bc638090d31b6d2143d606f9b35853cf1480a5 100644
--- a/src/main/java/nl/tudelft/tam/repository/ApplicationExportRepository.java
+++ b/src/main/java/nl/tudelft/tam/repository/ApplicationExportRepository.java
@@ -38,7 +38,6 @@ public interface ApplicationExportRepository extends JpaRepository<ApplicationEx
.orElseThrow(() -> new ResourceNotFoundException("Application Export was not found: " + id));
}
- List<ApplicationExport> findAllByExportedByAndProgramIdOrderByExportAtDesc(Long exportedBy,
- Long programId);
-
+ List<ApplicationExport> findAllByExportedByAndProgramIdInOrderByExportAtDesc(Long exportedBy,
+ List<Long> programIds);
}
diff --git a/src/main/java/nl/tudelft/tam/security/AuthorisationService.java b/src/main/java/nl/tudelft/tam/security/AuthorisationService.java
index 826b02d1632085e382d5abe2d54b4860922786ef..a5c51a1479c119027bacef56f2c9092534e56a83 100644
--- a/src/main/java/nl/tudelft/tam/security/AuthorisationService.java
+++ b/src/main/java/nl/tudelft/tam/security/AuthorisationService.java
@@ -135,6 +135,16 @@ public class AuthorisationService {
.anyMatch(c -> Objects.equals(c.getId(), getAuthPerson().getId()));
}
+ /**
+ * Checks if the person is a coordinator for multiple programs
+ *
+ * @param programIds The ids of the programs
+ * @return true if the authenticated person is a coordinator
+ */
+ public boolean isCoordinatorForPrograms(List<Long> programIds) {
+ return isAdmin() || programIds.stream().allMatch(this::isCoordinatorForProgram);
+ }
+
/**
* Checks if the person can edit the coordinators for a programme
*
diff --git a/src/main/java/nl/tudelft/tam/service/ApplicationService.java b/src/main/java/nl/tudelft/tam/service/ApplicationService.java
index 95bd991d3cd41ee806c6daf585136ba532b463fd..5ba1f5dc0115eac7a5b821c760a02b8bec4b7365 100644
--- a/src/main/java/nl/tudelft/tam/service/ApplicationService.java
+++ b/src/main/java/nl/tudelft/tam/service/ApplicationService.java
@@ -120,6 +120,8 @@ public class ApplicationService {
@Autowired
DTOConverter converter;
+ @Autowired
+ private ProgramService programService;
// endregion
@@ -743,7 +745,7 @@ public class ApplicationService {
*/
public List<Application> getCoordinatingApplications(Long personId) {
List<Long> programIds = coordinatorService.findAllByPersonId(personId).stream()
- .map(Coordinator::getProgramId).collect(Collectors.toList());
+ .map(Coordinator::getProgramId).toList();
return applicationRepository.findAll().stream()
.filter(e -> programIds.contains(courseService
.getOrThrow(
@@ -790,15 +792,15 @@ public class ApplicationService {
}
/**
- * Finds all the exports a person did for a program.
+ * Finds all the exports a person did for a list of programs.
*
* @param person The person
- * @param programId The id of the program
+ * @param programId The ids of the programs
* @return All exports done by the person
*/
- public List<ApplicationExport> getExportsForPersonAndProgram(Person person, Long programId) {
- return applicationExportRepository.findAllByExportedByAndProgramIdOrderByExportAtDesc(person.getId(),
- programId);
+ public List<ApplicationExport> getExportsForPersonAndPrograms(Person person, List<Long> programIds) {
+ return applicationExportRepository
+ .findAllByExportedByAndProgramIdInOrderByExportAtDesc(person.getId(), programIds);
}
/**
@@ -810,8 +812,8 @@ public class ApplicationService {
*/
public List<Application> getFilteredCoordinatingApplications(Long personId,
ApplicationFilterDTO filter) {
- List<Application> applications = getCoordinatingApplicationsForProgram(personId, filter.getProgram())
- .stream()
+ List<Application> applications = filter.getPrograms().stream()
+ .flatMap(programId -> getCoordinatingApplicationsForProgram(personId, programId).stream())
.filter(a -> !editionService.getOrThrow(a.getJobOffer().getEditionId()).getIsArchived())
.toList();
return filterApplications(applications, filter);
@@ -835,6 +837,11 @@ public class ApplicationService {
.filter(app -> filter.getUpdatedSince() == null || !applicationChangeEventRepository
.findByApplicationIdAndChangedAtAfter(app.getId(), filter.getUpdatedSince())
.isEmpty())
+ .filter(a -> filter.getAcademicPeriods() == null ||
+ Optional.ofNullable(
+ editionService.getEditionById(a.getJobOffer().getEditionId()).getPeriod())
+ .map(period -> filter.getAcademicPeriods().contains(period.getId()))
+ .orElse(false))
.toList();
}
@@ -951,7 +958,7 @@ public class ApplicationService {
* Exports a list of applications with FlexDelft specific fields
*
* @param person The person exporting
- * @param programId The program being exported for
+ * @param programIds The programs being exported
* @param batch The batch number
* @param name The name of the file
* @param applications The list of applications
@@ -959,7 +966,8 @@ public class ApplicationService {
* @return The Path to the export file
*/
@Transactional
- public Path exportApplicationsForFlexDelft(Person person, Long programId, Integer batch, String name,
+ public Path exportApplicationsForFlexDelft(Person person, List<Long> programIds, Integer batch,
+ String name,
List<Application> applications, LocalDateTime since)
throws IOException {
if (since != null) {
@@ -967,13 +975,18 @@ public class ApplicationService {
ApplicationFilterDTO.builder().updatedSince(since).build());
}
- applicationExportRepository.save(ApplicationExport.builder().exportedBy(person.getId())
- .programId(programId).batchNumber(batch).build());
+ programIds.forEach(programId -> {
+ applicationExportRepository.save(ApplicationExport.builder()
+ .exportedBy(person.getId())
+ .programId(programId)
+ .batchNumber(batch)
+ .build());
+ });
// Fetch all people and editions to cache and avoid many requests to core
personService.get(applications.stream().map(a -> a.getId().getPersonId()));
editionService.get(
- applications.stream().map(a -> a.getJobOffer().getEditionId()).collect(Collectors.toList()));
+ applications.stream().map(a -> a.getJobOffer().getEditionId()).toList());
String[] headers;
diff --git a/src/main/java/nl/tudelft/tam/service/JobOfferService.java b/src/main/java/nl/tudelft/tam/service/JobOfferService.java
index f042681dc710653217225a1bba3deb6d67c0a55a..756a27fa556402024ac41e19577d24cd8659704f 100644
--- a/src/main/java/nl/tudelft/tam/service/JobOfferService.java
+++ b/src/main/java/nl/tudelft/tam/service/JobOfferService.java
@@ -36,6 +36,7 @@ import org.springframework.web.multipart.MultipartFile;
import com.opencsv.exceptions.CsvValidationException;
import jakarta.validation.constraints.NotNull;
+import nl.tudelft.labracore.api.AcademicPeriodControllerApi;
import nl.tudelft.labracore.api.dto.*;
import nl.tudelft.librador.dto.DTOConverter;
import nl.tudelft.librador.dto.patch.Patch;
@@ -80,6 +81,9 @@ public class JobOfferService {
@Autowired
ProgramService programService;
+ @Autowired
+ AcademicPeriodControllerApi academicPeriodApi;
+
@Autowired
PersonService personService;
@@ -444,4 +448,13 @@ public class JobOfferService {
return roleService.getTAsAndHeadTAsById(id).stream()
.filter(x -> !applicationService.hasApplicationInEdition(x.getPerson().getId(), id)).toList();
}
+
+ /**
+ * Get all academic periods from CORE
+ *
+ * @return all academic periods
+ */
+ public List<AcademicPeriodDetailsDTO> getAllAcademicPeriods() {
+ return academicPeriodApi.getAll().collectList().block();
+ }
}
diff --git a/src/main/resources/templates/application/all.html b/src/main/resources/templates/application/all.html
index de13d3856f28d1d37ed6a672aa29244492739cc5..4020e72951db1a473202d224720a8ecc984b78ae 100644
--- a/src/main/resources/templates/application/all.html
+++ b/src/main/resources/templates/application/all.html
@@ -50,14 +50,38 @@
method="get">
<div class="flex vertical gap-1">
<label for="export-program" th:text="#{jobOffer.export.program}"></label>
- <select class="textfield" id="export-program" name="program" required>
+ <select
+ class="textfield"
+ id="export-program"
+ name="programs"
+ multiple
+ data-select
+ required>
<option
th:each="program : ${programs}"
- th:selected="${program.id.toString() == param.program?.toString()}"
+ th:selected="${param.programs == null
+ || #arrays.contains(#strings.arraySplit(param.programs, ','), program.id.toString())}"
th:value="${program.id}"
th:text="${program.name}"></option>
</select>
</div>
+ <div>
+ <label for="period-select" class="mr-3">Select a period</label>
+ <select
+ id="period-select"
+ class="textfield"
+ name="academicPeriods"
+ multiple
+ data-select>
+ <option
+ th:each="period: ${periods}"
+ th:value="${period.id}"
+ th:text="${period.name}"
+ th:selected="${param.period == null
+ || #arrays.contains(
+ #strings.arraySplit(param.period,','), period.id.toString())}" />
+ </select>
+ </div>
<div class="flex vertical gap-1">
<label
for="contract-start-after"
diff --git a/src/main/resources/templates/application/all_flexdelft.html b/src/main/resources/templates/application/all_flexdelft.html
index 679e0b2dd17e57b394e998d1e5933a78c84a99e2..10498a0711b7002c759fa1b572e9bdd6afa894df 100644
--- a/src/main/resources/templates/application/all_flexdelft.html
+++ b/src/main/resources/templates/application/all_flexdelft.html
@@ -83,14 +83,38 @@
</div>
<div class="flex vertical gap-1">
<label for="export-program" th:text="#{jobOffer.export.program}"></label>
- <select class="textfield" id="export-program" name="program" required>
+ <select
+ class="textfield"
+ id="export-program"
+ name="programs"
+ multiple
+ data-select
+ required>
<option
th:each="program : ${programs}"
- th:selected="${program.id.toString() == param.program?.toString()}"
+ th:selected="${param.programs == null
+ || #arrays.contains(#strings.arraySplit(param.programs, ','), program.id.toString())}"
th:value="${program.id}"
th:text="${program.name}"></option>
</select>
</div>
+ <div class="flex vertical gap-1">
+ <label for="period-select" class="mr-3">Select a period</label>
+ <select
+ id="period-select"
+ class="textfield"
+ name="academicPeriods"
+ multiple
+ data-select>
+ <option
+ th:each="period: ${periods}"
+ th:value="${period.id}"
+ th:text="${period.name}"
+ th:selected="${param.period == null
+ || #arrays.contains(
+ #strings.arraySplit(param.period,','), period.id.toString())}" />
+ </select>
+ </div>
<div class="flex vertical gap-1">
<label
for="export-batch-number"
diff --git a/src/main/resources/templates/application/view_many.html b/src/main/resources/templates/application/view_many.html
index 82831356105f2691279005677ead1ce5e34b7f74..1187a999e1f89917b827a667154b7b6acb97694b 100644
--- a/src/main/resources/templates/application/view_many.html
+++ b/src/main/resources/templates/application/view_many.html
@@ -58,7 +58,20 @@
th:text="#{application.my}"></a>
</div>
- <div>
+ <div class="flex align-end justify-end | md:vertical md:align-stretch">
+ <div>
+ <label for="period-select" class="mr-3">Select a period</label>
+ <select id="period-select" class="textfield" multiple data-select>
+ <option
+ th:each="period: ${periods}"
+ th:value="${period.id}"
+ th:text="${period.name}"
+ th:selected="${param.period == null
+ || param.period.equals(period.id.toString())
+ || #arrays.contains(
+ #strings.arraySplit(param.period,','), period.id.toString())}" />
+ </select>
+ </div>
<form class="search">
<input
name="q"
@@ -175,6 +188,13 @@
window.location = url.toString();
});
+ $("#period-select").change(function () {
+ let url = new URL(window.location);
+ console.log($(this).val());
+ url.searchParams.set("period", $(this).val());
+ window.location = url.toString();
+ });
+
$("#search-form").submit(function (event) {
event.preventDefault();
let url = new URL(window.location);
diff --git a/src/main/resources/templates/job_offer/view_many.html b/src/main/resources/templates/job_offer/view_many.html
index b36fe3a2c9882630b7b1d563f278080a68fbe91e..ec533f7b8c73025c2c4ee51aad722f46ead21b53 100644
--- a/src/main/resources/templates/job_offer/view_many.html
+++ b/src/main/resources/templates/job_offer/view_many.html
@@ -47,7 +47,7 @@
th:text="#{application.my}"></a>
</div>
- <div class="flex space-between align-center | md:vertical md:align-stretch">
+ <div class="flex space-between align-end | md:vertical md:align-stretch">
<div>
<label
for="program-select"
@@ -64,20 +64,32 @@
th:text="${program.name}"></option>
</select>
</div>
-
- <form class="search" id="search-form">
- <input
- name="q"
- id="job-offer-search"
- th:aria-label="#{general.search}"
- type="search"
- th:value="${param.q}"
- th:placeholder="#{jobOffer.search}" />
- <button
- class="fa-solid fa-search"
- th:aria-label="#{general.search}"
- type="submit"></button>
- </form>
+ <div class="flex space-between align-end">
+ <div>
+ <label for="period-select" class="mr-3">Select a period</label>
+ <select id="period-select" class="textfield" multiple data-select>
+ <option
+ th:each="period: ${periods}"
+ th:value="${period.id}"
+ th:text="${period.name}"
+ th:selected="${param.period == null
+ || #arrays.contains(#strings.arraySplit(param.period,','), period.id.toString())}" />
+ </select>
+ </div>
+ <form class="search" id="search-form">
+ <input
+ name="q"
+ id="job-offer-search"
+ th:aria-label="#{general.search}"
+ type="search"
+ th:value="${param.q}"
+ th:placeholder="#{jobOffer.search}" />
+ <button
+ class="fa-solid fa-search"
+ th:aria-label="#{general.search}"
+ type="submit"></button>
+ </form>
+ </div>
</div>
<table
@@ -291,6 +303,13 @@
window.location = url.toString();
});
+ $("#period-select").change(function () {
+ let url = new URL(window.location);
+ console.log($(this).val());
+ url.searchParams.set("period", $(this).val());
+ window.location = url.toString();
+ });
+
$("#search-form").submit(function (event) {
event.preventDefault();
let url = new URL(window.location);
diff --git a/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java b/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java
index 2434d7b58ef67fa1c3748257f2d9b5f1c5df70d9..9319f2789eb78e8f40a2dd08c780f441cb0dde19 100644
--- a/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java
+++ b/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java
@@ -758,7 +758,7 @@ class ApplicationServiceMockTest {
@SuppressWarnings("unchecked") ArgumentCaptor<List<String[]>> captor = ArgumentCaptor
.forClass(List.class);
- service.exportApplicationsForFlexDelft(exporter, PROGRAM_ID, 1, name, applications, null);
+ service.exportApplicationsForFlexDelft(exporter, List.of(PROGRAM_ID), 1, name, applications, null);
verify(csvService).writeToCSV(captor.capture(), any(), eq(name + ".csv"));
assertThat(captor.getValue())
diff --git a/src/test/java/nl/tudelft/tam/service/JobOfferServiceTest.java b/src/test/java/nl/tudelft/tam/service/JobOfferServiceTest.java
index fa0605417c477563406a66c997865d6b324c5794..d28f728b5db0760cec56d4a40bef9266b9777dfb 100644
--- a/src/test/java/nl/tudelft/tam/service/JobOfferServiceTest.java
+++ b/src/test/java/nl/tudelft/tam/service/JobOfferServiceTest.java
@@ -39,6 +39,7 @@ import org.springframework.mock.web.MockMultipartFile;
import org.springframework.transaction.annotation.Transactional;
import application.test.TestTAMApplication;
+import nl.tudelft.labracore.api.AcademicPeriodControllerApi;
import nl.tudelft.labracore.api.dto.*;
import nl.tudelft.librador.dto.DTOConverter;
import nl.tudelft.tam.dto.create.JobOfferCreateDTO;
@@ -52,6 +53,7 @@ import nl.tudelft.tam.model.Application;
import nl.tudelft.tam.model.JobOffer;
import nl.tudelft.tam.repository.JobOfferRepository;
import nl.tudelft.tam.security.AuthorisationService;
+import reactor.core.publisher.Flux;
@SpringBootTest(classes = TestTAMApplication.class)
@Transactional
@@ -94,6 +96,10 @@ class JobOfferServiceTest {
@MockBean
private RoleService roleService;
+ @MockBean
+ private AcademicPeriodControllerApi academicPeriodControllerApi;
+ @Autowired
+ private DTOConverter dtoMapper;
@BeforeEach
void setUp() {
@@ -561,4 +567,14 @@ class JobOfferServiceTest {
verify(applicationService).hasApplicationInEdition(3L, editionId);
}
+ @Test
+ void getAllAcademicPeriodTest() {
+ var originalPeriods = List.of(new AcademicPeriodDetailsDTO().id(1L));
+ when(academicPeriodControllerApi.getAll()).thenReturn(Flux.fromIterable(originalPeriods));
+
+ var period = service.getAllAcademicPeriods();
+
+ assertThat(originalPeriods).isEqualTo(period);
+
+ }
}