diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 00180807..4ff0c26a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -16,6 +16,7 @@ import RequireAuthBlog from './components/require-auth-blog'; 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(() => { @@ -35,6 +36,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 +
+