From 209b03f35df2e355bdd43356857dbbfa42a73511 Mon Sep 17 00:00:00 2001
From: Rykkel <220993G@mymail.nyp.edu.sg>
Date: Wed, 31 Jul 2024 18:59:33 +0800
Subject: [PATCH] Added comment model, comment post function
---
client/src/components/CommentsModule.tsx | 76 ++++++++++++++++++++++++
client/src/pages/PostPage.tsx | 5 ++
server/models/Comment.js | 38 ++++++++++++
server/routes/post.js | 29 ++++++++-
4 files changed, 146 insertions(+), 2 deletions(-)
create mode 100644 client/src/components/CommentsModule.tsx
create mode 100644 server/models/Comment.js
diff --git a/client/src/components/CommentsModule.tsx b/client/src/components/CommentsModule.tsx
new file mode 100644
index 0000000..9277ba1
--- /dev/null
+++ b/client/src/components/CommentsModule.tsx
@@ -0,0 +1,76 @@
+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";
+
+
+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 CommentsModule() {
+ 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) => {
+ const response = await instance.post(config.serverAddress + `/post/${postId}/comments`,
+ { ...values, userId, postId }
+ );
+ console.log("Comment created succesfully", response.data);
+ };
+
+ return (
+
+
+
+
+
+ {({ isValid, dirty }) => (
+
+ )}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/pages/PostPage.tsx b/client/src/pages/PostPage.tsx
index 256852e..aba9cc3 100644
--- a/client/src/pages/PostPage.tsx
+++ b/client/src/pages/PostPage.tsx
@@ -25,6 +25,7 @@ import {
ArrowUTurnLeftIcon,
} from "../icons";
import { retrieveUserInformationById } from "../security/usersbyid";
+import CommentsModule from "../components/CommentsModule";
interface Post {
title: string;
@@ -200,6 +201,10 @@ const PostPage: React.FC = () => {
+
+
{(onClose) => (
diff --git a/server/models/Comment.js b/server/models/Comment.js
new file mode 100644
index 0000000..7410dd8
--- /dev/null
+++ b/server/models/Comment.js
@@ -0,0 +1,38 @@
+module.exports = (sequelize, DataTypes) => {
+ const Comment = sequelize.define("Comment", {
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
+ allowNull: false,
+ },
+ content: {
+ type: DataTypes.TEXT,
+ allowNull: false,
+ },
+ parentId: {
+ type: DataTypes.UUID,
+ allowNull: true,
+ },
+ }, {
+ tableName: 'comments',
+ timestamps: true,
+ });
+
+ Comment.associate = (models) => {
+ Comment.belongsTo(models.User, {
+ foreignKey: 'userId',
+ onDelete: 'CASCADE',
+ });
+ Comment.belongsTo(models.Post, {
+ foreignKey: 'postId',
+ onDelete: 'CASCADE',
+ });
+ Comment.hasMany(models.Comment, {
+ foreignKey: 'parentId',
+ as: 'replies',
+ });
+ };
+
+ return Comment;
+}
\ No newline at end of file
diff --git a/server/routes/post.js b/server/routes/post.js
index 9d7d692..5834b9e 100644
--- a/server/routes/post.js
+++ b/server/routes/post.js
@@ -1,6 +1,6 @@
const express = require('express');
const router = express.Router();
-const { Post, User } = require('../models');
+const { Post, Comment } = require('../models');
const { Op, where } = require("sequelize");
const yup = require("yup");
const multer = require("multer");
@@ -28,7 +28,7 @@ router.post("/", async (req, res) => {
data = await validationSchema.validate(data, // validate() method is used to validate data against the schema and returns the valid data and any applied transformations
{ abortEarly: false }); // abortEarly: false means the validation won’t stop when the first error is detected
// Process valid data
-
+
// Check for profanity
if (filter.isProfane(data.title)) {
return res.status(400).json({ field: 'title', error: 'Profane content detected in title' });
@@ -45,6 +45,31 @@ 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) => {