User edit/delete comment function
This commit is contained in:
@@ -9,7 +9,6 @@ import { retrieveUserInformation } from "../security/users";
|
|||||||
import { Button } from "@nextui-org/react";
|
import { Button } from "@nextui-org/react";
|
||||||
import { PaperAirplaneIcon } from "../icons";
|
import { PaperAirplaneIcon } from "../icons";
|
||||||
|
|
||||||
|
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
content: Yup.string()
|
content: Yup.string()
|
||||||
.trim()
|
.trim()
|
||||||
|
|||||||
@@ -2,13 +2,16 @@ import instance from "../security/http";
|
|||||||
import config from "../config";
|
import config from "../config";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Avatar, Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, User } from "@nextui-org/react";
|
import { Avatar, Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, useDisclosure, User } from "@nextui-org/react";
|
||||||
import { ChatBubbleOvalLeftEllipsisIcon, EllipsisHorizontalIcon, HandThumbsUpIcon } from "../icons";
|
import { ChatBubbleOvalLeftEllipsisIcon, EllipsisHorizontalIcon, HandThumbsUpIcon, PaperAirplaneIcon } from "../icons";
|
||||||
|
import * as Yup from "yup";
|
||||||
|
import NextUIFormikTextarea from "./NextUIFormikTextarea";
|
||||||
|
import { Form, Formik, FormikHelpers } from "formik";
|
||||||
|
|
||||||
interface Comment {
|
interface Comment {
|
||||||
id: string;
|
id: string;
|
||||||
content: string;
|
content: string;
|
||||||
user: User; // Make user optional
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
@@ -17,9 +20,21 @@ interface User {
|
|||||||
lastName: string;
|
lastName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validationSchema = Yup.object({
|
||||||
|
content: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.min(3, "Content must be at least 3 characters")
|
||||||
|
.max(500, "Content must be at most 500 characters")
|
||||||
|
.required("Content is required"),
|
||||||
|
});
|
||||||
|
|
||||||
export default function CommentsModule() {
|
export default function CommentsModule() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [commentList, setCommentList] = useState<Comment[]>([]);
|
const [commentList, setCommentList] = useState<Comment[]>([]);
|
||||||
|
// To track/manage comment being edited
|
||||||
|
const [editingCommentId, setEditingCommentId] = useState<string | null>(null);
|
||||||
|
const { isOpen, onOpen, onOpenChange } = useDisclosure();
|
||||||
|
const [commentToDelete, setCommentToDelete] = useState<string | null>(null);
|
||||||
|
|
||||||
let postId = id
|
let postId = id
|
||||||
|
|
||||||
@@ -39,6 +54,53 @@ export default function CommentsModule() {
|
|||||||
getComments();
|
getComments();
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
|
const handleEditClick = (comment: Comment) => {
|
||||||
|
setEditingCommentId(comment.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitComment = async (values: { content: string }, { resetForm }: FormikHelpers<{ content: string }>) => {
|
||||||
|
// Find the comment that matches the `editingCommentId` to get userId and postId
|
||||||
|
const commentBeingEdited = commentList.find(comment => comment.id === editingCommentId);
|
||||||
|
if (!commentBeingEdited) {
|
||||||
|
console.error("Comment not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
content: values.content,
|
||||||
|
userId: commentBeingEdited.user.id, // Include the userId
|
||||||
|
postId: postId // Include the postId
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await instance.put(`${config.serverAddress}/post/comments/${editingCommentId}`, data);
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
console.log("Comment updated successfully", response.data);
|
||||||
|
resetForm();
|
||||||
|
setEditingCommentId(null);
|
||||||
|
getComments();
|
||||||
|
} else {
|
||||||
|
console.error("Failed to update the comment", response.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteClick = (commentId: string) => {
|
||||||
|
setCommentToDelete(commentId);
|
||||||
|
onOpen();
|
||||||
|
};
|
||||||
|
const handleDeleteConfirm = async () => {
|
||||||
|
if (!commentToDelete) return;
|
||||||
|
|
||||||
|
const response = await instance.delete(`${config.serverAddress}/post/comments/${commentToDelete}`);
|
||||||
|
if (response.status === 200) {
|
||||||
|
console.log("Comment deleted successfully", response.data);
|
||||||
|
onOpenChange();
|
||||||
|
setCommentToDelete(null);
|
||||||
|
getComments();
|
||||||
|
} else {
|
||||||
|
console.error("Failed to delete the comment", response.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex w-full">
|
<div className="flex w-full">
|
||||||
@@ -71,7 +133,66 @@ export default function CommentsModule() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col w-10/12 flex-grow text-sm ml-3">
|
<div className="flex flex-col w-10/12 flex-grow text-sm ml-3">
|
||||||
<div className="font-bold">{user.firstName} {user.lastName}</div>
|
<div className="font-bold">{user.firstName} {user.lastName}</div>
|
||||||
<div className="break-words whitespace-pre-wrap">{comment.content}</div>
|
{editingCommentId === comment.id ? (
|
||||||
|
<Formik
|
||||||
|
initialValues={{ content: comment.content }}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={submitComment}
|
||||||
|
>
|
||||||
|
{({ isValid, dirty }) => (
|
||||||
|
<Form className="w-full">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<NextUIFormikTextarea
|
||||||
|
label="Edit Comment"
|
||||||
|
name="content"
|
||||||
|
placeholder="Edit your comment here"
|
||||||
|
/>
|
||||||
|
<Button isIconOnly
|
||||||
|
type="submit"
|
||||||
|
disabled={!isValid || !dirty}
|
||||||
|
className="ml-2 bg-primary-950 text-white">
|
||||||
|
<PaperAirplaneIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
) : (
|
||||||
|
<div>{comment.content}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex-shrink-0 ml-10">
|
||||||
|
<div className="flex flex-row-reverse justify-center items-center size-7">
|
||||||
|
<Dropdown>
|
||||||
|
<div>
|
||||||
|
<DropdownTrigger
|
||||||
|
className="justify-center items-center"
|
||||||
|
>
|
||||||
|
<Button isIconOnly variant="light">
|
||||||
|
<EllipsisHorizontalIcon />
|
||||||
|
</Button>
|
||||||
|
</DropdownTrigger>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu aria-label="Static Actions">
|
||||||
|
<DropdownItem
|
||||||
|
key="edit"
|
||||||
|
textValue="Edit"
|
||||||
|
onClick={() => handleEditClick(comment)}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem
|
||||||
|
key="delete"
|
||||||
|
textValue="Delete"
|
||||||
|
className="text-danger"
|
||||||
|
color="danger"
|
||||||
|
onClick={() => handleDeleteClick(comment.id)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</DropdownItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row ml-14 mt-2 gap-3 w-11/12">
|
<div className="flex flex-row ml-14 mt-2 gap-3 w-11/12">
|
||||||
@@ -86,6 +207,28 @@ export default function CommentsModule() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-2/12"></div>
|
<div className="w-2/12"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||||
|
<ModalContent>
|
||||||
|
{(onClose) => (
|
||||||
|
<>
|
||||||
|
<ModalHeader className="flex flex-col gap-1">
|
||||||
|
Confirm Delete
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<p>Are you sure you want to delete this comment?</p>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button color="danger" variant="light" onPress={onClose}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
<Button color="primary" onPress={handleDeleteConfirm}>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -301,6 +301,34 @@ router.post("/:id/comments", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.put("/comments/:id", async (req, res) => {
|
||||||
|
let { id } = req.params;
|
||||||
|
let data = req.body;
|
||||||
|
|
||||||
|
// Validate request body
|
||||||
|
let validationSchema = yup.object({
|
||||||
|
content: yup.string().trim().min(3).max(500).required(),
|
||||||
|
userId: yup.string().required(),
|
||||||
|
postId: yup.string().required(),
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
console.log("Validating schema...");
|
||||||
|
data = await validationSchema.validate(data, { abortEarly: false });
|
||||||
|
|
||||||
|
// Ensure comment exists
|
||||||
|
let comment = await Comment.findByPk(id);
|
||||||
|
if (!comment) {
|
||||||
|
res.sendStatus(404); // Comment not found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = await comment.update(data);
|
||||||
|
res.json({ message: "Comment updated successfully", comment });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(400).json({ errors: err.errors });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
router.get("/:id/getComments", async (req, res) => {
|
router.get("/:id/getComments", async (req, res) => {
|
||||||
let id = req.params.id;
|
let id = req.params.id;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user