Im creating a course marketplace using MERN stack but i get the React-Quill error when I try to open the page of Edit Course - javascript

Iam getting this error when I load the page for course edit
Unhandled Runtime Error Error: You are passing the delta object from
the onChange event back as value. You most probably want
editor.getContents() instead. See:
https://github.com/zenoamaro/react-quill#using-deltas
Below is the code of the edit form page CreateCourseForm.js which im importing to the other page [slug].js
import { Badge } from "antd";
import dynamic from "next/dynamic";
const QuillNoSSRWrapper = dynamic(import('react-quill'), {
ssr: false,
loading: () => <p>Loading ...</p>,
});
const CreateCourseForm = ({
handleSubmit,
handleCourseImageUpload,
handleChange,
values,
setValues,
categories,
previewImage,
imageUploadButtonText,
editCategories,
handleCourseImageRemove = (f) => f,
editPage = false,
}) => {
const handleQuillEdit = (value) => {
setValues((prev) => {
return {
...prev,
description: value
}
});
};
const modules = {
toolbar: [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block', 'image'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'indent': '-1'}, { 'indent': '+1' }], // outdent/indent
[{ 'script': 'sub'}, { 'script': 'super' }], // superscript/subscript
// custom dropdown
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
[{ 'align': [] }],
['clean']
],
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
},
};
return (
<>
{values &&
<form onSubmit={handleSubmit}>
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Course Name"
name="name"
value={values.name}
onChange={handleChange}
/>
</div>
<div className="form-group">
<QuillNoSSRWrapper theme="snow"
placeholder="Course Description"
modules={modules}
value={values.description || ''}
onChange={handleQuillEdit}/>
</div>
<div className="row form-group">
<label>Course Fee</label>
<div className="col-md-6">
<select
className="form-control"
value={values.paid}
onChange={(v) => setValues({...values, paid: !values.paid})}
>
<option value={false}>Free</option>
<option value={true}>Paid</option>
</select>
</div>
{values.paid &&
<div className="col-md-6">
<input
type="number"
className="form-control"
placeholder="Enter Price in $"
name="price"
value={values.price}
onChange={handleChange}
/>
</div>
}
</div>
<div className="row">
<div className="col-md-8">
<div className="form-group">
<label className="btn btn-outline-secondary btn-block text-left">
{imageUploadButtonText}
<input
type="file"
name="image"
onChange={handleCourseImageUpload}
accept="image/*"
hidden
/>
</label>
</div>
</div>
{previewImage && (
<div className="col-md-4" onClick={handleCourseImageRemove} style={{cursor:'pointer'}}>
<Badge count={"Delete"} style={{color: '#fe4a55'}}>
<img src={previewImage} width="50" alt="image" />
</Badge>
</div>
)}
{editPage && values.image && (
<div className="col-md-2">
<img src={values.image.Location} width="50" alt="image" />
</div>
)}
<p>** If you don't upload any course image then Progyan will update it with a default system image</p>
</div>
<div className="form-group">
<label>Category</label>
<select className="form-control" value={values.category} name="category" onChange={handleChange}>
<option value="none" disabled>Select an Option</option>
{!editPage && categories.map((c) => (
<option value={c.name}>{c.name}</option>
))}
{editPage && editCategories.map((c) => (
<option value={c.name}>{c.name}</option>
))}
</select>
</div>
<button
onClick={handleSubmit}
disabled={values.loading || values.uploading || !values.name || !values.description}>
{values.loading ? "Saving..." : "Save & Continue"}
</button>
</form>
}
</>
);
};
export default CreateCourseForm;
And here is the [slug].js page code
const CourseEdit = () => {
{/*States For Updating Course*/}
const [values, setValues] = useState({
name: "",
description: "",
price: "",
uploading: false,
paid: false,
category: "",
loading: false,
lessons : [],
assessments : [],
});
const [image,setImage] = useState({});
const [previewImage, setPreviewImage] = useState("");
const [imageUploadButtonText, setImageUploadButtonText] = useState('Upload Course Thumbnail');
{/*States For Updating Lesson*/}
const [lessonVisible,setLessonVisible] = useState(false);
const [assessmentVisible,setAssessmentVisible] = useState(false);
const [current, setCurrent] = useState({});
const [updateVideoButtonText, setUpdateVideoButtonText] = useState("Update Lesson Video");
const [progress, setProgress] = useState(0);
const [uploading, setUploading] = useState(false);
const router = useRouter();
const {slug} = router.query;
useEffect(() => {loadCourse();}, [slug]);
const [editCategories, setEditCategories] = useState([]);
useEffect(() => {loadCategories(); }, []);
const loadCategories = async () => {
try {
let { data } = await axios.get("/api/categories");
setEditCategories(data);
} catch (err) {
toast.error(err.response.data);
}
};
{/*Function for load the data from MongoDB to the form*/}
const loadCourse = async () => {
const {data} = await axios.get(`/api/course/${slug}`);
if(data) setValues(data);
if(data && data.image) setImage(data.image);
}
{/*Function for saving all of the data from input form*/}
const handleChange = (e) => {
setValues({...values, [e.target.name]: e.target.value});
};
{/*Function for uploading the course image to AWS S3*/}
const handleCourseImageUpload = (e) => {
let file = e.target.files[0];
setPreviewImage(window.URL.createObjectURL(file));
setImageUploadButtonText(file.name);
setValues({...values, loading: true});
//Resize Image
Resizer.imageFileResizer(file, 750, 500, "JPEG", 90, 0, async (uri) => {
try{
let {data} = await axios.post(`/api/course/upload-image`,{
image: uri,
});
//set Image in the state
setImage(data);
setValues({...values, loading: false});
}catch(err){
setValues({...values, loading: false});
toast.error(err.response.data);
}
});
};
{/*Function for removing the course image from AWS S3*/}
const handleCourseImageRemove = async (e) => {
try{
setValues({...values, loading: true});
const res = await axios.post(`/api/course/remove-image`, {image});
setImage({});
setPreviewImage("");
setImageUploadButtonText('Upload Course Thumbnail');
setValues({...values, loading: false});
}catch(err){
setValues({...values, loading: false});
toast.error(err.response.data);
}
}
{/*Function for updating the course to MongoDB*/}
const handleSubmit = async (e) => {
e.preventDefault();
try{
if(!values.paid){ values.price=0; }
const {data} = await axios.put(`/api/course/${slug}`, {
...values,
image,
});
toast.success("Course Updated Successfully..!!");
}catch(err){
toast.error(err.response.data);
}
};
{/*Function for updating the lesson video to AWS S3*/}
const handleVideoUpdate = async (e) => {
//Remove Previous Video
if(current.video && current.video.Location){
const res = await axios.post(`/api/course/video-remove/${values.instructor._id}`, current.video);
toast.success(res);
}
//Upload
const file = e.target.files[0];
setUpdateVideoButtonText(file.name);
setUploading(true);
//Send Video as form data
const videoData = new FormData();
videoData.append('video',file);
videoData.append('courseId',values._id);
//Save progress bar and send video form data to backend
const {data} = await axios.post(
`/api/course/video-upload/${values.instructor._id}/${values.slug}`,
videoData,
{
onUploadProgress: (e) => setProgress(Math.round((100 * e.loaded) / e.total)),
}
);
setCurrent({...current, video: data});
setUploading(false);
};
{/*Function for dragging the lesson sequence*/}
const handleLessonDrag = (e, index) => {
e.dataTransfer.setData("itemIndex", index);
};
{/*Function for dropping the lesson sequence*/}
const handleLessonDrop = async (e, index) => {
const movingItemIndex = e.dataTransfer.getData("itemIndex");
const targetItemIndex = index;
let allLessons = values.lessons;
let movingItem = allLessons[movingItemIndex]; //Clicked or Dragged Item to Reorder
allLessons.splice(movingItemIndex, 1); //Remove 1 item from the given index
allLessons.splice(targetItemIndex, 0, movingItem); //Push item after target item index
setValues({...values, lessons: [...allLessons]});
//Save the new lessons order in MongoDB
const {data} = await axios.put(`/api/course/${slug}`,{...values, image});
toast.success("Lesson Rearrange Successfully");
};
{/*Function for deleting the lesson from the course & MongoDB*/}
const handleDeleteLesson = async (index) => {
Swal.fire({
title: 'Are you sure?',
text: "Do You Want To Delete This Lesson..??",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, Delete It.!'
}).then(async (result) => {
if (result.isConfirmed) {
let allLessons = values.lessons;
const removed = allLessons.splice(index, 1);
setValues({...values, lessons: allLessons});
const {data} = await axios.put(`/api/course/${slug}/${removed[0]._id}`);
toast.success("Lesson Deleted Successfully..!!!");
}
});
};
{/*Function for updating the lesson to MongoDB*/}
const handleUpdateLesson = async (e) => {
e.preventDefault();
const {data}= await axios.put(`/api/course/lesson/${slug}/${current._id}`, current);
setUpdateVideoButtonText("Update Lesson Video");
setLessonVisible(false);
//Update UI
if(data.ok){
let arr = values.lessons;
const index = arr.findIndex((el) => el._id === current._id);
arr[index] = current;
setValues({...values, lessons:arr});
toast.success("Lesson Updated Successfully..!");
}
};
{/*Function for dragging the assessment sequence*/}
const handleAssessmentDrag = (e, index) => {
e.dataTransfer.setData("itemIndex", index);
};
{/*Function for dropping the assessment sequence*/}
const handleAssessmentDrop = async (e, index) => {
const movingItemIndex = e.dataTransfer.getData("itemIndex");
const targetItemIndex = index;
let allAssessments = values.assessments;
let movingItem = allAssessments[movingItemIndex]; //Clicked or Dragged Item to Reorder
allAssessments.splice(movingItemIndex, 1); //Remove 1 item from the given index
allAssessments.splice(targetItemIndex, 0, movingItem); //Push item after target item index
setValues({...values, assessments: [...allAssessments]});
//Save the new assessment order in MongoDB
const {data} = await axios.put(`/api/course/${slug}`,{...values, image});
toast.success("Assessment Rearrange Successfully");
};
{/*Function for deleting the assessment from the course & MongoDB*/}
const handleDeleteAssessment = async (index) => {
Swal.fire({
title: 'Are you sure?',
text: "Do You Want To Delete This Assessment..??",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, Delete It.!'
}).then(async (result) => {
if (result.isConfirmed) {
let allAssessments = values.assessments;
const removed = allAssessments.splice(index, 1);
setValues({...values, assessments: allAssessments});
const {data} = await axios.put(`/api/course/assessment/${slug}/${removed[0]._id}`);
toast.success("Assessment Deleted Successfully..!!!");
}
});
};
{/*Function for updating the assessment to MongoDB*/}
const handleUpdateAssessment = async (e) => {
e.preventDefault();
const {data}= await axios.put(`/api/course/assessment/${slug}/${current._id}`, current);
setAssessmentVisible(false);
//Update UI
if(data.ok){
let arr = values.assessments;
const index = arr.findIndex((el) => el._id === current._id);
arr[index] = current;
setValues({...values, assessments:arr});
toast.success("Assessment Updated Successfully..!");
}
};
return (
<InstructorRoute>
<React.Fragment>
<PageBanner
pageTitle="Edit Course"
homePageUrl="/instructor"
homePageText="Dashboard"
activePageText="Edit Course"
/>
<div className="footer-area pt-100 pb-70">
<div className="container">
<div className="login-form">
<CreateCourseForm
handleSubmit={handleSubmit}
handleCourseImageUpload={handleCourseImageUpload}
handleChange={handleChange}
values={values}
setValues={setValues}
editCategories={editCategories}
previewImage={previewImage}
imageUploadButtonText={imageUploadButtonText}
handleCourseImageRemove={handleCourseImageRemove}
editPage={true}
/>
</div>
</div>
</div>
{values.lessons && values.lessons.length > 0 &&
<div className="about-area bg-fef8ef ptb-100">
<div className="container">
<div className="section-title">
<h5>Drag & Drop the Lessons to Change The Sequence</h5>
</div>
<List
onDragOver={(e) => e.preventDefault()}
dataSource={values && values.lessons}
renderItem={(item, index) => (
<Item
draggable
onDragStart={(e) => handleLessonDrag(e, index)}
onDrop={(e) => handleLessonDrop(e, index)}>
<div className="single-courses-item">
<div className="courses-content d-flex justify-content-between">
<h3>{index+1}) {item.title.length > 10 ? item.title.substring(0,10)+"...": item.title}</h3>
<div className="float-right">
<EditOutlined className="complementary-btn mx-5" onClick={() => {
setLessonVisible(true);
setCurrent(item);
}}/>
<DeleteOutlined
className="text-danger"
onClick={() => handleDeleteLesson(index)}/>
</div>
</div>
</div>
</Item>
)}>
</List>
</div>
<div className="shape1"><img src="/images/shape1.png" alt="image" /></div>
<div className="shape2"><img src="/images/shape2.png" alt="image" /></div>
<div className="shape3"><img src="/images/shape3.png" alt="image" /></div>
<div className="shape4"><img src="/images/shape4.png" alt="image" /></div>
</div>
}
{values.assessments && values.assessments.length > 0 &&
<div className="main-banner-wrapper ptb-100">
<div className="container">
<div className="section-title">
<h5>Drag & Drop the Assessment to Change The Sequence</h5>
</div>
<List
onDragOver={(e) => e.preventDefault()}
dataSource={values && values.assessments}
renderItem={(item, index) => (
<Item
draggable
onDragStart={(e) => handleAssessmentDrag(e, index)}
onDrop={(e) => handleAssessmentDrop(e, index)}>
<div className="single-courses-item">
<div className="courses-content d-flex justify-content-between">
<h3>{index+1}) {item.question.length > 10 ? item.question.substring(0,10)+"...": item.question}</h3>
<div className="float-right">
<EditOutlined className="complementary-btn mx-5" onClick={() => {
setAssessmentVisible(true);
setCurrent(item);
}}/>
<DeleteOutlined
className="text-danger"
onClick={() => handleDeleteAssessment(index)}/>
</div>
</div>
</div>
</Item>
)}>
</List>
</div>
<div class="banner-shape14"><img src="/images/banner-shape15.png" alt="image"/></div>
<div class="banner-shape15"><img src="/images/banner-shape16.png" alt="image"/></div>
<div class="banner-shape16"><img src="/images/banner-shape17.png" alt="image"/></div>
<div class="banner-shape17"><img src="/images/banner-shape18.png" alt="image"/></div>
<div class="banner-shape18"><img src="/images/banner-shape19.png" alt="image"/></div>
</div>
}
<div className="newsletter-modal" style={{display: lessonVisible?"block":"none"}}>
<div className="newsletter-modal-content">
<a className="close-btn" onClick={() => setLessonVisible(false)}>Close</a>
<UpdateLessonForm
current={current}
setCurrent={setCurrent}
handleVideoUpdate={handleVideoUpdate}
handleUpdateLesson={handleUpdateLesson}
updateVideoButtonText={updateVideoButtonText}
progress={progress}
uploading={uploading}
/>
</div>
</div>
<div className="newsletter-modal" style={{display: assessmentVisible?"block":"none"}}>
<div className="newsletter-modal-content">
<a className="close-btn" onClick={() => setAssessmentVisible(false)}>Close</a>
<UpdateAssessmentForm
current={current}
setCurrent={setCurrent}
handleUpdateAssessment={handleUpdateAssessment}
/>
</div>
</div>
<div className="lines">
<div className="line"></div>
<div className="line"></div>
<div className="line"></div>
</div>
</React.Fragment>
</InstructorRoute>
);
};
export default CourseEdit;

