Blog Form - Copy this React, Tailwind Component to your project
fix this code "use client"; import React, { useState, useEffect } from "react"; import { FaPlus, FaUpload } from "react-icons/fa"; import { MdClose } from "react-icons/md"; import Image from "next/image"; import { useRouter } from "next/navigation"; interface Blogger { title: string; description: string; image: File | null; } const AddBlogs = () => { const [blogTitle, setBlogTitle] = useState(""); const [blogDescription, setBlogDescription] = useState(""); const [blogImage, setBlogImage] = useState<File | null>(null); const [bloggers, setBloggers] = useState<Blogger[]>([]); const [blogge, setBlogge] = useState<Blogger[]>([]); const [loading, setLoading] = useState(false); const [errors, setErrors] = useState<Record<string, string>>({}); const [suggestions, setSuggestions] = useState<string[]>([]); const router = useRouter(); const commonTitles = [ "10 Tips for Effective Time Management", "The Future of Artificial Intelligence", "Healthy Eating Habits for a Better Lifestyle", "Mastering the Art of Public Speaking", "Exploring Hidden Gems: Travel Off the Beaten Path", ]; useEffect(() => { if (blogTitle.length > 2) { const filtered = commonTitles.filter((title) => title.toLowerCase().includes(blogTitle.toLowerCase()) ); setSuggestions(filtered); } else { setSuggestions([]); } }, [blogTitle]); const validateInputs = () => { const newErrors: Record<string, string> = {}; if (!blogTitle.trim()) newErrors.blogTitle = "Blog title is required"; if (!blogDescription.trim()) newErrors.blogDescription = "Blog description is required"; if (!blogImage) newErrors.blogImage = "Blog image is required"; bloggers.forEach((blogger, index) => { if (!blogger.title.trim()) newErrors[`blogger${index}Title`] = "Blogger title is required"; if (!blogger.description.trim()) newErrors[`blogger${index}Description`] = "Blogger description is required"; if (!blogger.image) newErrors[`blogger${index}Image`] = "Blogger image is required"; }); setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); if (validateInputs()) { try { // Prepare form data const formData = new FormData(); formData.append("blogTitle", blogTitle); formData.append("blogDescription", blogDescription); if (blogImage) formData.append("blogImage", blogImage); // Append File object let lastIndex = 0; bloggers.forEach((blogger, index) => { lastIndex = index; if (blogger.title) formData.append(`bloggers[${index}][title]`, blogger.title); if (blogger.description) formData.append(`bloggers[${index}][description]`, blogger.description); if (blogger.image) formData.append(`bloggers[${index}][image]`, blogger.image); // Append File object }); formData.append("bloggerCount", Number(lastIndex).toString()); formData.append("blogDescription", blogDescription); // Post data to your API const response = await fetch("/api/blog/PostBlog", { method: "POST", body: formData, }); if (response.ok) { await response.json(); router.push("/admin/bloglist") } else { console.error("Failed to submit blog", response.statusText); } } catch (error) { console.error("Error submitting blog:", error); } } }; const addBlogger = () => { setLoading(true); setTimeout(() => { setBloggers([...bloggers, { title: "", description: "", image: null }]); setLoading(false); }, 1000); }; const addBloggers = () => { setLoading(true); setTimeout(() => { setBlogge([...blogge, { title: "", description: "", image: null }]); setLoading(false); }, 1000); }; const removeBlogger = (index: number) => { const updatedBloggers = bloggers.filter((_, i) => i !== index); setBloggers(updatedBloggers); }; const removeBloggers = (index: number) => { const updatedBloggers = blogge.filter((_, i) => i !== index); setBlogge(updatedBloggers); }; const handleImageUpload = ( e: React.ChangeEvent<HTMLInputElement>, setter: (value: File | null) => void ) => { const file = e.target.files?.[0]; if (file) { setter(file); // Set the File object directly } }; return ( <div className="min-h-screen bg-gradient-to-br p-4 sm:p-6 md:p-8 lg:p-12"> <div className="max-w-4xl mx-auto bg-white rounded-lg shadow-xl overflow-hidden"> <div className="p-6 sm:p-8 md:p-10 lg:p-12"> <h1 className="text-3xl font-bold text-gray-800 mb-8">Create a New Blog</h1> <form onSubmit={handleSubmit} className="space-y-6"> <div className="space-y-4"> <div> <label htmlFor="blogTitle" className="block text-sm font-medium text-gray-700"> Blog Title </label> <input type="text" id="blogTitle" value={blogTitle} onChange={(e) => setBlogTitle(e.target.value)} className={`mt-1 block py-2.5 pl-2 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 ${ errors.blogTitle ? "border-red-500" : "" }`} placeholder="Enter blog title" aria-describedby="blogTitleError" /> {errors.blogTitle && ( <p id="blogTitleError" className="mt-2 text-sm text-red-600"> {errors.blogTitle} </p> )} {suggestions.length > 0 && ( <ul className="mt-2 bg-white border border-gray-300 rounded-md shadow-sm"> {suggestions.map((suggestion, index) => ( <li key={index} className="px-4 py-2 hover:bg-gray-100 cursor-pointer" onClick={() => setBlogTitle(suggestion)} > {suggestion} </li> ))} </ul> )} </div> <div> <label htmlFor="blogDescription" className="block text-sm font-medium text-gray-700"> Blog Description </label> <textarea id="blogDescription" value={blogDescription} onChange={(e) => setBlogDescription(e.target.value)} className={`mt-1 block w-full pl-2 pt-1 rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 ${ errors.blogDescription ? "border-red-500" : "" }`} rows={4} placeholder="Enter blog description" aria-describedby="blogDescriptionError" ></textarea> {errors.blogDescription && ( <p id="blogDescriptionError" className="mt-2 text-sm text-red-600"> {errors.blogDescription} </p> )} </div> <div> <label htmlFor="blogImage" className="block text-sm font-medium text-gray-700"> Blog Image </label> <div className="mt-1 flex items-center"> <input type="file" id="blogImage" onChange={(e) => handleImageUpload(e, setBlogImage)} className="sr-only" accept="image/*" aria-describedby="blogImageError" /> <div className="flex items-center justify-center gap-3"> <label htmlFor="blogImage" className={`cursor-pointer inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 ${ errors.blogImage ? "border-red-500" : "" }`} > <FaUpload className="mr-2 h-5 w-5 text-gray-400" /> Upload Image </label> {blogImage && ( <Image width={100} height={100} src={URL.createObjectURL(blogImage)} alt={blogImage.name} className="max-w-full h-auto rounded" /> )} </div> </div> {errors.blogImage && ( <p id="blogImageError" className="mt-2 text-sm text-red-600"> {errors.blogImage} </p> )} </div> </div> {/* Bloggers Section */} <div className="space-y-4"> <h2 className="text-2xl font-semibold text-gray-700">Add Bloggers title</h2> {bloggers.map((blogger, index) => ( <div key={index} className="border border-gray-200 rounded-md p-4 relative"> <button type="button" onClick={() => removeBlogger(index)} className="absolute top-2 right-2 text-red-500 hover:text-red-700" aria-label="Remove blogger" > <MdClose size={24} /> </button> <div className="space-y-4"> <div> <label htmlFor={`blogger${index}Title`} className="block text-sm font-medium text-gray-700" > Blogger Title </label> <input type="text" id={`blogger${index}Title`} value={blogger.title} onChange={(e) => setBloggers((prevBloggers) => prevBloggers.map((b, i) => i === index ? { ...b, title: e.target.value } : b ) ) } className={`mt-1 block py-2.5 pl-2 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 ${ errors[`blogger${index}Title`] ? "border-red-500" : "" }`} placeholder="Enter blogger title" aria-describedby={`blogger${index}TitleError`} /> {errors[`blogger${index}Title`] && ( <p id={`blogger${index}TitleError`} className="mt-2 text-sm text-red-600"> {errors[`blogger${index}Title`]} </p> )} </div> <div> <label htmlFor={`blogger${index}Description`} className="block text-sm font-medium text-gray-700" > Blogger Description </label> <textarea id={`blogger${index}Description`} value={blogger.description} onChange={(e) => setBloggers((prevBloggers) => prevBloggers.map((b, i) => i === index ? { ...b, description: e.target.value } : b ) ) } className={`mt-1 block w-full pl-2 pt-1 rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 ${ errors[`blogger${index}Description`] ? "border-red-500" : "" }`} rows={4} placeholder="Enter blogger description" aria-describedby={`blogger${index}DescriptionError`} ></textarea> {errors[`blogger${index}Description`] && ( <p id={`blogger${index}DescriptionError`} className="mt-2 text-sm text-red-600" > {errors[`blogger${index}Description`]} </p> )} </div> <div> <label htmlFor={`blogger${index}Image`} className="block text-sm font-medium text-gray-700" > Blogger Image </label> <div className="mt-1 flex items-center"> <input type="file" id={`blogger${index}Image`} onChange={(e) => handleImageUpload(e, (file) => setBloggers((prevBloggers) => prevBloggers.map((b, i) => i === index ? { ...b, image: file } : b ) ) ) } className="sr-only" accept="image/*" aria-describedby={`blogger${index}ImageError`} /> <div className="flex justify-center items-center gap-4"> <label htmlFor={`blogger${index}Image`} className={`cursor-pointer inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 ${ errors[`blogger${index}Image`] ? "border-red-500" : "" }`} > <FaUpload className="mr-2 h-5 w-5 text-gray-400" /> Upload Image </label> {blogger.image && ( <Image width={100} height={100} src={URL.createObjectURL(blogger.image)} alt={blogger.image.name} className="max-w-full h-auto rounded" /> )} </div> </div> {errors[`blogger${index}Image`] && ( <p id={`blogger${index}ImageError`} className="mt-2 text-sm text-red-600"> {errors[`blogger${index}Image`]} </p> )} </div> </div> <div className="space-y-4"> <h2 className="text-2xl font-semibold text-gray-700">Add Bloggers SECEON</h2> {blogge.map((blogger, indexs) => ( <div key={indexs} className="border border-gray-200 rounded-md p-4 relative"> <button type="button" onClick={() => removeBloggers(indexs)} className="absolute top-2 right-2 text-red-500 hover:text-red-700" aria-label="Remove blogger" > <MdClose size={24} /> </button> <div className="space-y-4"> <div> <label htmlFor={`blogger${index}${indexs}Title`} className="block text-sm font-medium text-gray-700" > Blogger Title </label> <input type="text" id={`blogger$${index}{indexs}Title`} value={blogger.title} onChange={(e) => setBloggers((prevBloggers) => prevBloggers.map((b, i) => i === indexs ? { ...b, title: e.target.value } : b ) ) } className={`mt-1 block py-2.5 pl-2 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 ${ errors[`blogger${index}${indexs}Title`] ? "border-red-500" : "" }`} placeholder="Enter blogger title" aria-describedby={`blogger${index}${indexs}TitleError`} /> {errors[`blogger${index}${indexs}Title`] && ( <p id={`blogger${index}${indexs}TitleError`} className="mt-2 text-sm text-red-600"> {errors[`blogger${index}${indexs}Title`]} </p> )} </div> <div> <label htmlFor={`blogger${index}${indexs}Description`} className="block text-sm font-medium text-gray-700" > Blogger Description </label> <textarea id={`blogger${index}${indexs}Description`} value={blogger.description} onChange={(e) => setBloggers((prevBloggers) => prevBloggers.map((b, i) => i === indexs ? { ...b, description: e.target.value } : b ) ) } className={`mt-1 block w-full pl-2 pt-1 rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 ${ errors[`blogger${index}${indexs}Description`] ? "border-red-500" : "" }`} rows={4} placeholder="Enter blogger description" aria-describedby={`blogger${index}${indexs}DescriptionError`} ></textarea> {errors[`blogger${index}${indexs}Description`] && ( <p id={`blogger${index}${indexs}DescriptionError`} className="mt-2 text-sm text-red-600" > {errors[`blogger${index}${indexs}Description`]} </p> )} </div> <div> <label htmlFor={`blogger${index}${indexs}Image`} className="block text-sm font-medium text-gray-700" > Blogger Image </label> <div className="mt-1 flex items-center"> <input type="file" id={`blogger${index}${indexs}Image`} onChange={(e) => handleImageUpload(e, (file) => setBloggers((prevBloggers) => prevBloggers.map((b, i) => i === indexs ? { ...b, image: file } : b ) ) ) } className="sr-only" accept="image/*" aria-describedby={`blogger${index}${indexs}ImageError`} /> <div className="flex justify-center items-center gap-4"> <label htmlFor={`blogger${index}${indexs}Image`} className={`cursor-pointer inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 ${ errors[`blogger${index}${indexs}Image`] ? "border-red-500" : "" }`} > <FaUpload className="mr-2 h-5 w-5 text-gray-400" /> Upload Image </label> {blogger.image && ( <Image width={100} height={100} src={URL.createObjectURL(blogger.image)} alt={blogger.image.name} className="max-w-full h-auto rounded" /> )} </div> </div> {errors[`blogger${index}${indexs}Image`] && ( <p id={`blogger${index}${indexs}ImageError`} className="mt-2 text-sm text-red-600"> {errors[`blogger${index}${indexs}Image`]} </p> )} </div> </div> </div> ))} <button type="button" onClick={addBloggers} className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500" > <FaPlus className="mr-2 h-5 w-5" /> Add Bloggers SOUCN </button> </div> </div> ))} </div> <div className="flex justify-between"> <button type="button" onClick={addBlogger} className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500" > <FaPlus className="mr-2 h-5 w-5" /> Add Blogger </button> <button type="submit" className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500" > Submit Blog </button> </div> </form> </div> </div> </div> ); }; export default AddBlogs;
