From 623c07a2f26bf8c4a50548a981c32f2a25e321e0 Mon Sep 17 00:00:00 2001 From: devsharmagit Date: Mon, 8 Jul 2024 19:24:43 +0530 Subject: [PATCH 1/2] feat-#419: added post edit page --- backend/middlewares/post-middleware.js | 4 +- frontend/src/App.tsx | 2 + frontend/src/components/form-blog.tsx | 300 +++++++++++++++++++++++++ frontend/src/lib/types.ts | 4 +- frontend/src/pages/add-blog.tsx | 280 +---------------------- frontend/src/pages/admin-blogs.tsx | 10 +- frontend/src/pages/details-page.tsx | 39 +++- frontend/src/pages/edit-blog.tsx | 50 +++++ frontend/src/pages/signin-page.tsx | 2 +- frontend/src/pages/signup-page.tsx | 2 +- frontend/src/types/post-type.tsx | 1 + 11 files changed, 409 insertions(+), 285 deletions(-) create mode 100644 frontend/src/components/form-blog.tsx create mode 100644 frontend/src/pages/edit-blog.tsx diff --git a/backend/middlewares/post-middleware.js b/backend/middlewares/post-middleware.js index 4a7dd72d..e6d04261 100644 --- a/backend/middlewares/post-middleware.js +++ b/backend/middlewares/post-middleware.js @@ -11,12 +11,12 @@ export const isAuthorMiddleware = async (req, res, next) => { } console.log(post.authorId, userId); - if (post.authorId.toString() !== userId) { + if (post?.authorId?.toString() !== userId) { return res .status(HTTP_STATUS.FORBIDDEN) .json({ message: RESPONSE_MESSAGES.POSTS.NOT_ALLOWED }); } - next(); + return next(); } catch (error) { res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ message: error.message }); } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e387b191..8c4a3042 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -15,6 +15,7 @@ import RequireAuth from './components/require-auth'; import useThemeClass from './utils/theme-changer'; import AdminContainer from './components/admin-container'; import { Role } from './types/role-type.tsx'; +import EditBlog from './pages/edit-blog.tsx'; function App() { useLayoutEffect(() => { @@ -34,6 +35,7 @@ function App() { }> } /> + } /> }> }> diff --git a/frontend/src/components/form-blog.tsx b/frontend/src/components/form-blog.tsx new file mode 100644 index 00000000..53fc5ca0 --- /dev/null +++ b/frontend/src/components/form-blog.tsx @@ -0,0 +1,300 @@ +import { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import navigateBackBlackIcon from '@/assets/svg/navigate-back-black.svg'; +import navigateBackWhiteIcon from '@/assets/svg/navigate-back-white.svg'; +import ModalComponent from '@/components/modal'; +import CategoryPill from '@/components/category-pill'; +import { categories } from '@/utils/category-colors'; +import userState from '@/utils/user-state'; +import axiosInstance from '@/helpers/axios-instance'; +import { AxiosError, isAxiosError } from 'axios'; +import { useForm } from 'react-hook-form'; +import { TFormBlogSchema, formBlogSchema } from '@/lib/types'; +import { zodResolver } from '@hookform/resolvers/zod'; +import Post from '@/types/post-type'; +import useAuthData from '@/hooks/useAuthData'; + +interface FormBlogPropType { + type: 'new' | 'edit'; + postId?: string; + post?: Post; +} + +function FormBlog({ type, postId, post }: FormBlogPropType) { + const [selectedImage, setSelectedImage] = useState(''); + const { + register, + handleSubmit, + reset, + setValue, + trigger, + formState: { errors }, + watch, + } = useForm({ + resolver: zodResolver(formBlogSchema), + defaultValues: { + title: post?.title || '', + authorName: post?.authorName || '', + imageLink: post?.imageLink || '', + categories: post?.categories || [], + description: post?.description || '', + isFeaturedPost: false, + }, + }); + const formData = watch(); + const handleImageSelect = (imageUrl: string) => { + setSelectedImage(imageUrl); + }; + + const [modal, setmodal] = useState(false); + const userData = useAuthData(); + + //checks the length of the categories array and if the category is already selected + const isValidCategory = (category: string): boolean => { + return formData.categories.length >= 3 && !formData.categories.includes(category); + }; + + const handleCategoryClick = (category: string) => { + if (isValidCategory(category)) return; + + if (formData.categories.includes(category)) { + setValue( + 'categories', + formData.categories.filter((cat) => cat !== category) + ); + } else { + setValue('categories', [...formData.categories, category]); + } + trigger('categories'); + }; + + const handleselector = () => { + setValue('imageLink', selectedImage); + setmodal(false); + }; + const handleCheckboxChange = () => { + setValue('isFeaturedPost', !formData.isFeaturedPost); + }; + const onSumbit = async () => { + try { + let postPromise; + if (type === 'new') { + postPromise = axiosInstance.post('/api/posts/', formData); + } + + if (type === 'edit' && postId) { + if (userData?.role === 'ADMIN') { + postPromise = axiosInstance.patch(`/api/posts/admin/${postId}`, formData); + } else { + postPromise = axiosInstance.patch(`/api/posts/${postId}`, formData); + } + } + if (postPromise) + toast.promise(postPromise, { + pending: 'Creating blog post...', + success: { + render() { + reset(); + navigate('/'); + return 'Blog created successfully'; + }, + }, + error: { + render({ data }) { + if (data instanceof AxiosError) { + if (data?.response?.data?.message) { + return data?.response?.data?.message; + } + } + return 'Blog creation failed'; + }, + }, + }); + if (postPromise) return (await postPromise).data; + } catch (error: unknown) { + if (isAxiosError(error)) { + navigate('/'); + userState.removeUser(); + console.error(error.response?.data?.message); + } else { + console.log(error); + } + } + }; + const navigate = useNavigate(); + const [isDarkMode, setIsDarkMode] = useState(null); + useEffect(() => { + const storedTheme = localStorage.getItem('theme'); + setIsDarkMode(storedTheme === 'dark'); + }, []); + + function Asterisk() { + return *; + } + + return ( +
+
+
+
+ theme navigate(-1)} + className="active:scale-click h-5 w-10" + /> +
+

+ Create Blog +

+
+
+
+
+
+ +
+ +
+
+ Blog title +
+ + {errors.title && ( + {`${errors.title.message}`} + )} +
+ +
+
+ Blog content +
+