From 40509c0b5d1f219c06e89b25ba989dbace86290d Mon Sep 17 00:00:00 2001 From: lkanwat Date: Fri, 15 Dec 2023 18:22:03 +0530 Subject: [PATCH] tag-#77: use the react-hook-form & zod for from validation --- frontend/src/lib/blog.zod.ts | 26 +++++++ frontend/src/pages/add-blog.tsx | 133 ++++++++++++++------------------ 2 files changed, 84 insertions(+), 75 deletions(-) create mode 100644 frontend/src/lib/blog.zod.ts diff --git a/frontend/src/lib/blog.zod.ts b/frontend/src/lib/blog.zod.ts new file mode 100644 index 00000000..f21aa651 --- /dev/null +++ b/frontend/src/lib/blog.zod.ts @@ -0,0 +1,26 @@ +import { z } from 'zod'; + +export const FormDataSchema = z.object({ + title: z.string().refine((value) => value.trim().split(/\s+/).length > 3, { + message: 'Oops! Title needs more spice. Give it at least 3 words.', + }), + authorName: z.string().refine((value) => value.trim().length >= 3 && value.trim().length <= 15, { + message: 'Hey, Your name should be more than 5 letters.', + }), + imageLink: z.string().refine((value) => isValidImageLink(value), { + message: 'Hmm... Image link should end with .jpg, .jpeg, .webp, or .png.', + }), + categories: z.array(z.string()).refine((value) => value.length <= 3, { + message: 'Easy there! Select up to 3 categories.', + }), + description: z.string(), + isFeaturedPost: z.boolean(), +}); + +// validate the image link +const isValidImageLink = (value: string) => { + const imageLinkRegex = /\.(jpg|jpeg|png|webp)$/i; + return imageLinkRegex.test(value); +}; + +export type TFormData = z.infer; diff --git a/frontend/src/pages/add-blog.tsx b/frontend/src/pages/add-blog.tsx index 0dc64d95..903d900b 100644 --- a/frontend/src/pages/add-blog.tsx +++ b/frontend/src/pages/add-blog.tsx @@ -1,3 +1,4 @@ +import { useForm } from 'react-hook-form'; import axios from 'axios'; import { ChangeEvent, FormEvent, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -9,85 +10,60 @@ import ModalComponent from '@/components/modal'; import CategoryPill from '@/components/category-pill'; import { categories } from '@/utils/category-colors'; -type FormData = { - title: string; - authorName: string; - imageLink: string; - categories: string[]; - description: string; - isFeaturedPost: boolean; -}; +import { zodResolver } from '@hookform/resolvers/zod'; + +import { FormDataSchema, TFormData } from '@/lib/blog.zod'; + function AddBlog() { + const { + register, + handleSubmit, + getValues, + setValue, + formState: { errors, isValid }, + } = useForm({ + resolver: zodResolver(FormDataSchema), + }); + const [selectedImage, setSelectedImage] = useState(''); const handleImageSelect = (imageUrl: string) => { setSelectedImage(imageUrl); + setValue('imageLink', imageUrl); }; const [modal, setmodal] = useState(false); - const [formData, setFormData] = useState({ - title: '', - authorName: '', - imageLink: '', - categories: [], - description: '', - isFeaturedPost: false, - }); + // const [isFormValid, setIsFormValid] = useState(false); // New state to track form validity - const handleInputChange = (e: ChangeEvent) => { - const { name, value } = e.target; - setFormData({ ...formData, [name]: value }); - }; + // useEffect(() => { + // // Update the isFormValid state whenever the form validity changes + // setIsFormValid(isValid); + // }, [isValid]); const handleCategoryClick = (category: string) => { - if (formData.categories.includes(category)) { - setFormData({ - ...formData, - categories: formData.categories.filter((cat) => cat !== category), - }); + const currentCategories = getValues('categories') || []; + + if (currentCategories.includes(category)) { + setValue( + 'categories', + currentCategories.filter((cat: string) => cat !== category) + ); } else { - setFormData({ - ...formData, - categories: [...formData.categories, category], - }); + setValue('categories', [...currentCategories, category]); } }; + const handleselector = () => { - setFormData({ - ...formData, - imageLink: selectedImage, - }); + setValue('imageLink', selectedImage); setmodal(false); }; - const handleCheckboxChange = () => { - setFormData({ ...formData, isFeaturedPost: !formData.isFeaturedPost }); - }; - const validateFormData = () => { - if ( - !formData.title || - !formData.authorName || - !formData.imageLink || - !formData.description || - formData.categories.length === 0 - ) { - toast.error('All fields must be filled out.'); - return false; - } - const imageLinkRegex = /\.(jpg|jpeg|png|webp)$/i; - if (!imageLinkRegex.test(formData.imageLink)) { - toast.error('Image URL must end with .jpg, .jpeg, .webp or .png'); - return false; - } - if (formData.categories.length > 3) { - toast.error('Select up to three categories.'); - return false; - } - return true; - }; - const handleSubmit = async (e: FormEvent) => { - e.preventDefault(); - if (validateFormData()) { + const formSubmit = async (formData: TFormData) => { + console.log('error', errors); + + console.log('From adad', formData); + + if (isValid) { try { const response = await axios.post(import.meta.env.VITE_API_PATH + '/api/posts/', formData); @@ -97,11 +73,12 @@ function AddBlog() { } else { toast.error('Error: ' + response.data.message); } - } catch (err: any) { - toast.error('Error: ' + err.message); + } catch (error: any) { + toast.error('Error: ' + error.message); } } }; + const navigate = useNavigate(); const [isDarkMode, setIsDarkMode] = useState( @@ -143,18 +120,17 @@ function AddBlog() {
-
+
@@ -164,14 +140,14 @@ function AddBlog() { Blog title
+ {errors.title ?

{`${errors.title.message}`}

: null}
@@ -179,12 +155,11 @@ function AddBlog() { Blog content