diff --git a/backend/src/main/java/com/openisle/controller/TagController.java b/backend/src/main/java/com/openisle/controller/TagController.java index b5a388849..58161359b 100644 --- a/backend/src/main/java/com/openisle/controller/TagController.java +++ b/backend/src/main/java/com/openisle/controller/TagController.java @@ -80,7 +80,18 @@ public class TagController { @ApiResponse(responseCode = "200", description = "List of tags", content = @Content(array = @ArraySchema(schema = @Schema(implementation = TagDto.class)))) public List list(@RequestParam(value = "keyword", required = false) String keyword, - @RequestParam(value = "limit", required = false) Integer limit) { + @RequestParam(value = "limit", required = false) Integer limit, + @RequestParam(value = "page", required = false) Integer page, + @RequestParam(value = "pageSize", required = false) Integer pageSize) { + if (page != null && pageSize != null) { + var tagPage = tagService.searchTags(keyword, page, pageSize); + List tags = tagPage.getContent(); + List tagIds = tags.stream().map(Tag::getId).toList(); + Map postCntByTagIds = postService.countPostsByTagIds(tagIds); + return tags.stream() + .map(t -> tagMapper.toDto(t, postCntByTagIds.getOrDefault(t.getId(), 0L))) + .collect(Collectors.toList()); + } List tags = tagService.searchTags(keyword); List tagIds = tags.stream().map(Tag::getId).toList(); Map postCntByTagIds = postService.countPostsByTagIds(tagIds); diff --git a/backend/src/main/java/com/openisle/repository/TagRepository.java b/backend/src/main/java/com/openisle/repository/TagRepository.java index 1e2868437..c89da035c 100644 --- a/backend/src/main/java/com/openisle/repository/TagRepository.java +++ b/backend/src/main/java/com/openisle/repository/TagRepository.java @@ -3,6 +3,7 @@ package com.openisle.repository; import com.openisle.model.Tag; import com.openisle.model.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import java.util.List; @@ -14,6 +15,9 @@ public interface TagRepository extends JpaRepository { List findByApprovedTrue(); List findByNameContainingIgnoreCaseAndApprovedTrue(String keyword); + Page findByApprovedTrue(Pageable pageable); + Page findByNameContainingIgnoreCaseAndApprovedTrue(String keyword, Pageable pageable); + List findByCreatorOrderByCreatedAtDesc(User creator, Pageable pageable); List findByCreator(User creator); diff --git a/backend/src/main/java/com/openisle/service/TagService.java b/backend/src/main/java/com/openisle/service/TagService.java index eee84121e..b8089d76f 100644 --- a/backend/src/main/java/com/openisle/service/TagService.java +++ b/backend/src/main/java/com/openisle/service/TagService.java @@ -108,6 +108,14 @@ public class TagService { return tagRepository.findByNameContainingIgnoreCaseAndApprovedTrue(keyword); } + public org.springframework.data.domain.Page searchTags(String keyword, int page, int pageSize) { + Pageable pageable = PageRequest.of(page, pageSize); + if (keyword == null || keyword.isBlank()) { + return tagRepository.findByApprovedTrue(pageable); + } + return tagRepository.findByNameContainingIgnoreCaseAndApprovedTrue(keyword, pageable); + } + public List getRecentTagsByUser(String username, int limit) { User user = userRepository.findByUsername(username) .orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found")); diff --git a/backend/src/test/java/com/openisle/controller/TagControllerTest.java b/backend/src/test/java/com/openisle/controller/TagControllerTest.java index 69c8be30b..1d00422e1 100644 --- a/backend/src/test/java/com/openisle/controller/TagControllerTest.java +++ b/backend/src/test/java/com/openisle/controller/TagControllerTest.java @@ -18,6 +18,8 @@ import org.springframework.test.web.servlet.MockMvc; import java.util.List; +import org.springframework.data.domain.PageImpl; + import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; @@ -86,6 +88,21 @@ class TagControllerTest { .andExpect(jsonPath("$[0].smallIcon").value("s2")); } + @Test + void listTagsWithPagination() throws Exception { + Tag t = new Tag(); + t.setId(4L); + t.setName("tag4"); + t.setDescription("d4"); + t.setIcon("i4"); + t.setSmallIcon("s4"); + Mockito.when(tagService.searchTags(null, 0, 1)).thenReturn(new PageImpl<>(List.of(t))); + + mockMvc.perform(get("/api/tags?page=0&pageSize=1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].name").value("tag4")); + } + @Test void updateTag() throws Exception { Tag t = new Tag(); diff --git a/frontend_nuxt/components/Dropdown.vue b/frontend_nuxt/components/Dropdown.vue index 6cc680874..d2a8ebc36 100644 --- a/frontend_nuxt/components/Dropdown.vue +++ b/frontend_nuxt/components/Dropdown.vue @@ -80,6 +80,12 @@ {{ o.name }} + @@ -116,6 +122,12 @@ {{ o.name }} + @@ -126,9 +138,11 @@