Related

How to create dynamic input field for searching in React JS and Node JS

I want to create multiple dynamic fields for searching (so I can add or subtract text fields to write the search), here's my code on the backend to find the requestor and the frontend which is still not integrated with the backend (I still following the tutorial steps to create a form whose fields are dynamic)
how do I get the two parts to be properly integrated and can produce data search results when pressing the handlesubmit button?
here I use react JS, node JS, express, and MySQL
thanks
for backend
export const getRequestor = async (req: TypedRequestQuery<{lastId: string, search_requestor:string}>, res: Response) =>{
const searchRequestor = req.query.search_requestor || "";
const resultRequestor = await Product.findAll({
where:{
[Op.or]: [
{title_dev:{ //requestor
[Op.like]: '%'+searchRequestor+'%'
}}]
},
order:[
['id_project', 'ASC']
]
});
res.json({
resultRequestor: resultRequestor,
});
}
for frontend
const Audit = () => {
const [requestors, setRequestors] = useState([]);
const [keyword, setKeyword] = useState("");
const [query, setQuery] = useState("");
useEffect(() => {
getRequestor();
}, [keyword]);
const getRequestor = async () => {
const response = await axios.get(
`http://localhost:5001/requestor?search_requestor=${keyword}`
);
setRequestors(response.data.resultRequestor);
};
const [inputFieldsRequestor, setInputFieldsRequestor] = useState([
{idRequestor: uuidv4(), requestor: ''},
]);
const handleSubmitRequestor = (e) => {
e.preventDefault();
console.log("InputFieldsRequestor", inputFieldsRequestor);
};
const handleSubmitAll = (e) => {
e.preventDefault();
console.log("InputFieldsRequestor", inputFieldsRequestor);
console.log("InputFieldsPeriod", inputFieldsPeriod);
};
const handleChangeInputRequestor = (idRequestor, event) => {
const newInputFieldsRequestor = inputFieldsRequestor.map(i => {
if(idRequestor === i.idRequestor){
i[event.target.name] = event.target.value
}
return i;
})
setInputFieldsRequestor(newInputFieldsRequestor);
}
const handleAddFieldsRequestor = () =>{
setInputFieldsRequestor([...inputFieldsRequestor, {idRequestor: uuidv4(), requestor:''}])
// setRequestors([...requestors]);
}
const handleRemoveFieldsRequestor = idRequestor => {
const values = [...inputFieldsRequestor];
values.splice(values.findIndex(value => value.idRequestor === idRequestor), 1);
setInputFieldsRequestor(values);
}
const submitrequestor= (e) =>{
e.preventDefault();
setKeyword(query);
console.log("Requestor", requestors);
}
return(
<div>
<form className='form-horizontal' onSubmit={handleSubmitRequestor}>
{inputFieldsRequestor.map(inputFieldRequestor => (
<div key={inputFieldRequestor.idRequestor}>
<div className="form-group row">
<label className="col-sm-2 col-form-label">Requestor</label>
<div className="col-sm-10">
<input type="text"
name="requestor"
className="form-control"
variant="filled"
value={inputFieldRequestor.requestor}
onChange={event => handleChangeInputRequestor(inputFieldRequestor.idRequestor, event)}
placeholder="Requestor" />
<button className="offset-sm-1 col-sm-2" disabled={inputFieldsRequestor.length === 1}
onClick={() => handleRemoveFieldsRequestor(inputFieldRequestor.idRequestor)}>
-
</button >
<button className="offset-sm-1 col-sm-2" onClick={handleAddFieldsRequestor}>
+
</button>
</div>
</div>
</div>
))}
<button
className="btn btn-danger"
type='submit'
onClick={handleSubmitRequestor}
>
send
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>)}

