Tag management function
This commit is contained in:
@@ -35,6 +35,8 @@ import CreateVoucherPage from "./pages/CreateVoucherPage";
|
|||||||
import EditVoucherPage from "./pages/EditVoucherPage";
|
import EditVoucherPage from "./pages/EditVoucherPage";
|
||||||
import FeedbackPage from "./pages/FeedbackPage";
|
import FeedbackPage from "./pages/FeedbackPage";
|
||||||
import ManageFeedbacksPage from "./pages/ManageFeedbacksPage";
|
import ManageFeedbacksPage from "./pages/ManageFeedbacksPage";
|
||||||
|
import TagManagement from "./pages/TagManagement";
|
||||||
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@@ -122,6 +124,7 @@ function App() {
|
|||||||
|
|
||||||
<Route path="community-posts">
|
<Route path="community-posts">
|
||||||
<Route index element={<CommunityPostManagement />} />
|
<Route index element={<CommunityPostManagement />} />
|
||||||
|
<Route path="manage-tag" element={<TagManagement />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ export default function AdministratorNavigationPanel() {
|
|||||||
<AdministratorNavigationPanelNavigationButton
|
<AdministratorNavigationPanelNavigationButton
|
||||||
text="Tags"
|
text="Tags"
|
||||||
icon={<TagIcon />}
|
icon={<TagIcon />}
|
||||||
onClickRef="#"
|
onClickRef="community-posts/manage-tag"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ export default function CommunityPostManagement() {
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleCopyID(item.postId, item.title)
|
handleCopyID(item.postId, item.title)
|
||||||
}
|
}
|
||||||
aria-label="Copy postId, title"
|
aria-label="Copy postId"
|
||||||
>
|
>
|
||||||
<ClipboardDocumentIcon />
|
<ClipboardDocumentIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
157
client/src/pages/TagManagement.tsx
Normal file
157
client/src/pages/TagManagement.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -258,7 +258,7 @@ router.delete("/:id", async (req, res) => {
|
|||||||
where: { id: id },
|
where: { id: id },
|
||||||
});
|
});
|
||||||
if (num == 1) {
|
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({
|
res.json({
|
||||||
message: "Post was deleted successfully.",
|
message: "Post was deleted successfully.",
|
||||||
});
|
});
|
||||||
@@ -317,4 +317,39 @@ router.get("/:id/getComments", async (req, res) => {
|
|||||||
res.json(comments);
|
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;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user