diff --git a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java index 41b99619e..d48dfdea4 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java @@ -15,9 +15,11 @@ */ package org.springframework.samples.petclinic.owner; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.IntStream; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -51,6 +53,12 @@ class OwnerController { private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm"; + private static final String DEFAULT_PAGE_SIZE_VALUE = "10"; + + private static final int DEFAULT_PAGE_SIZE = Integer.parseInt(DEFAULT_PAGE_SIZE_VALUE); + + private static final List PAGE_SIZE_OPTIONS = List.of(DEFAULT_PAGE_SIZE, 20, 30, 40, 50); + private final OwnerRepository owners; public OwnerController(OwnerRepository owners) { @@ -94,15 +102,20 @@ class OwnerController { @GetMapping("/owners") public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result, - Model model) { + Model model, @RequestParam(name = "size", defaultValue = DEFAULT_PAGE_SIZE_VALUE) int size) { // allow parameterless GET request for /owners to return all records String lastName = owner.getLastName(); if (lastName == null) { lastName = ""; // empty string signifies broadest possible search } + owner.setLastName(lastName); + + int resolvedPage = Math.max(page, 1); + int pageSize = resolvePageSize(size); + // find owners by last name - Page ownersResults = findPaginatedForOwnersLastName(page, lastName); + Page ownersResults = findPaginatedForOwnersLastName(resolvedPage, pageSize, lastName); if (ownersResults.isEmpty()) { // no owners found result.rejectValue("lastName", "notFound", "not found"); @@ -116,24 +129,56 @@ class OwnerController { } // multiple owners found - return addPaginationModel(page, model, ownersResults); + return addPaginationModel(resolvedPage, pageSize, owner, model, ownersResults); } - private String addPaginationModel(int page, Model model, Page paginated) { + private String addPaginationModel(int page, int pageSize, Owner owner, Model model, Page paginated) { List listOwners = paginated.getContent(); + long totalItems = paginated.getTotalElements(); + int currentItemCount = paginated.getNumberOfElements(); + long startItem = totalItems == 0 ? 0L : (long) ((page - 1L) * pageSize) + 1L; + long endItem = currentItemCount == 0 ? startItem : Math.min(startItem + currentItemCount - 1L, totalItems); + int totalPages = paginated.getTotalPages(); + int windowSize = 10; + int windowStart = 1; + int windowEnd = 0; + List pageNumbers = Collections.emptyList(); + boolean showLeadingGap = false; + boolean showTrailingGap = false; + if (totalPages > 0) { + int maxWindowStart = Math.max(1, totalPages - windowSize + 1); + windowStart = Math.max(1, Math.min(page, maxWindowStart)); + windowEnd = Math.min(windowStart + windowSize - 1, totalPages); + pageNumbers = IntStream.rangeClosed(windowStart, windowEnd).boxed().toList(); + int leadingHiddenCount = windowStart - 1; + int trailingHiddenCount = totalPages - windowEnd; + showLeadingGap = leadingHiddenCount > 0; + showTrailingGap = trailingHiddenCount > 0; + } model.addAttribute("currentPage", page); - model.addAttribute("totalPages", paginated.getTotalPages()); - model.addAttribute("totalItems", paginated.getTotalElements()); + model.addAttribute("pageSize", pageSize); + model.addAttribute("pageSizeOptions", PAGE_SIZE_OPTIONS); + model.addAttribute("totalPages", totalPages); + model.addAttribute("totalItems", totalItems); + model.addAttribute("startItem", startItem); + model.addAttribute("endItem", endItem); model.addAttribute("listOwners", listOwners); + model.addAttribute("owner", owner); + model.addAttribute("pageNumbers", pageNumbers); + model.addAttribute("showLeadingGap", showLeadingGap); + model.addAttribute("showTrailingGap", showTrailingGap); return "owners/ownersList"; } - private Page findPaginatedForOwnersLastName(int page, String lastname) { - int pageSize = 5; + private Page findPaginatedForOwnersLastName(int page, int pageSize, String lastname) { Pageable pageable = PageRequest.of(page - 1, pageSize); return owners.findByLastNameStartingWith(lastname, pageable); } + private int resolvePageSize(int requestedSize) { + return PAGE_SIZE_OPTIONS.contains(requestedSize) ? requestedSize : DEFAULT_PAGE_SIZE; + } + @GetMapping("/owners/{ownerId}/edit") public String initUpdateOwnerForm() { return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java index 89ad9bc41..415dfd304 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java @@ -15,7 +15,9 @@ */ package org.springframework.samples.petclinic.vet; +import java.util.Collections; import java.util.List; +import java.util.stream.IntStream; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -35,6 +37,14 @@ import org.springframework.web.bind.annotation.ResponseBody; @Controller class VetController { + private static final String DEFAULT_PAGE_SIZE_VALUE = "10"; + + private static final int DEFAULT_PAGE_SIZE = Integer.parseInt(DEFAULT_PAGE_SIZE_VALUE); + + private static final List PAGE_SIZE_OPTIONS = List.of(DEFAULT_PAGE_SIZE, 20, 30, 40, 50); + + private static final int PAGINATION_WINDOW_SIZE = 10; + private final VetRepository vetRepository; public VetController(VetRepository vetRepository) { @@ -42,30 +52,63 @@ class VetController { } @GetMapping("/vets.html") - public String showVetList(@RequestParam(defaultValue = "1") int page, Model model) { + public String showVetList(@RequestParam(defaultValue = "1") int page, Model model, + @RequestParam(name = "size", defaultValue = DEFAULT_PAGE_SIZE_VALUE) int size) { // Here we are returning an object of type 'Vets' rather than a collection of Vet // objects so it is simpler for Object-Xml mapping Vets vets = new Vets(); - Page paginated = findPaginated(page); + int resolvedPage = Math.max(page, 1); + int pageSize = resolvePageSize(size); + Page paginated = findPaginated(resolvedPage, pageSize); vets.getVetList().addAll(paginated.toList()); - return addPaginationModel(page, paginated, model); + return addPaginationModel(resolvedPage, pageSize, paginated, model); } - private String addPaginationModel(int page, Page paginated, Model model) { + private String addPaginationModel(int page, int pageSize, Page paginated, Model model) { List listVets = paginated.getContent(); + long totalItems = paginated.getTotalElements(); + int currentItemCount = paginated.getNumberOfElements(); + long startItem = totalItems == 0 ? 0L : (long) ((page - 1L) * pageSize) + 1L; + long endItem = currentItemCount == 0 ? startItem : Math.min(startItem + currentItemCount - 1L, totalItems); + int totalPages = paginated.getTotalPages(); + int windowStart = 1; + int windowEnd = 0; + List pageNumbers = Collections.emptyList(); + boolean showLeadingGap = false; + boolean showTrailingGap = false; + if (totalPages > 0) { + int maxWindowStart = Math.max(1, totalPages - PAGINATION_WINDOW_SIZE + 1); + windowStart = Math.max(1, Math.min(page, maxWindowStart)); + windowEnd = Math.min(windowStart + PAGINATION_WINDOW_SIZE - 1, totalPages); + pageNumbers = IntStream.rangeClosed(windowStart, windowEnd).boxed().toList(); + int leadingHiddenCount = windowStart - 1; + int trailingHiddenCount = totalPages - windowEnd; + showLeadingGap = leadingHiddenCount > 0; + showTrailingGap = trailingHiddenCount > 0; + } model.addAttribute("currentPage", page); - model.addAttribute("totalPages", paginated.getTotalPages()); - model.addAttribute("totalItems", paginated.getTotalElements()); + model.addAttribute("pageSize", pageSize); + model.addAttribute("pageSizeOptions", PAGE_SIZE_OPTIONS); + model.addAttribute("totalPages", totalPages); + model.addAttribute("totalItems", totalItems); + model.addAttribute("startItem", startItem); + model.addAttribute("endItem", endItem); model.addAttribute("listVets", listVets); + model.addAttribute("pageNumbers", pageNumbers); + model.addAttribute("showLeadingGap", showLeadingGap); + model.addAttribute("showTrailingGap", showTrailingGap); return "vets/vetList"; } - private Page findPaginated(int page) { - int pageSize = 5; + private Page findPaginated(int page, int pageSize) { Pageable pageable = PageRequest.of(page - 1, pageSize); return vetRepository.findAll(pageable); } + private int resolvePageSize(int requestedSize) { + return PAGE_SIZE_OPTIONS.contains(requestedSize) ? requestedSize : DEFAULT_PAGE_SIZE; + } + @GetMapping({ "/vets" }) public @ResponseBody Vets showResourcesVetList() { // Here we are returning an object of type 'Vets' rather than a collection of Vet diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 193565895..175b63faa 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -13,6 +13,10 @@ address=Address city=City telephone=Telephone owners=Owners +owners.pageSize.label=Page Row +owners.pageSize.submit=Apply +owners.pagination.summary=Showing {0}-{1} of {2} +owners.pagination.ellipsis=... addOwner=Add Owner findOwner=Find Owner findOwners=Find Owners diff --git a/src/main/resources/messages/messages_de.properties b/src/main/resources/messages/messages_de.properties index 89a08eaad..1f43abdb9 100644 --- a/src/main/resources/messages/messages_de.properties +++ b/src/main/resources/messages/messages_de.properties @@ -49,3 +49,9 @@ petsAndVisits=Haustiere und Besuche error.404=Die angeforderte Seite wurde nicht gefunden. error.500=Ein interner Serverfehler ist aufgetreten. error.general=Ein unerwarteter Fehler ist aufgetreten. + +owners.pageSize.label=Page Row +owners.pageSize.submit=Apply +owners.pagination.summary=Showing {0}-{1} of {2} + +owners.pagination.ellipsis=... diff --git a/src/main/resources/messages/messages_es.properties b/src/main/resources/messages/messages_es.properties index 911d7337f..b6ccb929d 100644 --- a/src/main/resources/messages/messages_es.properties +++ b/src/main/resources/messages/messages_es.properties @@ -49,3 +49,9 @@ petsAndVisits=Mascotas y visitas error.404=La página solicitada no fue encontrada. error.500=Ocurrió un error interno del servidor. error.general=Ocurrió un error inesperado. + +owners.pageSize.label=Page Row +owners.pageSize.submit=Apply +owners.pagination.summary=Showing {0}-{1} of {2} + +owners.pagination.ellipsis=... diff --git a/src/main/resources/messages/messages_fa.properties b/src/main/resources/messages/messages_fa.properties index 6d0994a92..9add5dd23 100644 --- a/src/main/resources/messages/messages_fa.properties +++ b/src/main/resources/messages/messages_fa.properties @@ -49,3 +49,9 @@ petsAndVisits=حیوانات و ویزیت‌ها error.404=صفحه درخواستی پیدا نشد. error.500=خطای داخلی سرور رخ داد. error.general=خطای غیرمنتظره‌ای رخ داد. + +owners.pageSize.label=Page Row +owners.pageSize.submit=Apply +owners.pagination.summary=Showing {0}-{1} of {2} + +owners.pagination.ellipsis=... diff --git a/src/main/resources/messages/messages_ko.properties b/src/main/resources/messages/messages_ko.properties index 6e2f4880a..bcda548d4 100644 --- a/src/main/resources/messages/messages_ko.properties +++ b/src/main/resources/messages/messages_ko.properties @@ -49,3 +49,9 @@ petsAndVisits=반려동물 및 방문 error.404=요청하신 페이지를 찾을 수 없습니다. error.500=서버 내부 오류가 발생했습니다. error.general=알 수 없는 오류가 발생했습니다. + +owners.pageSize.label=Page Row +owners.pageSize.submit=Apply +owners.pagination.summary=Showing {0}-{1} of {2} + +owners.pagination.ellipsis=... diff --git a/src/main/resources/messages/messages_pt.properties b/src/main/resources/messages/messages_pt.properties index 7eea4b9d1..01764bdad 100644 --- a/src/main/resources/messages/messages_pt.properties +++ b/src/main/resources/messages/messages_pt.properties @@ -49,3 +49,9 @@ petsAndVisits=Animais e visitas error.404=A página solicitada não foi encontrada. error.500=Ocorreu um erro interno no servidor. error.general=Ocorreu um erro inesperado. + +owners.pageSize.label=Page Row +owners.pageSize.submit=Apply +owners.pagination.summary=Showing {0}-{1} of {2} + +owners.pagination.ellipsis=... diff --git a/src/main/resources/messages/messages_ru.properties b/src/main/resources/messages/messages_ru.properties index f06d2cb6c..c3ed82619 100644 --- a/src/main/resources/messages/messages_ru.properties +++ b/src/main/resources/messages/messages_ru.properties @@ -49,3 +49,9 @@ petsAndVisits=Питомцы и визиты error.404=Запрашиваемая страница не найдена. error.500=Произошла внутренняя ошибка сервера. error.general=Произошла непредвиденная ошибка. + +owners.pageSize.label=Page Row +owners.pageSize.submit=Apply +owners.pagination.summary=Showing {0}-{1} of {2} + +owners.pagination.ellipsis=... diff --git a/src/main/resources/messages/messages_tr.properties b/src/main/resources/messages/messages_tr.properties index 2c806f9e6..3a14ad1de 100644 --- a/src/main/resources/messages/messages_tr.properties +++ b/src/main/resources/messages/messages_tr.properties @@ -49,3 +49,9 @@ petsAndVisits=Evcil Hayvanlar ve Ziyaretler error.404=İstenen sayfa bulunamadı. error.500=Sunucuda dahili bir hata oluştu. error.general=Beklenmeyen bir hata oluştu. + +owners.pageSize.label=Page Row +owners.pageSize.submit=Apply +owners.pagination.summary=Showing {0}-{1} of {2} + +owners.pagination.ellipsis=... diff --git a/src/main/resources/templates/owners/ownersList.html b/src/main/resources/templates/owners/ownersList.html index 01223c1c5..f3a8c24e2 100644 --- a/src/main/resources/templates/owners/ownersList.html +++ b/src/main/resources/templates/owners/ownersList.html @@ -6,6 +6,23 @@

Owners

+
+
Showing 1-10 of 42
+
+ + + + + +
+
+ @@ -28,34 +45,56 @@
-
- Pages: - [ - - [[${i}]] - [[${i}]] - - - - - - - - - - - - - - - - - - +
+
+ Showing 1-10 of 42 +
+
- \ No newline at end of file + diff --git a/src/main/resources/templates/vets/vetList.html b/src/main/resources/templates/vets/vetList.html index e40fd654e..3a93dc881 100644 --- a/src/main/resources/templates/vets/vetList.html +++ b/src/main/resources/templates/vets/vetList.html @@ -6,6 +6,22 @@

Veterinarians

+
+
Showing 1-10 of 42
+
+ + + + +
+
+ @@ -23,35 +39,53 @@
-
- Pages: - [ - - [[${i}]] - [[${i}]] - - - - - - - - - - - - - - - - - - +
+
+ Showing 1-10 of 42 +
+
- \ No newline at end of file +