how get new data from database in reactjs

My projct is a note app where you can can add notes and delete notes. I want to get data from the database and show it but I have to delete note twice, add note or reload page to show the new data.
notesList.jsx
this is my main component
i send getNotes() to another component for i can get new datas
const NotesList = () => {
const [notes, setNotes] = useState([]);
const getNotes = async () => {
const getNoteInformation = {
email: localStorage.getItem("tokenEmail"),
};
const response = await axios.post(
"http://localhost:9000/api/note/get",
getNoteInformation
);
try {
setNotes(response.data.data);
} catch (error) {}
};
const handleAddNote = async (tasktext) => {
const addNoteInformation = {
email: localStorage.getItem("tokenEmail"),
taskText: tasktext,
date: moment(new Date()).locale("fa").format("YYYY/MM/DD").toString(),
};
if (addNoteInformation.email && addNoteInformation.taskText) {
try {
await axios.post(
"http://localhost:9000/api/note/add",
addNoteInformation
);
} catch (error) {}
}
};
const handleDeleteNote = async (id) => {
const deletedInformaion = {
email: localStorage.getItem("tokenEmail"),
noteId: id,
};
if (deletedInformaion.email && deletedInformaion.noteId) {
await axios.post(
"http://localhost:9000/api/note/deleted",
deletedInformaion
);
}
};
useEffect(() => {
getNotes();
}, []);
return (
<>
<Navbar />
<div className="container">
<div className="row">
{notes
.filter((notes) => notes != null)
.map((notes, index) => (
<Note
key={index}
text={notes.text}
date={notes.date}
id={notes._id}
deletenote={handleDeleteNote}
getnote={getNotes}
/>
))}
<AddNote getnote={getNotes} addnote={handleAddNote} />
</div>
</div>
</>
);
};
note.jsx
const Note = (props) => {
const handleDeleteNote = () => {
props.deletenote(props.id);
props.getnote();
};
return (
<>
<div className="col-lg-4 col-md-6 col-sm-12 p-4">
<div className="note d-flex flex-column">
<span className="note-top overflow-auto m-2 ps-2">{props.text}</span>
<div className="note-bottom d-flex justify-content-between flex-row-reverse mt-auto">
<small>{props.date}</small>
<MdDelete
onClick={handleDeleteNote}
className="delete-icon"
size="1.3rem"
color="#bb86fc"
/>
</div>
</div>
</div>
</>
);
};
addNote.jsx
const AddNote = (props) => {
let addNoteText = useRef();
const handleAddNote = async () => {
props.addnote(addNoteText.current.value);
props.getnote();
addNoteText.current.value = "";
};
return (
<div className="col-lg-4 col-md-6 col-sm-12 p-4">
<div className="add-note-box d-flex flex-column justify-content-between">
<div className="top-box">
<textarea
placeholder="یادداشت خود را وارد کنید ......"
class="form-control"
rows={7}
ref={addNoteText}
></textarea>
</div>
<BsFillPlusCircleFill
onClick={handleAddNote}
className="plus-icon"
size="1.3rem"
color="#bb86fc"
/>
</div>
</div>
);
};
You didn't update state after sending the respective add and delete request, that's why you need to refresh to get the updated data. This is the fix:
const handleAddNote = async (tasktext) => {
const addNoteInformation = {
email: localStorage.getItem("tokenEmail"),
taskText: tasktext,
date: moment(new Date()).locale("fa").format("YYYY/MM/DD").toString(),
};
if (addNoteInformation.email && addNoteInformation.taskText) {
try {
await axios.post(
"http://localhost:9000/api/note/add",
addNoteInformation
);
setNotes([...notes, addNoteInformation]); // update state using spread operator and put the new one to the end
} catch (error) {}
}
};
const handleDeleteNote = async (id) => {
const deletedInformaion = {
email: localStorage.getItem("tokenEmail"),
noteId: id,
};
if (deletedInformaion.email && deletedInformaion.noteId) {
await axios.post(
"http://localhost:9000/api/note/deleted",
deletedInformaion
);
setNotes(notes.filter(note => note.id !== id)) // update state using filter function
}
};
Alternativelly, when adding notes, you can update state using the response object from your request, since the id of newly created note maybe generated by your database. It dependes on the actual response object from the axios request:
const response = await axios.post();
setNotes([...notes, response.data]);

