Tag management function

This commit is contained in:
Rykkel
2024-08-12 00:32:10 +08:00
parent 19c8599b2f
commit 672eca530b
5 changed files with 198 additions and 3 deletions

View File

@@ -35,6 +35,8 @@ import CreateVoucherPage from "./pages/CreateVoucherPage";
import EditVoucherPage from "./pages/EditVoucherPage";
import FeedbackPage from "./pages/FeedbackPage";
import ManageFeedbacksPage from "./pages/ManageFeedbacksPage";
import TagManagement from "./pages/TagManagement";
function App() {
return (
@@ -122,6 +124,7 @@ function App() {
<Route path="community-posts">
<Route index element={<CommunityPostManagement />} />
<Route path="manage-tag" element={<TagManagement />} />
</Route>
</Route>
</Route>

View File

@@ -157,7 +157,7 @@ export default function AdministratorNavigationPanel() {
<AdministratorNavigationPanelNavigationButton
text="Tags"
icon={<TagIcon />}
onClickRef="#"
onClickRef="community-posts/manage-tag"
/>
</div>
<div>

View File

@@ -181,7 +181,7 @@ export default function CommunityPostManagement() {
onClick={() =>
handleCopyID(item.postId, item.title)
}
aria-label="Copy postId, title"
aria-label="Copy postId"
>
<ClipboardDocumentIcon />
</Button>

View File

@@ -0,0 +1,157 @@
import {
Table,
TableBody,
TableCell,
TableColumn,
TableHeader,
TableRow,
Button,
Tooltip,
Modal,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
} from "@nextui-org/react";
import { useEffect, useState } from "react";
import instance from "../security/http";
import config from "../config";
import { popErrorToast, popToast } from "../utilities";
import { ClipboardDocumentIcon, TrashDeleteIcon } from "../icons";
export default function TagManagement() {
const [tagList, setTagList] = useState<any[]>([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const [tagToDelete, setTagToDelete] = useState<any>(null);
const columns = [
{ key: "tag", label: "TAG" },
{ key: "actions", label: "ACTIONS" },
];
const populateTagList = () => {
instance
.get(`${config.serverAddress}/post/tags/all`)
.then((response) => {
setTagList(response.data);
console.log(response.data);
})
.catch((error) => {
popErrorToast(error);
});
};
useEffect(() => {
populateTagList();
}, []);
const handleCopyID = (id: string, tag: string) => {
navigator.clipboard.writeText(id);
popToast(tag + "'s Tag ID has been copied!", 1);
};
const handleDeleteClick = (item: any) => {
setTagToDelete(item);
setIsModalOpen(true);
};
const handleDeleteConfirm = async () => {
if (tagToDelete) {
try {
await instance.delete(
`${config.serverAddress}/post/tags/${tagToDelete.id}`
);
populateTagList();
setIsModalOpen(false);
} catch (error) {
console.error("Error deleting post:", error);
}
}
};
return (
<div>
{!tagList && (<div className="flex w-full justify-center m-auto p-20 text-gray-500">There are currently no tags!</div>)}
{tagList && (
<div className="flex flex-col gap-8 p-8">
<p className="text-4xl font-bold">Tag Management</p>
<Table aria-label="Tag Management Table">
<TableHeader columns={columns}>
{(column) => (
<TableColumn key={column.key} className={column.key === "actions" ? "text-right" : ""}>
{column.label}
</TableColumn>
)}
</TableHeader>
<TableBody items={tagList}>
{(tagEntry: any) => (
<TableRow key={tagEntry.id}>
{(columnKey) => (
<TableCell>
{columnKey === "actions" ? (
<div className="flex justify-end gap-2">
<Tooltip content="Copy ID">
<Button
variant="light"
isIconOnly
onClick={() => handleCopyID(tagEntry.id, tagEntry.tag)}
aria-label="Copy tagId">
<ClipboardDocumentIcon />
</Button>
</Tooltip>
<Tooltip content="Delete Tag">
<Button
variant="light"
isIconOnly
onClick={() => handleDeleteClick(tagEntry)}
aria-label="Delete tag"
className="text-red-500">
<TrashDeleteIcon />
</Button>
</Tooltip>
</div>
) : (
<p>{tagEntry.tag}</p>
)}
</TableCell>
)}
</TableRow>
)}
</TableBody>
</Table>
<Modal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
isDismissable={false}
isKeyboardDismissDisabled={true}
>
<ModalContent>
<>
<ModalHeader className="flex flex-col text-danger gap-1">
{tagToDelete?.tag
? `DELETING TAG: ${tagToDelete.tag}`
: "Delete tag"}
</ModalHeader>
<ModalBody>
<p>Are you sure you want to delete this tag?</p>
</ModalBody>
<ModalFooter>
<Button
color="danger"
variant="light"
onPress={() => setIsModalOpen(false)}
>
Cancel
</Button>
<Button color="danger" onPress={handleDeleteConfirm}>
Delete
</Button>
</ModalFooter>
</>
</ModalContent>
</Modal>
</div>
)}
</div>
)
}

View File

@@ -258,7 +258,7 @@ router.delete("/:id", async (req, res) => {
where: { id: id },
});
if (num == 1) {
// destry() returns no. of rows affected, that's why if num == 1
// destroy() returns no. of rows affected, that's why if num == 1
res.json({
message: "Post was deleted successfully.",
});
@@ -317,4 +317,39 @@ router.get("/:id/getComments", async (req, res) => {
res.json(comments);
});
router.get("/tags/all", async (req, res) => {
// Check id not found
let tags = await Tag.findAll();
if (!tags) {
res.sendStatus(404);
return;
}
res.json(tags);
})
router.delete("/tags/:id", async (req, res) => {
let id = req.params.id;
// Check id not found
let tag = await Tag.findByPk(id);
if (!tag) {
res.sendStatus(404);
return;
}
let num = await Tag.destroy({
// destroy() deletes data based on the where condition, and returns the number of rows affected
where: { id: id },
});
if (num == 1) {
// destroy() returns no. of rows affected, that's why if num == 1
res.json({
message: "Tag was deleted successfully.",
});
} else {
res.status(400).json({
message: `Cannot delete tag with id ${id}.`,
});
}
})
module.exports = router;