From ef3dc7c11ecf7d4b2e173bbf917b88fdf91a834b Mon Sep 17 00:00:00 2001 From: meet Date: Fri, 7 Jun 2024 22:14:20 +0530 Subject: [PATCH 1/2] Created delete post feature --- backend/src/routes/post/controller.ts | 42 +++++++++++++++++++++++ backend/src/routes/post/route.ts | 4 ++- frontend/src/components/PostCard.tsx | 49 +++++++++++++++++++++++++-- frontend/src/pages/Posts.tsx | 10 +++++- frontend/src/pages/Profile.tsx | 14 +++++--- 5 files changed, 110 insertions(+), 9 deletions(-) diff --git a/backend/src/routes/post/controller.ts b/backend/src/routes/post/controller.ts index 51047b9d..45fa025a 100644 --- a/backend/src/routes/post/controller.ts +++ b/backend/src/routes/post/controller.ts @@ -578,4 +578,46 @@ export const getLeaderboardController = async (req: Request, res: Response) => { error: "Failed to fetch leaderboard.", }); } +}; + +export const deletePostController = async (req: UserAuthRequest, res: Response) => { + try { + const userId = req.userId; + const postId = req.params.id; + + if (!userId) { + return res.status(403).json({ error: { message: "Invalid user" } }); + } + + const post = await prisma.post.findUnique({ + where: { id: postId }, + }); + + if (!post) { + return res.status(404).json({ error: { message: "Post not found" } }); + } + + if (post.authorId !== userId) { + return res.status(403).json({ error: { message: "You are not authorized to delete this post" } }); + } + + await prisma.userPostInteraction.deleteMany({ + where: { postId } + }); + await prisma.comment.deleteMany({ + where: { postId } + }); + await prisma.favorite.deleteMany({ + where: { postId } + }); + + await prisma.post.delete({ + where: { id: postId }, + }); + + res.status(200).json({ message: "Post deleted successfully" }); + } catch (error) { + console.log(error) + res.status(500).json({ error: { message: "An unexpected error occurred" } }); + } }; \ No newline at end of file diff --git a/backend/src/routes/post/route.ts b/backend/src/routes/post/route.ts index 31ed903b..88d8bea2 100644 --- a/backend/src/routes/post/route.ts +++ b/backend/src/routes/post/route.ts @@ -1,7 +1,7 @@ import { Router } from "express"; import authMiddleware from "../../middleware/auth" -import { createCommentController, createPostController, dislikePostController, favoritePostController, getCommentsController, getFavoritePostsController, getLeaderboardController, getPostController, getPostsWithPagination, likePostController, unfavoritePostController } from "./controller"; +import { createCommentController, createPostController, deletePostController, dislikePostController, favoritePostController, getCommentsController, getFavoritePostsController, getLeaderboardController, getPostController, getPostsWithPagination, likePostController, unfavoritePostController } from "./controller"; const postRouter = Router(); @@ -29,4 +29,6 @@ postRouter.get('/:id/favorites', authMiddleware, getFavoritePostsController); postRouter.get('/all/leaderboard', getLeaderboardController); +postRouter.delete('/delete/:id', authMiddleware, deletePostController); + export default postRouter; \ No newline at end of file diff --git a/frontend/src/components/PostCard.tsx b/frontend/src/components/PostCard.tsx index 334ad2d2..069e60ad 100644 --- a/frontend/src/components/PostCard.tsx +++ b/frontend/src/components/PostCard.tsx @@ -1,11 +1,42 @@ import { Link } from "react-router-dom"; -import { IPost } from "../types"; +import { IPost, IUser } from "../types"; +import { MdDeleteOutline } from "react-icons/md"; +import axios from "axios"; +import { useState } from "react"; +import toast from "react-hot-toast"; type Props = { post: IPost; + onDelete: (id: string) => void; + currentUser: IUser ; }; -const PostCard = ({ post }: Props) => { +const PostCard = ({ post,onDelete,currentUser }: Props) => { + + const [isDeleting, setIsDeleting] = useState(false); + + const handleDelete = async () => { + setIsDeleting(true); + try { + const token = localStorage.getItem('token'); + if (!token) { + toast.error('Please login to remove a post from favorites'); + return; + } + await axios.delete(`/api/v1/posts/delete/${post.id}`, { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + onDelete(post.id); + toast.error('Post Deleted successfully !') + } catch (error) { + console.error("Failed to delete post", error); + } finally { + setIsDeleting(false); + } + }; + return (
{ ))}
+
Read more + {currentUser && currentUser.id === post.author.id && ( + + )} +
+ ); }; diff --git a/frontend/src/pages/Posts.tsx b/frontend/src/pages/Posts.tsx index e768b232..9bbf6b04 100644 --- a/frontend/src/pages/Posts.tsx +++ b/frontend/src/pages/Posts.tsx @@ -3,6 +3,8 @@ import axios from 'axios'; import { IPost } from '../types'; import Loader from '../components/Loader'; import PostCard from '../components/PostCard'; +import { userState } from '../store/atoms/auth'; +import { useRecoilValue } from 'recoil'; const Posts = () => { const [posts, setPosts] = useState([]); @@ -16,6 +18,8 @@ const Posts = () => { const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(1); + const currentUser = useRecoilValue(userState); + useEffect(() => { const fetchPosts = async () => { try { @@ -99,6 +103,10 @@ const Posts = () => { return
{error}
; } + const handleDelete = (id: string) => { + setPosts((prevPosts) => prevPosts.filter((post) => post.id !== id)); + }; + return (

Posts

@@ -160,7 +168,7 @@ const Posts = () => {
{filteredPosts.map((post, index) => ( - + ))}
diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 243f5ebf..20272373 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -1,8 +1,8 @@ import { useEffect, useState } from "react"; -import { IUser } from "../types"; +import { IPost, IUser } from "../types"; import axios from "axios"; import { useRecoilValue } from "recoil"; -import { tokenState } from "../store/atoms/auth"; +import { tokenState, userState } from "../store/atoms/auth"; import Loader from "../components/Loader"; import PostCard from "../components/PostCard"; import { GoUnverified } from "react-icons/go"; @@ -12,6 +12,7 @@ import { AiTwotoneInfoCircle } from "react-icons/ai"; import toast from "react-hot-toast"; const Profile = () => { + const [posts, setPosts] = useState([]); const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [errorMessage, setErrorMessage] = useState(""); @@ -19,6 +20,7 @@ const Profile = () => { const [otp, setOtp] = useState(""); const [verificationError, setVerificationError] = useState(""); const token = useRecoilValue(tokenState); + const currentUser = useRecoilValue(userState); useEffect(() => { const fetchUser = async () => { @@ -37,7 +39,7 @@ const Profile = () => { }; fetchUser(); - }, [token]); + }, [token,posts]); const handleGenerateOtp = async () => { try { @@ -87,6 +89,10 @@ const Profile = () => { return
{errorMessage}
; } + const handleDelete = (id: string) => { + setPosts((prevPosts) => prevPosts.filter((post) => post.id !== id)); + }; + return ( <>
@@ -135,7 +141,7 @@ const Profile = () => {

Posts ( {user?.posts.length} )

- {user?.posts.map(post => )} + {user?.posts.map(post => )}
From e1679520a3c06879cf8b6e4b4fd970e7d69b44b3 Mon Sep 17 00:00:00 2001 From: meet Date: Sat, 8 Jun 2024 19:14:54 +0530 Subject: [PATCH 2/2] Fixed deployment failed problem, ready to merge --- frontend/src/pages/Favorite.tsx | 8 +++++++- frontend/src/pages/HomePagePosts.tsx | 9 ++++++++- frontend/src/pages/Profile.tsx | 8 ++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/Favorite.tsx b/frontend/src/pages/Favorite.tsx index bb8854d9..a11ffde3 100644 --- a/frontend/src/pages/Favorite.tsx +++ b/frontend/src/pages/Favorite.tsx @@ -12,6 +12,7 @@ const Favorite = () => { const [loading, setLoading] = useState(true); const [errorMessage, setErrorMessage] = useState(""); const token = useRecoilValue(tokenState); + const currentUser = useRecoilValue(userState); const fetchFavoritePosts = async (user: IUser): Promise => { try { @@ -47,6 +48,11 @@ const Favorite = () => { getFavoritePosts(); }, [user]); + const handleDelete = (id: string) => { + setFavoritePosts((prevPosts) => prevPosts.filter((post) => post.id !== id)); + }; + + if (loading) { return ; } @@ -67,7 +73,7 @@ const Favorite = () => {

Favorite Posts ( {favoritePosts.length} )

{favoritePosts.map(post => ( - + ))}
diff --git a/frontend/src/pages/HomePagePosts.tsx b/frontend/src/pages/HomePagePosts.tsx index b7dfe645..89eba54b 100644 --- a/frontend/src/pages/HomePagePosts.tsx +++ b/frontend/src/pages/HomePagePosts.tsx @@ -3,11 +3,14 @@ import axios from 'axios'; import { IPost } from '../types'; import Loader from '../components/Loader'; import PostCard from '../components/PostCard'; +import { userState } from '../store/atoms/auth'; +import { useRecoilValue } from 'recoil'; const HomePagePost = () => { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); + const currentUser = useRecoilValue(userState); useEffect(() => { const fetchPosts = async () => { @@ -25,6 +28,10 @@ const HomePagePost = () => { fetchPosts(); }, []); + const handleDelete = (id: string) => { + setPosts((prevPosts) => prevPosts.filter((post) => post.id !== id)); + }; + if (loading) { return ; } @@ -38,7 +45,7 @@ const HomePagePost = () => {

📃 Recent Posts Added

{posts.map((post, index) => ( - + ))}
diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 20272373..02d3f440 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -81,6 +81,10 @@ const Profile = () => { } }; + const handleDelete = (id: string) => { + setPosts((prevPosts) => prevPosts.filter((post) => post.id !== id)); + }; + if (loading) { return ; } @@ -89,10 +93,6 @@ const Profile = () => { return
{errorMessage}
; } - const handleDelete = (id: string) => { - setPosts((prevPosts) => prevPosts.filter((post) => post.id !== id)); - }; - return ( <>