diff --git a/frontend/src/app.tsx b/frontend/src/app.tsx index 58f6d78..19be6b8 100644 --- a/frontend/src/app.tsx +++ b/frontend/src/app.tsx @@ -8,6 +8,7 @@ import SearchPage from "./pages/search_page.tsx"; import ResourcePage from "./pages/resource_details_page.tsx"; import "./i18n.ts" import ManagePage from "./pages/manage_page.tsx"; +import TaggedResourcesPage from "./pages/tagged_resources_page.tsx"; export default function App() { return ( @@ -21,6 +22,7 @@ export default function App() { } /> }/> }/> + }/> diff --git a/frontend/src/components/resources_view.tsx b/frontend/src/components/resources_view.tsx index b6da687..f281a15 100644 --- a/frontend/src/components/resources_view.tsx +++ b/frontend/src/components/resources_view.tsx @@ -17,7 +17,7 @@ export default function ResourcesView({loader}: {loader: (page: number) => Promi isLoadingRef.current = true const res = await loader(pageRef.current) if (!res.success) { - showToast({message: "Error loading resources", type: "error"}) + showToast({message: res.message, type: "error"}) } else { isLoadingRef.current = false pageRef.current = pageRef.current + 1 diff --git a/frontend/src/index.css b/frontend/src/index.css index 1fef2ae..c5a830b 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -124,4 +124,9 @@ body { 100% { top: -100%; } +} + +.text-md { + font-size: 1rem; + line-height: 1.5rem; } \ No newline at end of file diff --git a/frontend/src/network/network.ts b/frontend/src/network/network.ts index 54d3625..296ed87 100644 --- a/frontend/src/network/network.ts +++ b/frontend/src/network/network.ts @@ -308,6 +308,23 @@ class Network { } } + async getResourcesByTag(tag: string, page: number): Promise> { + try { + const response = await axios.get(`${this.apiBaseUrl}/resource/tag/${tag}`, { + params: { + page + } + }) + return response.data + } catch (e: any) { + console.error(e) + return { + success: false, + message: e.toString(), + } + } + } + async searchResources(keyword: string, page: number): Promise> { try { const response = await axios.get(`${this.apiBaseUrl}/resource/search`, { diff --git a/frontend/src/pages/resource_details_page.tsx b/frontend/src/pages/resource_details_page.tsx index c728176..e41ae27 100644 --- a/frontend/src/pages/resource_details_page.tsx +++ b/frontend/src/pages/resource_details_page.tsx @@ -1,4 +1,4 @@ -import {useParams} from "react-router"; +import {useNavigate, useParams} from "react-router"; import {createContext, useCallback, useContext, useEffect, useRef, useState} from "react"; import {ResourceDetails, RFile, Storage, Comment} from "../network/models.ts"; import {network} from "../network/network.ts"; @@ -49,6 +49,8 @@ export default function ResourcePage() { } }, [id]) + const navigate = useNavigate() + if (isNaN(id)) { return
@@ -84,7 +86,9 @@ export default function ResourcePage() {

{ resource.tags.map((e) => { - return {e.name} + return { + navigate(`/tag/${e.name}`); + }}>{e.name} }) }

diff --git a/frontend/src/pages/tagged_resources_page.tsx b/frontend/src/pages/tagged_resources_page.tsx new file mode 100644 index 0000000..5f3502a --- /dev/null +++ b/frontend/src/pages/tagged_resources_page.tsx @@ -0,0 +1,23 @@ +import {useParams} from "react-router"; +import {ErrorAlert} from "../components/alert.tsx"; +import ResourcesView from "../components/resources_view.tsx"; +import {network} from "../network/network.ts"; + +export default function TaggedResourcesPage() { + const {tag} = useParams() + + if (!tag) { + return
+ +
+ } + + return
+

+ {tag} +

+ { + return network.getResourcesByTag(tag, page) + }}> +
+} \ No newline at end of file diff --git a/server/api/resource.go b/server/api/resource.go index 3244eea..5a5c051 100644 --- a/server/api/resource.go +++ b/server/api/resource.go @@ -99,6 +99,34 @@ func handleListResources(c fiber.Ctx) error { }) } +func handleListResourcesWithTag(c fiber.Ctx) error { + tag := c.Params("tag") + if tag == "" { + return model.NewRequestError("Tag is required") + } + pageStr := c.Query("page") + if pageStr == "" { + pageStr = "1" + } + page, err := strconv.Atoi(pageStr) + if err != nil { + return model.NewRequestError("Invalid page number") + } + resources, totalPages, err := service.GetResourcesWithTag(tag, page) + if err != nil { + return err + } + if resources == nil { + resources = []model.ResourceView{} + } + return c.Status(fiber.StatusOK).JSON(model.PageResponse[model.ResourceView]{ + Success: true, + Data: resources, + TotalPages: totalPages, + Message: "Resources retrieved successfully", + }) +} + func handleSearchResources(c fiber.Ctx) error { query := c.Query("keyword") if query == "" { @@ -135,5 +163,6 @@ func AddResourceRoutes(api fiber.Router) { resource.Get("/", handleListResources) resource.Get("/:id", handleGetResource) resource.Delete("/:id", handleDeleteResource) + resource.Get("/tag/:tag", handleListResourcesWithTag) } } diff --git a/server/dao/resource.go b/server/dao/resource.go index 390cb8c..d2ae529 100644 --- a/server/dao/resource.go +++ b/server/dao/resource.go @@ -41,7 +41,7 @@ func GetResourceList(page, pageSize int) ([]model.Resource, int, error) { return nil, 0, err } - totalPages := int(total) / pageSize + totalPages := (total + int64(pageSize) - 1) / int64(pageSize) return resources, int(totalPages), nil } @@ -110,7 +110,7 @@ func Search(query string, page, pageSize int) ([]model.Resource, int, error) { if endIndex > len(resource) { endIndex = len(resource) } - totalPages := len(resource) / pageSize + totalPages := (len(resource) + pageSize - 1) / pageSize result := make([]model.Resource, 0, endIndex-startIndex) for i := startIndex; i < endIndex; i++ { @@ -157,13 +157,13 @@ func GetResourceByTag(tagID uint, page int, pageSize int) ([]model.Resource, int total = db.Model(&model.Tag{}).Where("id = ?", tagID).Association("Resources").Count() - if err := db.Model(&model.Tag{}).Where("id = ?", tagID).Preload("User").Preload("Resources", func(tx *gorm.DB) *gorm.DB { - return tx.Offset((page - 1) * pageSize).Limit(pageSize) + if err := db.Model(&model.Tag{}).Where("id = ?", tagID).Preload("Resources", func(tx *gorm.DB) *gorm.DB { + return tx.Offset((page - 1) * pageSize).Limit(pageSize).Preload("Tags").Preload("User").Preload("Images").Order("created_at DESC") }).First(&tag).Error; err != nil { return nil, 0, err } - totalPages := int(total) / pageSize + totalPages := (int(total) + pageSize - 1) / pageSize return tag.Resources, totalPages, nil }