Retrieve post comments

This commit is contained in:
Rykkel
2024-08-01 03:02:09 +08:00
parent 1bc4672cc7
commit 46ceb96419
6 changed files with 258 additions and 120 deletions

View File

@@ -0,0 +1,86 @@
import { Formik, Form, FormikHelpers } from "formik";
import { useEffect, useState } from "react";
import config from '../config';
import * as Yup from "yup";
import NextUIFormikTextarea from "./NextUIFormikTextarea";
import instance from '../security/http';
import { useNavigate, useParams } from "react-router-dom";
import { retrieveUserInformation } from "../security/users";
import { Button } from "@nextui-org/react";
import { PaperAirplaneIcon } from "../icons";
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")
});
export default function CommentInputModule() {
const navigate = useNavigate();
const [userId, setUserId] = useState(null);
const { id } = useParams(); // Retrieve 'id' from the route
const initialValues = {
content: "",
};
const postId = id;
useEffect(() => {
const getUserInformation = async () => {
try {
const user = await retrieveUserInformation(); // Get the user ID
setUserId(user.id); // Set the user ID in the state
} catch (error) {
console.error(error);
}
};
getUserInformation();
}, []);
const submitComment = async (values: any, { resetForm }: FormikHelpers<{ content: string }>) => {
const response = await instance.post(config.serverAddress + `/post/${postId}/comments`,
{ ...values, userId, postId }
);
console.log("Comment created succesfully", response.data);
resetForm(); // Reset the form after successful submission
};
return (
<div className="flex w-full">
<div className="w-2/12"></div>
<div className="w-8/12 mx-auto mt-5">
<div>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={submitComment}
>
{({ isValid, dirty }) => (
<Form>
<div className="relative">
<NextUIFormikTextarea
label="Comment"
name="content"
placeholder="Write your comment here"
/>
<div className="flex justify-end my-2">
<Button isIconOnly
type="submit"
disabled={!isValid || !dirty}
className="bg-primary-950 text-white">
<PaperAirplaneIcon />
</Button>
</div>
</div>
</Form>
)}
</Formik>
</div>
</div>
<div className="w-2/12"></div>
</div>
);
}

View File

@@ -1,85 +1,118 @@
import { Formik, Form } from "formik";
import React, { useEffect, useState } from "react";
import config from '../config';
import * as Yup from "yup";
import NextUIFormikTextarea from "../components/NextUIFormikTextarea";
import instance from '../security/http';
import { useNavigate, useParams } from "react-router-dom";
import { retrieveUserInformation } from "../security/users";
import { Button } from "@nextui-org/react";
import { PaperAirplaneIcon } from "../icons";
import instance from "../security/http";
import config from "../config";
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import { Avatar, Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger } from "@nextui-org/react";
import { ChatBubbleOvalLeftEllipsisIcon, EllipsisHorizontalIcon, HandThumbsUpIcon } from "../icons";
interface Comment {
id: string;
content: 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")
});
// type User = {
// id: string;
// firstName: string;
// lastName: string;
// };
export default function CommentsModule() {
const navigate = useNavigate();
const [userId, setUserId] = useState(null);
const { id } = useParams(); // Retrieve 'id' from the route
const { id } = useParams();
const [commentList, setCommentList] = useState<Comment[]>([]);
const initialValues = {
content: "",
let postId = id
const getComments = async () => {
instance
.get(config.serverAddress + `/post/${postId}/getComments`).then((res) => {
setCommentList(res.data);
});
};
const postId = id;
useEffect(() => {
const getUserInformation = async () => {
try {
const user = await retrieveUserInformation(); // Get the user ID
setUserId(user.id); // Set the user ID in the state
} catch (error) {
console.error(error);
}
};
getUserInformation();
}, []);
const submitComment = async (values: any) => {
const response = await instance.post(config.serverAddress + `/post/${postId}/comments`,
{ ...values, userId, postId }
);
console.log("Comment created succesfully", response.data);
};
getComments();
}, [id]);
return (
<div className="flex w-full">
<div className="w-2/12"></div>
<div className="w-full mx-auto mt-5">
<div>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={submitComment}
>
{({ isValid, dirty }) => (
<Form>
<div className="relative">
<NextUIFormikTextarea
label="Comment"
name="content"
placeholder="Write your comment here"
/>
<div className="flex justify-end my-2">
<Button isIconOnly
type="submit"
disabled={!isValid || !dirty}
className="bg-primary-950 text-white">
<PaperAirplaneIcon />
</Button>
</div>
</div>
</Form>
)}
</Formik>
</div>
<>
<div className="flex w-full">
<div className="w-2/12"></div>
<div className="w-8/12 ml-5 mb-2 text-md font-bold opacity-65">Comment Section</div>
<div className="w-2/12"></div>
</div>
<div className="w-2/12"></div>
</div>
<div className="flex w-full h-full">
<div className="w-2/12"></div>
<div className="w-8/12 mx-auto">
{commentList.length > 0 ? (
commentList.map((comment) => {
return (
<div className="flex flex-col w-full bg-primary-50 dark:bg-primary-950 rounded-xl mb-2 p-3 mx-auto"
key={comment.id}>
<div className="flex flex-row w-full">
<div>
<Avatar
src="https://pbs.twimg.com/media/GOva9x5a0AAK8Bn?format=jpg&name=large"
size="md"
/>
</div>
<div className="flex flex-col w-11/12 text-sm ml-3">
<div className="font-bold">Name</div>
<div className="break-words whitespace-pre-wrap">{comment.content}</div>
</div>
<div className="ml-4">
<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"
>
Edit
</DropdownItem>
<DropdownItem
key="delete"
textValue="Delete"
className="text-danger"
color="danger"
>
Delete
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
</div>
<div className="flex flex-row ml-14 mt-2 gap-3 w-11/12">
<Button variant="light" isIconOnly>
<HandThumbsUpIcon />
</Button>
<Button variant="light" isIconOnly className="w-20">
<div>
<ChatBubbleOvalLeftEllipsisIcon />
</div>
<div>
Reply
</div>
</Button>
</div>
</div>
);
})
) : (
<div className="flex w-full justify-center m-auto p-20 text-gray-500">No comments... It's so lonely 🥹</div>
)}
</div>
<div className="w-2/12"></div>
</div>
</>
);
}
};

