diff --git a/README.md b/README.md
index b1e1070..027f00b 100644
--- a/README.md
+++ b/README.md
@@ -76,3 +76,6 @@ Direct Message Feature
###### upstream/main
###### main
+
+
+test test test
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index dd8f851..64aed64 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,10 +1,11 @@
{
- "name": "react-auth-boilerplate",
+ "name": "team-project-frontend",
"version": "0.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
+ "name": "team-project-frontend",
"version": "0.1.0",
"dependencies": {
"@testing-library/jest-dom": "^5.16.1",
@@ -13,6 +14,7 @@
"axios": "^0.24.0",
"bootstrap": "^5.1.3",
"dotenv": "^8.2.0",
+ "express": "^4.18.2",
"react": "^17.0.2",
"react-bootstrap": "^2.0.3",
"react-dom": "^17.0.2",
@@ -4384,6 +4386,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/ansi-escapes/node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/ansi-html": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
@@ -19720,9 +19733,11 @@
}
},
"node_modules/type-fest": {
- "version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
+ "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
+ "optional": true,
+ "peer": true,
"engines": {
"node": ">=10"
},
@@ -25301,6 +25316,13 @@
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
"requires": {
"type-fest": "^0.21.3"
+ },
+ "dependencies": {
+ "type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="
+ }
}
},
"ansi-html": {
@@ -37297,9 +37319,11 @@
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
},
"type-fest": {
- "version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
+ "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
+ "optional": true,
+ "peer": true
},
"type-is": {
"version": "1.6.18",
diff --git a/package.json b/package.json
index ed3e22e..96502d8 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"axios": "^0.24.0",
"bootstrap": "^5.1.3",
"dotenv": "^8.2.0",
+ "express": "^4.18.2",
"react": "^17.0.2",
"react-bootstrap": "^2.0.3",
"react-dom": "^17.0.2",
@@ -19,7 +20,7 @@
"web-vitals": "^1.1.2"
},
"scripts": {
- "start": "react-scripts start",
+ "start": "react-scripts --openssl-legacy-provider start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
diff --git a/src/App.js b/src/App.js
index c7583d8..8197ef5 100644
--- a/src/App.js
+++ b/src/App.js
@@ -12,6 +12,9 @@ import SignUp from './components/auth/SignUp'
import SignIn from './components/auth/SignIn'
import SignOut from './components/auth/SignOut'
import ChangePassword from './components/auth/ChangePassword'
+import Comments from './components/comments/comments'
+
+
const App = () => {
@@ -41,46 +44,56 @@ const App = () => {
}
return (
-
-
-
- } />
- }
- />
- }
- />
-
-
-
- }
- />
-
-
- }
- />
-
- {msgAlerts.map((msgAlert) => (
-
+
+
+
+
+ } />
+ }
+ />
+ }
+ />
+
+
+
+ }
+ />
+
+
+ }
+ />
+
+ {msgAlerts.map((msgAlert) => (
+
- ))}
-
+ />
+ ))}
+
+
+
+
+
)
-}
+ }
export default App
diff --git a/src/api/auth.js b/src/api/auth.js
index 5a8c629..52bc72e 100644
--- a/src/api/auth.js
+++ b/src/api/auth.js
@@ -1,6 +1,8 @@
import apiUrl from '../apiConfig'
import axios from 'axios'
+
+
export const signUp = (credentials) => {
return axios({
method: 'POST',
@@ -53,3 +55,59 @@ export const changePassword = (passwords, user) => {
},
})
}
+
+
+
+export const getComments = async (user) => {
+ return axios({
+ url: apiUrl + '/comments',
+ method: 'GET',
+ headers: {
+ Authorization: `Token token=${user.token}`,
+ },
+ })
+ }
+
+ export const createComment = async (text, parentId, user) => {
+ console.log("hello world", user)
+
+ return axios({
+ url: apiUrl + '/comments/' + user._id,
+ method: 'POST',
+ headers: {
+ Authorization: `Token token=${user.token}`,
+ },
+ data: {
+ comments: {
+ body: text,
+ parentId: parentId,
+ },
+ },
+ })
+ }
+
+ export const updateComment = async (text, id, user) => {
+ return axios({
+ url: apiUrl + '/comments/' + id,
+ method: 'PATCH',
+ headers: {
+ Authorization: `Token token=${user.token}`,
+ },
+ data: {
+ comments: {
+ body: text,
+ },
+ },
+ })
+ }
+
+ export const deleteComment = async (id, user) => {
+ return axios({
+ url: apiUrl + '/comments/' + id,
+ method: 'DELETE',
+ headers: {
+ Authorization: `Token token=${user.token}`,
+ },
+ })
+ }
+
\ No newline at end of file
diff --git a/src/components/Home.js b/src/components/Home.js
index d90311e..acb4e9a 100644
--- a/src/components/Home.js
+++ b/src/components/Home.js
@@ -3,10 +3,11 @@ const Home = (props) => {
console.log('props in home', props)
return (
- <>
-
Home Page
- >
+
+
Home
+
)
}
export default Home
+
\ No newline at end of file
diff --git a/src/components/auth/SignUp.js b/src/components/auth/SignUp.js
index a97d4d0..c57bcb8 100644
--- a/src/components/auth/SignUp.js
+++ b/src/components/auth/SignUp.js
@@ -8,6 +8,7 @@ import messages from '../shared/AutoDismissAlert/messages'
import Form from 'react-bootstrap/Form'
import Button from 'react-bootstrap/Button'
+
const SignUp = (props) => {
// constructor(props) {
// super(props)
diff --git a/src/components/comments/comment.js b/src/components/comments/comment.js
new file mode 100644
index 0000000..cc3de79
--- /dev/null
+++ b/src/components/comments/comment.js
@@ -0,0 +1,116 @@
+import { useEffect } from "react";
+import CommentForm from "./commentForm"
+
+const Comment = ({
+ comment,
+ replies,
+ setActiveComment,
+ activeComment,
+ updateComment,
+ deleteComment,
+ addComment,
+ parentId = null,
+ currentUserId,
+}) => {
+ const isEditing =
+ activeComment &&
+ activeComment.id === comment.id &&
+ activeComment.type === "editing"
+ const isReplying =
+ activeComment &&
+ activeComment.id === comment.id &&
+ activeComment.type === "replying"
+ const fiveMinutes = 300000
+ const timePassed = new Date() - new Date(comment.createdAt) > fiveMinutes
+ const canDelete =
+ currentUserId === comment.userId && replies.length === 0 && !timePassed
+ const canReply = Boolean(currentUserId)
+ const canEdit = currentUserId === comment.userId && !timePassed
+ const replyId = parentId ? parentId : comment.id
+ useEffect (() => {
+ }, )
+ console.log("hello" + replyId)
+ const createdAt = new Date(comment.createdAt).toLocaleDateString()
+ return (
+
+
+

+
+
+
+
{comment.username}
+
{createdAt}
+
+ {!isEditing &&
{comment.body}
}
+ {isEditing && (
+
updateComment(text, comment.id)}
+ handleCancel={() => {
+ setActiveComment(null)
+ }}
+ />
+ )}
+
+ {canReply && (
+
+ setActiveComment({ id: comment.id, type: "replying" })
+ }
+ >
+ Reply
+
+ )}
+ {canEdit && (
+
+ setActiveComment({ id: comment.id, type: "editing" })
+ }
+ >
+ Edit
+
+ )}
+ {canDelete && (
+
deleteComment(comment.id)}
+ >
+ Delete
+
+ )}
+
+ {isReplying && (
+ addComment(text, replyId)}
+ />
+ )}
+ {replies.length > 0 && (
+
+ {replies.map((reply) => (
+
+ ))}
+
+ )}
+
+
+ );
+};
+
+export default Comment
+
diff --git a/src/components/comments/commentForm.js b/src/components/comments/commentForm.js
new file mode 100644
index 0000000..1ba48b8
--- /dev/null
+++ b/src/components/comments/commentForm.js
@@ -0,0 +1,41 @@
+import { useState } from "react"
+
+const CommentForm = ({
+ handleSubmit,
+ submitLabel,
+ hasCancelButton = false,
+ handleCancel,
+ initialText = "",
+}) => {
+ const [text, setText] = useState(initialText)
+ const isTextareaDisabled = text.length === 0
+ const onSubmit = (event) => {
+ console.log(event)
+ event.preventDefault()
+ handleSubmit(text, )
+ setText("")
+ };
+ return (
+
+ );
+};
+
+export default CommentForm
\ No newline at end of file
diff --git a/src/components/comments/comments.js b/src/components/comments/comments.js
new file mode 100644
index 0000000..8bd1eb4
--- /dev/null
+++ b/src/components/comments/comments.js
@@ -0,0 +1,93 @@
+import { useState, useEffect } from 'react'
+import CommentForm from './commentForm'
+import Comment from './comment'
+import {
+ getComments as getCommentsApi,
+ createComment as createCommentApi,
+ updateComment as updateCommentApi,
+ deleteComment as deleteCommentApi,
+} from '../../api/auth'
+
+
+
+const Comments = ({ currentUserId, commentsUrl, user }) => {
+ const [backendComments, setBackendComments] = useState([])
+ const [activeComment, setActiveComment] = useState(null)
+ const rootComments = backendComments.filter(
+ (backendComment) => backendComment.parentId !== null
+ )
+
+ const getReplies = (commentId) => {
+ return backendComments
+ .filter((backendComment) => backendComment.parentId === commentId)
+ .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
+ }
+
+ const addComment = (text, parentId) => {
+ console.log("hello", text, parentId)
+ createCommentApi(text, parentId, user).then((comment) => {
+ setBackendComments([...backendComments, comment])
+ setActiveComment(null)
+ }) .catch(error => {
+ console.log(error)
+ })
+ }
+
+
+ const updateComment = (text, commentId) => {
+ console.log(text, commentId)
+ updateCommentApi(text, commentId).then(() => {
+ const updatedBackendComments = backendComments.map((backendComment) => {
+ if (backendComment.id === commentId) {
+ return { ...backendComment, body: text }
+ }
+ return backendComment
+ })
+ setBackendComments(updatedBackendComments)
+ setActiveComment(null)
+ })
+ }
+
+
+ const deleteComment = (commentId) => {
+ deleteCommentApi(commentId).then(() => {
+ const updatedBackendComments = backendComments.filter(
+ (backendComment) => backendComment.id !== commentId
+ )
+ setBackendComments(updatedBackendComments)
+ setActiveComment(null)
+ })
+ }
+
+
+ useEffect(() => {
+ getCommentsApi(commentsUrl).then((res) => {
+ setBackendComments(res.data.comment)
+ })
+ }, [])
+
+ return (
+
+
Comments
+
Write comment
+
+
+ {rootComments.map((rootComment) => (
+
+ ))}
+
+
+ )
+}
+
+export default Comments
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index ec2585e..9f6fdbc 100644
--- a/src/index.css
+++ b/src/index.css
@@ -11,3 +11,101 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
+
+.comments {
+ margin-top: 20px;
+}
+
+.comments-title {
+ font-size: 30px;
+ margin-bottom: 20px;
+}
+
+.comments-container {
+ margin-top: 40px;
+}
+
+.comment-form-title {
+ font-size: 22px;
+}
+
+.comment-form-textarea {
+ width: 100%;
+ height: 80px;
+ margin-bottom: 8px;
+ margin-top: 8px;
+ border: 1px solid rgb(107, 114, 12);
+}
+
+.comment-form-button {
+ font-size: 16px;
+ padding: 8px 16px;
+ background: rgb(59, 130, 246);
+ border-radius: 8px;
+ color: white;
+}
+
+.comment-form-button:hover:enabled {
+ cursor: pointer;
+ background: rgb(37, 99, 235);
+}
+
+.comment-form-button:disabled {
+ opacity: 0.7;
+ cursor: default;
+}
+
+.comment-form-cancel-button {
+ margin-left: 10px;
+}
+
+.comment {
+ display: flex;
+ margin-bottom: 28px;
+}
+
+.comment-image-container {
+ margin-right: 12px;
+}
+
+.comment-image-container img {
+ border-radius: 50px;
+}
+
+.comment-right-part {
+ width: 100%;
+}
+
+.comment-content {
+ display: flex;
+}
+
+.comment-author {
+ margin-right: 8px;
+ font-size: 20px;
+ color: rgb(59, 130, 246);
+}
+
+.comment-text {
+ font-size: 18px;
+}
+
+.comment-actions {
+ display: flex;
+ font-size: 12px;
+ color: rgb(51, 51, 51);
+ cursor: pointer;
+ margin-top: 8px;
+}
+
+.comment-action {
+ margin-right: 8px;
+}
+
+.comment-action:hover {
+ text-decoration: underline;
+}
+
+.replies {
+ margin-top: 20px;
+}
\ No newline at end of file