mirror of
https://github.com/wgh136/nysoure.git
synced 2025-09-27 12:17:24 +00:00
Add tag alias management functionality
This commit is contained in:
@@ -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
|
||||
*/
|
||||
|
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user