mirror of
https://github.com/wgh136/nysoure.git
synced 2025-09-27 12:17:24 +00:00
Add tagged resources page.
This commit is contained in:
@@ -8,6 +8,7 @@ import SearchPage from "./pages/search_page.tsx";
|
|||||||
import ResourcePage from "./pages/resource_details_page.tsx";
|
import ResourcePage from "./pages/resource_details_page.tsx";
|
||||||
import "./i18n.ts"
|
import "./i18n.ts"
|
||||||
import ManagePage from "./pages/manage_page.tsx";
|
import ManagePage from "./pages/manage_page.tsx";
|
||||||
|
import TaggedResourcesPage from "./pages/tagged_resources_page.tsx";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
@@ -21,6 +22,7 @@ export default function App() {
|
|||||||
<Route path={"/search"} element={<SearchPage/>} />
|
<Route path={"/search"} element={<SearchPage/>} />
|
||||||
<Route path={"/resources/:id"} element={<ResourcePage/>}/>
|
<Route path={"/resources/:id"} element={<ResourcePage/>}/>
|
||||||
<Route path={"/manage"} element={<ManagePage/>}/>
|
<Route path={"/manage"} element={<ManagePage/>}/>
|
||||||
|
<Route path={"/tag/:tag"} element={<TaggedResourcesPage/>}/>
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
@@ -17,7 +17,7 @@ export default function ResourcesView({loader}: {loader: (page: number) => Promi
|
|||||||
isLoadingRef.current = true
|
isLoadingRef.current = true
|
||||||
const res = await loader(pageRef.current)
|
const res = await loader(pageRef.current)
|
||||||
if (!res.success) {
|
if (!res.success) {
|
||||||
showToast({message: "Error loading resources", type: "error"})
|
showToast({message: res.message, type: "error"})
|
||||||
} else {
|
} else {
|
||||||
isLoadingRef.current = false
|
isLoadingRef.current = false
|
||||||
pageRef.current = pageRef.current + 1
|
pageRef.current = pageRef.current + 1
|
||||||
|
@@ -125,3 +125,8 @@ body {
|
|||||||
top: -100%;
|
top: -100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-md {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
}
|
@@ -308,6 +308,23 @@ class Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getResourcesByTag(tag: string, page: number): Promise<PageResponse<Resource>> {
|
||||||
|
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<PageResponse<Resource>> {
|
async searchResources(keyword: string, page: number): Promise<PageResponse<Resource>> {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`${this.apiBaseUrl}/resource/search`, {
|
const response = await axios.get(`${this.apiBaseUrl}/resource/search`, {
|
||||||
|
@@ -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 {createContext, useCallback, useContext, useEffect, useRef, useState} from "react";
|
||||||
import {ResourceDetails, RFile, Storage, Comment} from "../network/models.ts";
|
import {ResourceDetails, RFile, Storage, Comment} from "../network/models.ts";
|
||||||
import {network} from "../network/network.ts";
|
import {network} from "../network/network.ts";
|
||||||
@@ -49,6 +49,8 @@ export default function ResourcePage() {
|
|||||||
}
|
}
|
||||||
}, [id])
|
}, [id])
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
return <div className="alert alert-error shadow-lg">
|
return <div className="alert alert-error shadow-lg">
|
||||||
<div>
|
<div>
|
||||||
@@ -84,7 +86,9 @@ export default function ResourcePage() {
|
|||||||
<p className={"px-4 pt-2"}>
|
<p className={"px-4 pt-2"}>
|
||||||
{
|
{
|
||||||
resource.tags.map((e) => {
|
resource.tags.map((e) => {
|
||||||
return <span key={e.id} className="badge badge-primary mr-2 text-sm">{e.name}</span>
|
return <span key={e.id} className="badge badge-primary mr-2 text-sm cursor-pointer" onClick={() => {
|
||||||
|
navigate(`/tag/${e.name}`);
|
||||||
|
}}>{e.name}</span>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
|
23
frontend/src/pages/tagged_resources_page.tsx
Normal file
23
frontend/src/pages/tagged_resources_page.tsx
Normal file
@@ -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 <div>
|
||||||
|
<ErrorAlert message={"Tag not found"}/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<h1 className={"text-2xl pt-4 pb-2 px-4"}>
|
||||||
|
{tag}
|
||||||
|
</h1>
|
||||||
|
<ResourcesView loader={(page) => {
|
||||||
|
return network.getResourcesByTag(tag, page)
|
||||||
|
}}></ResourcesView>
|
||||||
|
</div>
|
||||||
|
}
|
@@ -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 {
|
func handleSearchResources(c fiber.Ctx) error {
|
||||||
query := c.Query("keyword")
|
query := c.Query("keyword")
|
||||||
if query == "" {
|
if query == "" {
|
||||||
@@ -135,5 +163,6 @@ func AddResourceRoutes(api fiber.Router) {
|
|||||||
resource.Get("/", handleListResources)
|
resource.Get("/", handleListResources)
|
||||||
resource.Get("/:id", handleGetResource)
|
resource.Get("/:id", handleGetResource)
|
||||||
resource.Delete("/:id", handleDeleteResource)
|
resource.Delete("/:id", handleDeleteResource)
|
||||||
|
resource.Get("/tag/:tag", handleListResourcesWithTag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -41,7 +41,7 @@ func GetResourceList(page, pageSize int) ([]model.Resource, int, error) {
|
|||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
totalPages := int(total) / pageSize
|
totalPages := (total + int64(pageSize) - 1) / int64(pageSize)
|
||||||
|
|
||||||
return resources, int(totalPages), nil
|
return resources, int(totalPages), nil
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ func Search(query string, page, pageSize int) ([]model.Resource, int, error) {
|
|||||||
if endIndex > len(resource) {
|
if endIndex > len(resource) {
|
||||||
endIndex = len(resource)
|
endIndex = len(resource)
|
||||||
}
|
}
|
||||||
totalPages := len(resource) / pageSize
|
totalPages := (len(resource) + pageSize - 1) / pageSize
|
||||||
|
|
||||||
result := make([]model.Resource, 0, endIndex-startIndex)
|
result := make([]model.Resource, 0, endIndex-startIndex)
|
||||||
for i := startIndex; i < endIndex; i++ {
|
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()
|
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 {
|
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)
|
return tx.Offset((page - 1) * pageSize).Limit(pageSize).Preload("Tags").Preload("User").Preload("Images").Order("created_at DESC")
|
||||||
}).First(&tag).Error; err != nil {
|
}).First(&tag).Error; err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
totalPages := int(total) / pageSize
|
totalPages := (int(total) + pageSize - 1) / pageSize
|
||||||
|
|
||||||
return tag.Resources, totalPages, nil
|
return tag.Resources, totalPages, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user