diff --git a/client/src/App.tsx b/client/src/App.tsx index 35d4de9..6f1fa97 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -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() { } /> + } /> diff --git a/client/src/components/AdministratorNavigationPanel.tsx b/client/src/components/AdministratorNavigationPanel.tsx index 140e166..2c5b724 100644 --- a/client/src/components/AdministratorNavigationPanel.tsx +++ b/client/src/components/AdministratorNavigationPanel.tsx @@ -157,7 +157,7 @@ export default function AdministratorNavigationPanel() { } - onClickRef="#" + onClickRef="community-posts/manage-tag" />
diff --git a/client/src/pages/CommunityPostManagement.tsx b/client/src/pages/CommunityPostManagement.tsx index a960cd9..df0c7b1 100644 --- a/client/src/pages/CommunityPostManagement.tsx +++ b/client/src/pages/CommunityPostManagement.tsx @@ -181,7 +181,7 @@ export default function CommunityPostManagement() { onClick={() => handleCopyID(item.postId, item.title) } - aria-label="Copy postId, title" + aria-label="Copy postId" > diff --git a/client/src/pages/TagManagement.tsx b/client/src/pages/TagManagement.tsx new file mode 100644 index 0000000..f961baf --- /dev/null +++ b/client/src/pages/TagManagement.tsx @@ -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([]); + const [isModalOpen, setIsModalOpen] = useState(false); + const [tagToDelete, setTagToDelete] = useState(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 ( +
+ {!tagList && (
There are currently no tags!
)} + {tagList && ( +
+

Tag Management

+ + + {(column) => ( + + {column.label} + + )} + + + {(tagEntry: any) => ( + + {(columnKey) => ( + + {columnKey === "actions" ? ( +
+ + + + + + +
+ ) : ( +

{tagEntry.tag}

+ )} +
+ )} +
+ )} +
+
+ + setIsModalOpen(false)} + isDismissable={false} + isKeyboardDismissDisabled={true} + > + + <> + + {tagToDelete?.tag + ? `DELETING TAG: ${tagToDelete.tag}` + : "Delete tag"} + + +

Are you sure you want to delete this tag?

+
+ + + + + +
+
+
+ )} +
+ ) +} diff --git a/server/routes/post.js b/server/routes/post.js index 645dd6c..3f2f6bb 100644 --- a/server/routes/post.js +++ b/server/routes/post.js @@ -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;