View File

@@ -620,15 +620,14 @@ export const PaperAirplaneIcon = () => {
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24"
stroke-width="1.5"
strokeWidth={1.5}
stroke="currentColor"
className="size-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5"
/>
</svg>
)
}
className="size-6">
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5" />
</svg>
);
};

View File

@@ -31,7 +31,6 @@ import {
import { useNavigate } from "react-router-dom";
import { retrieveUserInformationById } from "../security/usersbyid";
import { retrieveUserInformation } from "../security/users";
import { number } from "yup";
// import UserPostImage from "../components/UserPostImage";
interface Post {

View File

@@ -1,5 +1,5 @@
import { useParams, useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import instance from "../security/http";
import config from "../config";
import {
@@ -25,6 +25,7 @@ import {
ArrowUTurnLeftIcon,
} from "../icons";
import { retrieveUserInformationById } from "../security/usersbyid";
import CommentInputModule from "../components/CommentInputModule";
import CommentsModule from "../components/CommentsModule";
interface Post {
@@ -114,7 +115,7 @@ const PostPage: React.FC = () => {
<ArrowUTurnLeftIcon />
</Button>
</div>
<div className="flex flex-row w-full gap-4 mx-auto ">
<div className="flex flex-row w-8/12 gap-4 mx-auto">
<div className="flex flex-col gap-8 w-full">
<div className="flex flex-col gap-4">
<section
@@ -136,7 +137,8 @@ const PostPage: React.FC = () => {
</div>
<div className="flex flex-row-reverse justify-center items-center">
<Dropdown>
<DropdownTrigger className="justify-center items-center">
<DropdownTrigger
className="justify-center items-center">
<Button isIconOnly variant="light">
<EllipsisHorizontalIcon />
</Button>
@@ -146,7 +148,7 @@ const PostPage: React.FC = () => {
key="edit"
textValue="Edit"
onClick={() => {
navigate(`edit/${post.id}`);
navigate(`../edit/${post.id}`);
}}
>
Edit
@@ -201,6 +203,9 @@ const PostPage: React.FC = () => {
</div>
<div className="w-2/12"></div>
</section>
<section>
<CommentInputModule />
</section>
<section>
<CommentsModule />
</section>

View File

@@ -45,32 +45,6 @@ router.post("/", async (req, res) => {
}
});
router.post("/:id/comments", async (req, res) => {
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 });
console.log("Creating comment...");
let result = await Comment.create(data);
res.json(result);
console.log("Success!");
} catch (err) {
console.log("Error caught! Info: " + err);
res.status(400).json({ errors: err.errors });
}
});
// // sequelize method findAll is used to generate a standard SELECT query which will retrieve all entries from the table
// router.get("/", async (req, res) => {
// let list = await Tutorial.findAll({
@@ -180,4 +154,46 @@ router.delete("/:id", async (req, res) => {
}
});
router.post("/:id/comments", async (req, res) => {
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 });
console.log("Creating comment...");
let result = await Comment.create(data);
res.json(result);
console.log("Success!");
} catch (err) {
console.log("Error caught! Info: " + err);
res.status(400).json({ errors: err.errors });
}
});
router.get("/:id/getComments", async (req, res) => {
let id = req.params.id;
let condition = {
where: { postId: id },
order: [['createdAt', 'DESC']]
};
// Check id not found
let comments = await Comment.findAll(condition);
if (!comments) {
res.sendStatus(404);
return;
}
res.json(comments);
});
module.exports = router;