Wrong document gets updated when using updateDoc from Firebase/Firestore

I use prop drilling to pass down the id value of the document, but every time I click on a document to update it using updateDoc, the same document gets updated(always the latest one added), not the one I clicked on. I don't understand why the unique IDs don't get passed down correctly to the function, or whether that's the problem. I use deleteDoc this way and it's working perfectly. Any help will be appreciated.
This is where I get the id value from
const getPosts = useCallback(async (id) => {
const data = await getDocs(postCollectionRef);
setPosts(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
});
useEffect(() => {
getPosts();
}, [deletePost]);
return (
<div className={classes.home}>
<ul className={classes.list}>
{posts.map((post) => (
<BlogCard
key={post.id}
id={post.id}
title={post.title}
image={post.image}
post={post.post}
date={post.date}
showModal={showModal}
setShowModal={setShowModal}
deletePost={() => {
deletePost(post.id);
}}
showUpdateModal={showUpdateModal}
setShowUpdateModal={setShowUpdateModal}
/>
))}
</ul>
</div>
);
This is where I pass through the id value to the update modal component for each document:
function BlogCard(props) {
const [postIsOpen, setPostIsOpen] = useState(false);
const modalHandler = () => {
props.setShowModal((prevState) => {
return (prevState = !prevState);
});
};
const updateModalHandler = () => {
props.setShowUpdateModal((prevState) => {
return (prevState = !prevState);
});
};
const handleView = () => {
setPostIsOpen((prevState) => {
return (prevState = !prevState);
});
};
return (
<>
{props.showUpdateModal && (
<UpdateModal
showUpdateModal={props.showUpdateModal}
setShowUpdateModal={props.setShowUpdateModal}
id={props.id}
title={props.title}
image={props.image}
post={props.post}
/>
)}
{props.showModal && (
<DeleteModal
showModal={props.showModal}
setShowModal={props.setShowModal}
deletePost={props.deletePost}
/>
)}
<div className={classes.blogCard} id={props.id}>
<div className={classes.head}>
<p className={classes.title}> {props.title}</p>
<div className={classes.buttons}>
<button className={classes.editButton} onClick={updateModalHandler}>
Edit
</button>
<button className={classes.removeButton} onClick={modalHandler}>
Delete
</button>
</div>
</div>
<p className={classes.date}>{props.date}</p>
<img src={props.image} alt="image" />
{!postIsOpen ? (
<p className={classes.viewHandler} onClick={handleView}>
Show More
</p>
) : (
<p className={classes.viewHandler} onClick={handleView}>
Show Less
</p>
)}
{postIsOpen && <p className={classes.article}>{props.post}</p>}
</div>
</>
);
}
export default BlogCard;
Here I create the function to update and add the onclick listener
function UpdateModal(props) {
const [title, setTitle] = useState(props.title);
const [image, setImage] = useState(props.image);
const [post, setPost] = useState(props.post);
const updateModalHandler = (prevState) => {
props.setShowUpdateModal((prevState = !prevState));
};
const updatePost = async (id) => {
const postDocRef = doc(db, "posts", id);
props.setShowUpdateModal(false);
try {
await updateDoc(postDocRef, {
title: title,
image: image,
post: post,
});
} catch (err) {
alert(err);
}
};
return (
<div onClick={updateModalHandler} className={classes.backdrop}>
<form onClick={(e) => e.stopPropagation()} className={classes.form}>
<label htmlFor="title">Title</label>
<input
id="title"
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<label htmlFor="image">Image(URL)</label>
<input
id="image"
type="text"
value={image}
onChange={(e) => setImage(e.target.value)}
/>
<label htmlFor="post">Post</label>
<textarea
id="post"
cols="30"
rows="30"
value={post}
onChange={(e) => setPost(e.target.value)}
/>
<div className={classes.buttons}>
<button className={classes.cancel} onClick={updateModalHandler}>Cancel</button>
<button className={classes.update} onClick={() => updatePost(props.id)}>Update</button>
</div>
</form>
</div>
);
}
export default UpdateModal;
This is the way my data is structured
firebase

How to Filter By More Than One Option Ath The Same Time, Checkbox

I have set up a checkbox filter. I am filtering by brand or type. With my current code I can filter either by brand or type but not brand and type.
I want to be able to filter by brand and type.
I thought I had figured this out but lack of sleep is killing me and I can't seem to wrap my head around this problem at the moment so any help would be much appreciated.
My current code:
guns.jsx
const Guns = (props) => {
const { guns } = props
if (!guns) {
return (
<Layout>
<div className="flex justify-center items-center min-h-vh">
<Image src="/loading.gif" width={500} height={300} />
</div>
</Layout>
)
}
const [checkedInputs, setCheckedInputs] = useState({})
const handleInputChange = (event) => {
setCheckedInputs({ ...checkedInputs, [event.target.value]: event.target.checked })
}
useEffect(() => {
console.log('Checked Inputs', checkedInputs)
}, [checkedInputs])
// Get categories for the Gun filter.
const getCategories = () => {
const findCategories = guns.map(gun => {
return gun.Type
})
const filterCategories = findCategories.filter((category, index) => findCategories.indexOf(category) === index).sort()
const categories = filterCategories.map((cat, index) => {
return {
categories: {
catID: index,
name: cat
}
}
})
return categories
}
//Get brands for the Gun filter.
const getBrands = () => {
const findBrands = guns.map(gun => {
return gun.Make
})
const filterBrands = findBrands.filter((brand, index) => findBrands.indexOf(brand) === index).sort()
const brands = filterBrands.map((brand, index) => {
return {
brands: {
brandID: index,
name: brand
}
}
})
return brands
}
return (
<Layout>
<div className="flex mx-96">
<div className="w-1/4">
<GunFilter categories={getCategories()} brands={getBrands()} handleInputChange={handleInputChange} checkedInputs={checkedInputs} />
</div>
<div className="w-3/4">
<div className="grid grid-cols-3 gap-2 lg:my-12 lg:justify-center">
{guns.map(gun => {
if (gun.ImageCount > 1) {
// If no options boxes selected
if (Object.keys(checkedInputs).length < 1 || Object.keys(checkedInputs).every(value => checkedInputs[value] === false)) {
return <GunProductCard gun={gun} />
}
for (const [key, value] of Object.entries(checkedInputs)) {
if (value === true) {
if (key === gun.Type || key === gun.Make) {
return (
<GunProductCard gun={gun} />
)
}
}
}
}
})}
</div>
</div>
</div>
</Layout >
)
}
export async function getStaticProps() {
const res = await fetch(process.env.GUNTRADER_API)
const data = await res.json()
const guns = data.Guns
return {
props: {
guns
},
revalidate: 600
}
}
GunFilter.jsx
const GunFilter = (props) => {
const { categories, brands, handleInputChange, checkedInputs } = props
return (
<div className="text-black my-24">
<h4 className="p-2 border-b-2 border-r-2 font-bold">Filter By</h4>
<div className="p-2 my-2 border-r-2 border-b-2">
<h5 className="mb-2 font-semibold">Category</h5>
{categories.map(cat => {
return (
<div>
<input type="checkbox" id={cat.categories.name} value={cat.categories.name} checked={checkedInputs[cat.categories.name]} onChange={handleInputChange} />
<label key={cat.categories.name} className="ml-2" htmlFor={cat.categories.name}>{cat.categories.name}</label>
</div>
)
})}
</div>
<div className="p-2 my-2 border-r-2 border-b-2">
<h5 className="mb-2 font-semibold">Brand</h5>
{brands.map(brand => {
return (
<div>
<input type="checkbox" id={brand.brands.name} value={brand.brands.name} checked={checkedInputs[brand.brands.name]} onChange={handleInputChange} />
<label key={brand.brands.name} className="ml-2" htmlFor={brand.brands.name}>{brand.brands.name}</label>
</div>
)
})}
</div>
</div>
)
}
export default GunFilter

How to add a button and a dropdown in AgGrid Cell in react Functional Component

I have list of data. I am using AgGrid in react to display this data list. For each row i need to display a column having a delete button and a second column having a dropdown and a reset button.
When i click the delete button i need the corresponding row data and when i click reset button i need the dropdown option select as well as the corresponding row data.
I have searched but i am not able to figure out how to do it in react functional components. I have found that i need to use ICellRendererReactComp but i am not sure how as i am new to react and AgGrid
My current code looks something like this :
import React, { useState } from "react";
import toaster from "toasted-notes";
import { apiRequest, errorHandler } from "../../utilis/apiRequest";
import { columnDefsFromArr } from "../Threads/columnDefs";
import { AgGridReact, ICellRendererReactComp } from "ag-grid-react";
import { isResourcePresent } from "../../utilis/helper";
function Sessions(props) {
const [email, setEmail] = useState("");
const [reid, setReid] = useState(null);
const [sessionsResp, setSessionsResp] = useState(null);
const [columnDefs, setColumnDefs] = useState(null);
const [rowData, setRowData] = useState(null);
const defaultColDef = {
sortable: true,
filter: true,
resizable: true,
};
const colsToExlude = ["requestId"];
const resetSessionOptions = [
"None",
"ClearClientCache",
"PasswordChange",
"Suspended",
"InvalidSession",
"Expired",
];
const handleEmailChange = (e) => {
setEmail(e.target.value);
};
const handleGetSession = () => {
setReid(email);
getSessions({ reid: email, env: props.env });
};
const getSessions = (data) => {
console.log(data, reid);
let isError = validateForm(data);
if (isError.status) {
apiRequest(
props.sessionToken,
"get_session",
data,
(res) => {
if (res.status === 200) {
console.log(res.data);
setRowData(res.data.sessions);
makeGrid(res.data);
}
},
(err) => {
errorHandler(err);
}
);
} else {
toaster.notify(isError.msg, {
duration: 1500,
});
}
};
const handleDelete = (data) => {
console.log(data);
};
const handleReset = (data) => {
console.log(data);
};
const makeGrid = (data) => {
let cols = [];
data.sessions.map((ele) => {
Object.keys(ele).map((key) => cols.push(key));
});
let localCols = [];
if (isResourcePresent(props.resources, "del_session")) {
localCols.push({
headerName: "Delete Sessio",
});
}
if (isResourcePresent(props.resources, "reset_session")) {
localCols.push({
headerName: "Reset Session"
});
}
cols = [...new Set(cols)];
colsToExlude.map((key) => {
let ind = cols.indexOf(key);
if (ind > -1) {
cols.splice(ind, 1);
}
});
let finalColDefs = [...localCols, ...columnDefsFromArr(cols)];
console.log(finalColDefs);
setColumnDefs(finalColDefs);
};
const validateForm = (data) => {
if (data.reid.trim() === "") {
return { status: false, msg: "Email/Email Id is reqd" };
} else {
return { status: true };
}
};
return (
<div className="container-fluid">
<div>
<h5>Get Sessions Information</h5>
</div>
<div className="card mt-2 p-3 bg-red shadow p-3 mb-5 bg-white rounded">
<div className="row m-2">
<div className="col-sm-6">
<input
type="text"
className="form-control"
value={email || ""}
placeholder="Email / Email ID"
onChange={handleEmailChange}
/>
</div>
<div className="col-sm-2">
<button className="button btn-primary" onClick={handleGetSession}>
Get Information
</button>
</div>
</div>
</div>
{rowData == null ? null : (
<div className="card mt-2 p-3 bg-red shadow p-3 mb-5 bg-white rounded">
<div
className="ag-theme-balham"
style={{ height: "500px", width: "100%" }}
>
<AgGridReact
columnDefs={columnDefs}
rowData={rowData}
></AgGridReact>
</div>
</div>
)}
</div>
);
}
export { Sessions };
handleDelete : this is the function i want to call when that delete button is clicked for some corresponding row,
resetSessionOptions : this is the dropdown list options i need to display in second column along with Reset button.
handleReset : this the function i want to call when that Reset button besides the dropdown is clicked for some corresponding row
So I searched a lot and finally came across this example : https://stackblitz.com/edit/angular-ag-grid-button-renderer?file=src%2Fapp%2Frenderer%2Fbutton-renderer.component.ts
Above example is for Angular and uses classes.
I figured out how to do it in react Functional Components. I did not used any interface or something but implemented all methods in above given example and made Renderer classes for the two columns i needed. You can see the code below.
UPDATE : My this solution works but this is causing my all other state variables to reset to initial state. I am not sure why thats happening. I am looking for a solution for that.
Session.js
import React, { useState } from "react";
import toaster from "toasted-notes";
import { apiRequest, errorHandler } from "../../utilis/apiRequest";
import { columnDefsFromArr } from "../Threads/columnDefs";
import { AgGridReact, ICellRendererReactComp } from "ag-grid-react";
import { isResourcePresent } from "../../utilis/helper";
import { ButtonRenderer } from "./ButtonRenderer";
import { DropDownRender } from "./DropDownRender";
function Sessions(props) {
const [email, setEmail] = useState("");
const [reid, setReid] = useState(null);
const [sessionsResp, setSessionsResp] = useState(null);
const [columnDefs, setColumnDefs] = useState(null);
const [rowData, setRowData] = useState(null);
const frameworkComponents = {
buttonRenderer: ButtonRenderer,
dropDownRenderer: DropDownRender,
};
const defaultColDef = {
sortable: true,
filter: true,
resizable: true,
};
const colsToExlude = ["requestId"];
const resetSessionOptions = [
"None",
"ClearClientCache",
"PasswordChange",
"Suspended",
"InvalidSession",
"Expired",
];
const handleEmailChange = (e) => {
setEmail(e.target.value);
};
const handleGetSession = () => {
setReid(email);
getSessions({ reid: email, env: props.env });
};
const getSessions = (data) => {
console.log(data, reid);
let isError = validateForm(data);
if (isError.status) {
apiRequest(
props.sessionToken,
"get_session",
data,
(res) => {
if (res.status === 200) {
console.log(res.data);
setRowData(res.data.sessions);
makeGrid(res.data);
}
},
(err) => {
errorHandler(err);
}
);
} else {
toaster.notify(isError.msg, {
duration: 1500,
});
}
};
const handleDelete = (data) => {
console.log("DEL", data);
};
const handleReset = (data) => {
console.log("RESET", data);
};
const makeGrid = (data) => {
let cols = [];
data.sessions.map((ele) => {
Object.keys(ele).map((key) => cols.push(key));
});
let localCols = [];
if (isResourcePresent(props.resources, "del_session")) {
localCols.push({
headerName: "Delete Sessio",
cellRenderer: "buttonRenderer",
cellRendererParams: {
onClick: handleDelete,
label: "Delete",
},
});
}
if (isResourcePresent(props.resources, "reset_session")) {
localCols.push({
headerName: "Reset Session",
cellRenderer: "dropDownRenderer",
cellRendererParams: {
onClick: handleReset,
label: "RESET",
dropDown: resetSessionOptions,
},
});
}
cols = [...new Set(cols)];
colsToExlude.map((key) => {
let ind = cols.indexOf(key);
if (ind > -1) {
cols.splice(ind, 1);
}
});
let finalColDefs = [...localCols, ...columnDefsFromArr(cols)];
setColumnDefs(finalColDefs);
};
const validateForm = (data) => {
if (data.reid.trim() === "") {
return { status: false, msg: "Email/Email Id is reqd" };
} else {
return { status: true };
}
};
return (
<div className="container-fluid">
<div>
<h5>Get Sessions Information</h5>
</div>
<div className="card mt-2 p-3 bg-red shadow p-3 mb-5 bg-white rounded">
<div className="row m-2">
<div className="col-sm-6">
<input
type="text"
className="form-control"
value={email || ""}
placeholder="Email / Email ID"
onChange={handleEmailChange}
/>
</div>
<div className="col-sm-2">
<button className="button btn-primary" onClick={handleGetSession}>
Get Information
</button>
</div>
</div>
</div>
{rowData == null ? null : (
<div className="card mt-2 p-3 bg-red shadow p-3 mb-5 bg-white rounded">
<div
className="ag-theme-balham"
style={{ height: "500px", width: "100%" }}
>
<AgGridReact
defaultColDef={defaultColDef}
columnDefs={columnDefs}
rowData={rowData}
frameworkComponents={frameworkComponents}
></AgGridReact>
</div>
</div>
)}
</div>
);
}
export { Sessions };
ButtonRenderer.js
import React from "react";
function ButtonRenderer(params) {
const refresh = (param) => {
return true;
};
const onClick = ($event) => {
if (params.onClick instanceof Function) {
const retParams = {
event: $event,
rowData: params.node.data,
};
params.onClick(retParams);
}
};
return (
<button className="button btn-primary" onClick={onClick}>
{params.label}
</button>
);
}
export { ButtonRenderer };
DropDownRenderer.js
import React, { useState } from "react";
function DropDownRender(params) {
const [selection, setSelection] = useState(params.dropDown[0]);
const refresh = (param) => {
return true;
};
const handleDropDown = (e) => {
setSelection(e.target.value);
};
const onClick = ($event) => {
if (params.onClick instanceof Function) {
const retParams = {
event: $event,
rowData: params.node.data,
selection: selection,
};
params.onClick(retParams);
}
};
return (
<div className="row">
<div className="col">
<select className="form-control" onChange={handleDropDown}>
{params.dropDown.map((i) => {
return (
<option key={i} value={i}>
{i}
</option>
);
})}
</select>
</div>
<div className="col">
<button className="button btn-primary" onClick={onClick}>
{params.label}
</button>
</div>
</div>
);
}
export { DropDownRender };

Categories

Resources