diff --git a/client/src/App.js b/client/src/App.js index 46f1393..3672d84 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -28,7 +28,7 @@ function App() { - + ) } diff --git a/client/src/components/PostItem.jsx b/client/src/components/PostItem.jsx index bb22bd0..40167b7 100644 --- a/client/src/components/PostItem.jsx +++ b/client/src/components/PostItem.jsx @@ -1,10 +1,21 @@ -const PostItem = ({ post }) => { +import { FaRegEdit, FaTrash } from 'react-icons/fa' + +const PostItem = (props) => { + const { post, deletePostAction } = {...props} return (
-
{post.title}
+
{props.post.title}

{post.content}

+
+ + +
) } diff --git a/client/src/features/blog/blogService.js b/client/src/features/blog/blogService.js index 78826f4..5bf053e 100644 --- a/client/src/features/blog/blogService.js +++ b/client/src/features/blog/blogService.js @@ -1,6 +1,6 @@ import axios from 'axios' -const API_URL = 'http://localhost:8000/blog' +const API_URL = 'http://localhost:8000/blog/' // Create new post const createBlog = async (postData, token) => { @@ -27,9 +27,22 @@ const getPosts = async (token) => { return response.data } +// Delete a post +const deletePost = async (token, postId) => { + const config = { + headers: { + Authorization: `Bearer ${token}`, + }, + } + + const response = await axios.delete(API_URL + postId, config) + return response.data +} + const blogService = { createBlog, getPosts, + deletePost } export default blogService diff --git a/client/src/features/blog/blogSlice.js b/client/src/features/blog/blogSlice.js index 85741c0..088ba0b 100644 --- a/client/src/features/blog/blogSlice.js +++ b/client/src/features/blog/blogSlice.js @@ -1,5 +1,6 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import blogService from './blogService' +import { toast } from "react-toastify"; const initialState = { posts: [], @@ -50,6 +51,28 @@ export const getPosts = createAsyncThunk( } ) +// Delete single user post +export const deletePost = createAsyncThunk( + 'blog/delete', + async (postId, thunkAPI) => { + try { + const token = thunkAPI.getState().auth.user.access_token + await blogService.deletePost(token, postId) + toast.success('Deleted post successfully') + thunkAPI.dispatch(getPosts()); + } catch (error) { + const message = + (error.response && + error.response.data && + error.response.data.message) || + error.message || + error.toString() + + return thunkAPI.rejectWithValue(message) + } + } +) + export const postSlice = createSlice({ name: 'post', initialState, @@ -83,6 +106,18 @@ export const postSlice = createSlice({ state.isError = true state.message = action.payload }) + .addCase(deletePost.pending, (state) => { + state.isLoading = true + }) + .addCase(deletePost.fulfilled, (state, action) => { + state.isLoading = false + state.isSuccess = true + }) + .addCase(deletePost.rejected, (state, action) => { + state.isLoading = false + state.isError = true + state.message = action.payload + }) }, }) diff --git a/client/src/index.css b/client/src/index.css index 7a426c5..7b23b00 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -359,3 +359,13 @@ footer { font-size: 1.5rem; } } + +.action-buttons { + display: flex; + justify-content: center; + align-items: center; +} + +.action-buttons button { + margin: auto 0.5rem; +} diff --git a/client/src/pages/Posts.jsx b/client/src/pages/Posts.jsx index d7e9a00..df44c66 100644 --- a/client/src/pages/Posts.jsx +++ b/client/src/pages/Posts.jsx @@ -1,50 +1,68 @@ -import { useEffect } from 'react' -import { useSelector, useDispatch } from 'react-redux' -import { getPosts, reset } from '../features/blog/blogSlice' -import Spinner from '../components/Spinner' -import BackButton from '../components/BackButton' -import PostItem from '../components/PostItem' +import { useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { toast } from "react-toastify"; +import { getPosts, deletePost, reset } from "../features/blog/blogSlice"; +import Spinner from "../components/Spinner"; +import BackButton from "../components/BackButton"; +import PostItem from "../components/PostItem"; const Posts = () => { - const { posts, isLoading, isSuccess } = useSelector( + const { posts, isLoading, isError, isSuccess, message } = useSelector( (state) => state.blog - ) + ); - const dispatch = useDispatch() + const dispatch = useDispatch(); useEffect(() => { return () => { if (isSuccess) { - dispatch(reset()) + dispatch(reset()); } - } - }, [dispatch, isSuccess]) + }; + }, [dispatch, isSuccess]); + + const deletePostAction = (postId) => { + dispatch(deletePost(postId)); + }; + + if (isError) { + toast.error(message); + } useEffect(() => { - dispatch(getPosts()) - }, [dispatch]) + dispatch(getPosts()); + }, [dispatch]); if (isLoading) { - return + return ; } return ( <>
- +

Posts

-
-
-
Title
-
Content
+ {posts.length === 0 ? ( +

No posts found

+ ) : ( +
+
+
Title
+
Content
+
Actions
+
+ {posts.map((post) => ( + + ))}
- {posts.map((post) => ( - - ))} -
+ )} - ) -} + ); +}; -export default Posts +export default Posts;