Add Quick Add Tag functionality and improve tag management in Edit and Publish pages

This commit is contained in:
2025-05-31 12:43:08 +08:00
parent 37a3d0e459
commit d597d62c1c
8 changed files with 221 additions and 8 deletions

View File

@@ -4,6 +4,9 @@ import {useTranslation} from "react-i18next";
import {network} from "../network/network.ts";
import {LuInfo} from "react-icons/lu";
import {MdSearch} from "react-icons/md";
import Button from "./button.tsx";
import Input, {TextArea} from "./input.tsx";
import {ErrorAlert} from "./alert.tsx";
export default function TagInput({ onAdd, mainTag }: { onAdd: (tag: Tag) => void, mainTag?: boolean }) {
const [keyword, setKeyword] = useState<string>("")
@@ -142,4 +145,61 @@ class Debounce {
this.timer = null
}
}
}
export function QuickAddTagDialog({ onAdded }: { onAdded: (tags: Tag[]) => void }) {
const {t} = useTranslation();
const [text, setText] = useState<string>("")
const [type, setType] = useState<string>("")
const [error, setError] = useState<string | null>(null)
const handleSubmit = async () => {
if (text.trim().length === 0) {
return
}
setError(null)
const names = text.split(",").filter((n) => n.length > 0)
const res = await network.getOrCreateTags(names, type)
if (!res.success) {
setError(res.message)
return
}
const tags = res.data!
onAdded(tags)
setText("")
setType("")
const dialog = document.getElementById("quick_add_tag_dialog") as HTMLDialogElement
dialog.close()
}
return <>
<Button className={"btn-soft btn-primary"} onClick={() => {
const dialog = document.getElementById("quick_add_tag_dialog") as HTMLDialogElement
dialog.showModal()
}}>{t("Quick Add")}</Button>
<dialog id="quick_add_tag_dialog" className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">{t('Add Tags')}</h3>
<p className="py-2 text-sm">
{t("Input tags separated by commas.")}
<br/>
{t("If the tag does not exist, it will be created automatically.")}
<br/>
{t("Optionally, you can specify a type for the new tags.")}
</p>
<TextArea value={text} onChange={(e) => setText(e.target.value)} label={"Tags"}/>
<Input value={type} onChange={(e) => setType(e.target.value)} label={"Type"}/>
{error && <ErrorAlert className={"mt-2"} message={error}/>}
<div className="modal-action">
<form method="dialog">
<Button className="btn">{t("Cancel")}</Button>
</form>
<Button className={"btn-primary"} disabled={text === ""} onClick={handleSubmit}>{t("Submit")}</Button>
</div>
</div>
</dialog>
</>
}

View File

@@ -172,6 +172,11 @@ export const i18nData = {
"About": "About",
"Home": "Home",
"Other": "Other",
"Quick Add": "Quick Add",
"Add Tags": "Add Tags",
"Input tags separated by commas.": "Input tags separated by commas.",
"If the tag does not exist, it will be created automatically.": "If the tag does not exist, it will be created automatically.",
"Optionally, you can specify a type for the new tags.": "Optionally, you can specify a type for the new tags.",
}
},
"zh-CN": {
@@ -347,6 +352,11 @@ export const i18nData = {
"About": "关于",
"Home": "首页",
"Other": "其他",
"Quick Add": "快速添加",
"Add Tags": "添加标签",
"Input tags separated by commas.": "输入标签, 用逗号分隔。",
"If the tag does not exist, it will be created automatically.": "如果标签不存在, 将自动创建。",
"Optionally, you can specify a type for the new tags.": "您可以选择为新标签指定一个类型。",
}
},
"zh-TW": {
@@ -522,6 +532,11 @@ export const i18nData = {
"About": "關於",
"Home": "首頁",
"Other": "其他",
"Quick Add": "快速添加",
"Add Tags": "添加標籤",
"Input tags separated by commas.": "輸入標籤, 用逗號分隔。",
"If the tag does not exist, it will be created automatically.": "如果標籤不存在, 將自動創建。",
"Optionally, you can specify a type for the new tags.": "您可以選擇為新標籤指定一個類型。",
}
}
}

View File

@@ -330,6 +330,22 @@ class Network {
}
}
async getOrCreateTags(names: string[], tagType: string): Promise<Response<Tag[]>> {
try {
const response = await axios.post(`${this.apiBaseUrl}/tag/batch`, {
names,
type: tagType
})
return response.data
} catch (e: any) {
console.error(e)
return {
success: false,
message: e.toString(),
}
}
}
async getTagByName(name: string): Promise<Response<Tag>> {
try {
const response = await axios.get(`${this.apiBaseUrl}/tag/${name}`)

View File

@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next";
import { app } from "../app.ts";
import { ErrorAlert } from "../components/alert.tsx";
import Loading from "../components/loading.tsx";
import TagInput from "../components/tag_input.tsx";
import TagInput, {QuickAddTagDialog} from "../components/tag_input.tsx";
export default function EditResourcePage() {
const [title, setTitle] = useState<string>("")
@@ -178,9 +178,30 @@ export default function EditResourcePage() {
})
}
</p>
<TagInput onAdd={(tag) => {
setTags([...tags, tag])
}} />
<div className={"flex items-center"}>
<TagInput onAdd={(tag) => {
setTags((prev) => {
const existingTag = prev.find(t => t.id === tag.id);
if (existingTag) {
return prev; // If the tag already exists, do not add it again
}
return [...prev, tag];
})
}} />
<span className={"w-4"}/>
<QuickAddTagDialog onAdded={(tags) => {
setTags((prev) => {
const newTags = [...prev];
for (const tag of tags) {
const existingTag = newTags.find(t => t.id === tag.id);
if (!existingTag) {
newTags.push(tag);
}
}
return newTags;
})
}}/>
</div>
<div className={"h-4"}></div>
<p className={"my-1"}>{t("Description")}</p>
<textarea className="textarea w-full min-h-80 p-4" value={article} onChange={(e) => setArticle(e.target.value)} />

View File

@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next";
import { app } from "../app.ts";
import { ErrorAlert } from "../components/alert.tsx";
import {useAppContext} from "../components/AppContext.tsx";
import TagInput from "../components/tag_input.tsx";
import TagInput, {QuickAddTagDialog} from "../components/tag_input.tsx";
export default function PublishPage() {
const [title, setTitle] = useState<string>("")
@@ -154,9 +154,30 @@ export default function PublishPage() {
})
}
</p>
<TagInput onAdd={(tag) => {
setTags([...tags, tag])
}} />
<div className={"flex items-center"}>
<TagInput onAdd={(tag) => {
setTags((prev) => {
const existingTag = prev.find(t => t.id === tag.id);
if (existingTag) {
return prev; // If the tag already exists, do not add it again
}
return [...prev, tag];
})
}} />
<span className={"w-4"}/>
<QuickAddTagDialog onAdded={(tags) => {
setTags((prev) => {
const newTags = [...prev];
for (const tag of tags) {
const existingTag = newTags.find(t => t.id === tag.id);
if (!existingTag) {
newTags.push(tag);
}
}
return newTags;
})
}}/>
</div>
<div className={"h-4"}></div>
<p className={"my-1"}>{t("Description")}</p>
<textarea className="textarea w-full min-h-80 p-4" value={article} onChange={(e) => setArticle(e.target.value)} />