Add tag alias management functionality

This commit is contained in:
2025-06-09 19:47:46 +08:00
parent f46678b8db
commit 3aac8faad7
5 changed files with 297 additions and 2 deletions

View File

@@ -435,6 +435,24 @@ class Network {
}
}
async setTagAlias(tagID: number, aliases: string[]): Promise<Response<Tag>> {
try {
const response = await axios.put(
`${this.apiBaseUrl}/tag/${tagID}/alias`,
{
aliases,
},
);
return response.data;
} catch (e: any) {
console.error(e);
return {
success: false,
message: e.toString(),
};
}
}
/**
* Upload image and return the image id
*/

View File

@@ -12,6 +12,7 @@ import Input, { TextArea } from "../components/input.tsx";
import TagInput from "../components/tag_input.tsx";
import Badge from "../components/badge.tsx";
import { useAppContext } from "../components/AppContext.tsx";
import { MdAdd, MdClose, MdEdit } from "react-icons/md";
export default function TaggedResourcesPage() {
const { tag: tagName } = useParams();
@@ -45,8 +46,8 @@ export default function TaggedResourcesPage() {
return (
<div>
<div className={"flex items-center"}>
<h1 className={"text-2xl pt-6 pb-2 px-4 font-bold flex-1"}>
<div className={"flex items-center px-4"}>
<h1 className={"text-2xl pt-6 pb-2 font-bold flex-1"}>
{tag?.name ?? tagName}
</h1>
{tag && app.canUpload() && (
@@ -67,6 +68,9 @@ export default function TaggedResourcesPage() {
{(tag?.aliases ?? []).map((e) => {
return <Badge className={"m-1 badge-primary badge-soft"}>{e}</Badge>;
})}
{app.canUpload() && tag && (
<EditAliasDialog tag={tag} onEdited={setTag} />
)}
</div>
{tag?.description && (
<article className={"px-4 py-2"}>
@@ -216,3 +220,138 @@ function EditTagButton({
</>
);
}
function EditAliasDialog({
tag,
onEdited,
}: {
tag: Tag;
onEdited?: (t: Tag) => void;
}) {
const { t } = useTranslation();
const [alias, setAlias] = useState<string[]>(() => {
return tag.aliases ?? [];
});
const [isLoading, setIsLoading] = useState(false);
const [content, setContent] = useState("");
const [error, setError] = useState<string | null>(null);
const submit = async () => {
if (isLoading) {
return;
}
setError(null);
// compare alias and tag.aliases
let isModified = false;
if (alias.length !== tag.aliases?.length) {
isModified = true;
} else {
for (let i = 0; i < alias.length; i++) {
if (alias[i] !== tag.aliases![i]) {
isModified = true;
break;
}
}
}
if (!isModified) {
setError(t("No changes made"));
return;
}
setIsLoading(true);
const res = await network.setTagAlias(tag.id, alias);
setIsLoading(false);
if (res.success) {
const dialog = document.getElementById(
"edit_alias_dialog",
) as HTMLDialogElement;
dialog.close();
if (onEdited) {
onEdited(res.data!);
}
} else {
setError(res.message || t("Unknown error"));
}
};
return (
<>
<Badge
className={
"m-1 badge-accent badge-soft cursor-pointer hover:shadow-sm transition-shadow"
}
onClick={() => {
setError(null);
setAlias(tag.aliases ?? []);
const dialog = document.getElementById(
"edit_alias_dialog",
) as HTMLDialogElement;
dialog.showModal();
}}
>
<MdEdit />
<span>{t("Edit")}</span>
</Badge>
<dialog id="edit_alias_dialog" className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">{t("Edit Alias")}</h3>
<p className="py-4">
{alias.map((e) => {
return (
<Badge className={"m-1 badge-primary badge-soft"}>
<span className={"text-sm pt-0.5"}>{e}</span>
<span
className={
"inline-flex items-center justify-center cursor-pointer hover:bg-base-300 transition-colors rounded-full h-5 w-5"
}
onClick={() => {
setAlias((prev) => prev.filter((x) => x !== e));
}}
>
<MdClose />
</span>
</Badge>
);
})}
</p>
<div className={"flex"}>
<input
className={"input flex-1"}
value={content}
onInput={(e) => {
setContent(e.currentTarget.value);
}}
/>
<span className={"w-4"}></span>
<Button
className={"btn-circle"}
onClick={() => {
if (content.trim() === "") {
return;
}
setAlias((prev) => [...prev, content.trim()]);
setContent("");
}}
>
<MdAdd size={20} />
</Button>
</div>
{error && <ErrorAlert className={"mt-2"} message={error} />}
<div className="modal-action">
<form method="dialog">
<Button className="btn btn-ghost">{t("Close")}</Button>
</form>
<Button
onClick={submit}
isLoading={isLoading}
className={"btn-primary"}
>
{t("Submit")}
</Button>
</div>
</div>
</dialog>
</>
);
}