mirror of
https://github.com/wgh136/nysoure.git
synced 2025-09-27 12:17:24 +00:00
Add tag description.
This commit is contained in:
@@ -30,6 +30,7 @@ export interface PageResponse<T> {
|
||||
export interface Tag {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface CreateResourceParams {
|
||||
|
@@ -316,6 +316,34 @@ class Network {
|
||||
}
|
||||
}
|
||||
|
||||
async getTagByName(name: string): Promise<Response<Tag>> {
|
||||
try {
|
||||
const response = await axios.get(`${this.apiBaseUrl}/tag/${name}`)
|
||||
return response.data
|
||||
} catch (e: any) {
|
||||
console.error(e)
|
||||
return {
|
||||
success: false,
|
||||
message: e.toString(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async setTagDescription(tagId: number, description: string): Promise<Response<Tag>> {
|
||||
try {
|
||||
const response = await axios.putForm(`${this.apiBaseUrl}/tag/${tagId}/description`, {
|
||||
description
|
||||
})
|
||||
return response.data
|
||||
} catch (e: any) {
|
||||
console.error(e)
|
||||
return {
|
||||
success: false,
|
||||
message: e.toString(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload image and return the image id
|
||||
*/
|
||||
@@ -453,7 +481,7 @@ class Network {
|
||||
async getResourceDetails(id: number): Promise<Response<ResourceDetails>> {
|
||||
try {
|
||||
const response = await axios.get(`${this.apiBaseUrl}/resource/${id}`)
|
||||
let data = response.data
|
||||
const data = response.data
|
||||
if (!data.related) {
|
||||
data.related = []
|
||||
}
|
||||
|
@@ -2,30 +2,118 @@ import { useParams } from "react-router";
|
||||
import { ErrorAlert } from "../components/alert.tsx";
|
||||
import ResourcesView from "../components/resources_view.tsx";
|
||||
import { network } from "../network/network.ts";
|
||||
import { useEffect } from "react";
|
||||
import {useEffect, useState} from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {Tag} from "../network/models.ts";
|
||||
import Button from "../components/button.tsx";
|
||||
import Markdown from "react-markdown";
|
||||
import {app} from "../app.ts";
|
||||
|
||||
export default function TaggedResourcesPage() {
|
||||
const { tag } = useParams()
|
||||
const { tag: tagName } = useParams()
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!tag) {
|
||||
return <div>
|
||||
const [tag, setTag] = useState<Tag | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("Tag: " + tagName);
|
||||
}, [t, tagName])
|
||||
|
||||
useEffect(() => {
|
||||
if (!tagName) {
|
||||
return;
|
||||
}
|
||||
network.getTagByName(tagName).then((res) => {
|
||||
if (res.success) {
|
||||
setTag(res.data!);
|
||||
}
|
||||
});
|
||||
}, [tagName]);
|
||||
|
||||
if (!tagName) {
|
||||
return <div className={"m-4"}>
|
||||
<ErrorAlert message={"Tag not found"} />
|
||||
</div>
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("Tag: " + tag);
|
||||
}, [tag])
|
||||
|
||||
return <div>
|
||||
<h1 className={"text-2xl pt-6 pb-2 px-4 font-bold"}>
|
||||
Tag: {tag}
|
||||
</h1>
|
||||
<div className={"flex items-center"}>
|
||||
<h1 className={"text-2xl pt-6 pb-2 px-4 font-bold flex-1"}>
|
||||
{tagName}
|
||||
</h1>
|
||||
{
|
||||
tag && <EditTagButton tag={tag} onEdited={(t) => {
|
||||
setTag(t)
|
||||
}} />
|
||||
}
|
||||
</div>
|
||||
{
|
||||
(tag?.description && app.canUpload()) && <article className={"px-4 py-2"}>
|
||||
<Markdown>
|
||||
{tag.description}
|
||||
</Markdown>
|
||||
</article>
|
||||
}
|
||||
<ResourcesView loader={(page) => {
|
||||
return network.getResourcesByTag(tag, page)
|
||||
return network.getResourcesByTag(tagName, page)
|
||||
}}></ResourcesView>
|
||||
</div>
|
||||
}
|
||||
|
||||
function EditTagButton({tag, onEdited}: { tag: Tag, onEdited: (t: Tag) => void }) {
|
||||
const [description, setDescription] = useState(tag.description);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setDescription(tag.description)
|
||||
}, [tag.description]);
|
||||
|
||||
const submit = async () => {
|
||||
if (description === tag.description) {
|
||||
return;
|
||||
}
|
||||
if (description && description.length > 256) {
|
||||
setError("Description is too long");
|
||||
return;
|
||||
}
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
const res = await network.setTagDescription(tag.id, description);
|
||||
setIsLoading(false);
|
||||
if (res.success) {
|
||||
const dialog = document.getElementById("edit_tag_dialog") as HTMLDialogElement;
|
||||
dialog.close();
|
||||
onEdited(res.data!);
|
||||
} else {
|
||||
setError(res.message || "Unknown error");
|
||||
}
|
||||
};
|
||||
|
||||
return <>
|
||||
<Button onClick={()=> {
|
||||
const dialog = document.getElementById("edit_tag_dialog") as HTMLDialogElement;
|
||||
dialog.showModal();
|
||||
}}>Edit</Button>
|
||||
<dialog id="edit_tag_dialog" className="modal">
|
||||
<div className="modal-box">
|
||||
<h3 className="font-bold text-lg">Edit Tag</h3>
|
||||
<p className="py-2 text-sm">Set the description of the tag.</p>
|
||||
<p className="pb-3 text-sm">Use markdown format.</p>
|
||||
<textarea className="textarea h-24 w-full resize-none" value={description} onChange={(e) => setDescription(e.target.value)}/>
|
||||
{error && <ErrorAlert className={"mt-2"} message={error} />}
|
||||
<div className="modal-action">
|
||||
<form method="dialog">
|
||||
<Button className="btn">Close</Button>
|
||||
</form>
|
||||
<Button isLoading={isLoading} className={"btn-primary"} onClick={submit}>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
</>
|
||||
}
|
Reference in New Issue
Block a user