From 1a1d4a250070fed954e5cdf4563f41632ecfc4ff Mon Sep 17 00:00:00 2001 From: Shivam Gaur Date: Tue, 28 May 2024 19:59:48 +0530 Subject: [PATCH 1/2] adding updateFaq functionality --- .../Components/Faq/ManageFaq/ManageFaq.jsx | 131 +++++++++++++++++- .../Faq/ManageFaq/manage-faq.module.scss | 112 ++++++++++++++- 2 files changed, 234 insertions(+), 9 deletions(-) diff --git a/frontend/src/pages/Admin/Components/Faq/ManageFaq/ManageFaq.jsx b/frontend/src/pages/Admin/Components/Faq/ManageFaq/ManageFaq.jsx index bba4f997..3a8bd6d9 100644 --- a/frontend/src/pages/Admin/Components/Faq/ManageFaq/ManageFaq.jsx +++ b/frontend/src/pages/Admin/Components/Faq/ManageFaq/ManageFaq.jsx @@ -1,5 +1,4 @@ -import React from "react"; -import { useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import Accordion from "@material-ui/core/Accordion"; import AccordionDetails from "@material-ui/core/AccordionDetails"; import AccordionSummary from "@material-ui/core/AccordionSummary"; @@ -11,6 +10,7 @@ import { END_POINT } from "../../../../../config/api"; import Loader from "../../../../../components/util/Loader"; import style from "./manage-faq.module.scss"; import { SimpleToast } from "../../../../../components/util/Toast"; +import { Button2 } from "../../../../../components/util/Button/index"; export function ManageFaq() { const [faqs, setFaqs] = useState([]); @@ -20,19 +20,27 @@ export function ManageFaq() { const [toastMessage, setToastMessage] = useState(""); const [severity, setSeverity] = useState("success"); const [reload, setReload] = useState(true); + const [editedFaq, setEditedFaq] = useState(null); + const [openEditDialog, setOpenEditDialog] = useState(false); + const [editedQuestion, setEditedQuestion] = useState(""); + const [editedAnswer, setEditedAnswer] = useState(""); + const [editedIsActive, setEditedIsActive] = useState(true); + const [editedTags, setEditedTags] = useState([]); + const handleCloseToast = () => { setTimeout(() => { setOpenToast(false); }, 500); }; + const handleChange = (panel) => (event, isExpanded) => { setExpanded(isExpanded ? panel : false); }; + async function fetchAllFaq() { try { const response = await fetch(`${END_POINT}/faq/getFaq`); const data = await response.json(); - console.log(data); setFaqs(data.Faq); setIsFetching(false); } catch (err) { @@ -44,9 +52,7 @@ export function ManageFaq() { const deleteFaq = async (faqId) => { setIsFetching(true); const url = `${END_POINT}/faq/deleteFaq`; - const body = { - faqId: faqId, - }; + const body = { faqId: faqId }; const headers = { "Content-Type": "application/json", authorization: `Bearer ${localStorage.getItem("token")}`, @@ -73,10 +79,64 @@ export function ManageFaq() { setIsFetching(false); } }; + + const updateFaq = async (faqId, updatedFaqDetails) => { + try { + const response = await fetch(`${END_POINT}/faq/updateFaq`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + authorization: `Bearer ${localStorage.getItem("token")}`, + }, + body: JSON.stringify({ faqId: faqId, ...updatedFaqDetails }), + }); + + if (!response.ok) { + throw new Error("Failed to update FAQ"); + } + + const data = await response.json(); + setToastMessage(data.message); + setOpenToast(true); + setSeverity("success"); + setReload(!reload); + } catch (error) { + console.error("Failed to update FAQ:", error.message); + setToastMessage("Failed to update FAQ"); + setOpenToast(true); + setSeverity("error"); + } + }; + useEffect(() => { fetchAllFaq(); }, [reload]); + const handleEdit = (faqId) => { + const editedFaq = faqs.find((faq) => faq._id === faqId); + setEditedFaq(editedFaq); + setEditedQuestion(editedFaq.question); + setEditedAnswer(editedFaq.answer); + setEditedIsActive(editedFaq.isActive); + setEditedTags(editedFaq.tags); + setOpenEditDialog(true); + }; + + const handleSaveEdit = () => { + const updatedFaq = { + question: editedQuestion, + answer: editedAnswer, + isActive: editedIsActive, + tags: editedTags, + }; + updateFaq(editedFaq._id, updatedFaq); + setOpenEditDialog(false); + }; + + const handleCancelEdit = () => { + setOpenEditDialog(false); + }; + return (

Manage FAQ

@@ -129,8 +189,9 @@ export function ManageFaq() { className={style["btns"]} variant="contained" endIcon={} + onClick={() => handleEdit(faq._id)} > - UPDATE + EDIT
+ {editedFaq && openEditDialog && ( +
+
+

Edit FAQ

+
+ + setEditedQuestion(e.target.value)} + className={style["faq-input"]} + /> + + setEditedAnswer(e.target.value)} + className={style["faq-input"]} + /> + + setEditedIsActive(e.target.checked)} + className={style["faq-input"]} + /> + + setEditedTags(e.target.value.split(","))} + className={style["faq-input"]} + /> +
+ + +
+
+
+
+ )} ); } + diff --git a/frontend/src/pages/Admin/Components/Faq/ManageFaq/manage-faq.module.scss b/frontend/src/pages/Admin/Components/Faq/ManageFaq/manage-faq.module.scss index 5b6d069e..35ef20e2 100644 --- a/frontend/src/pages/Admin/Components/Faq/ManageFaq/manage-faq.module.scss +++ b/frontend/src/pages/Admin/Components/Faq/ManageFaq/manage-faq.module.scss @@ -6,6 +6,18 @@ height: 37rem; align-items: center; } + +.faq-input { + border-color: #1863ff; + outline: none; + border: double 2px transparent; + border-radius: 10px; + background-image: linear-gradient(white, white), + linear-gradient(to right, rgba(255, 0, 90, 1), rgba(10, 24, 61, 1)); + background-origin: border-box; + background-clip: padding-box, border-box; + background-color: #ffffff; +} .faq-container { padding-bottom: 10px; @@ -60,7 +72,6 @@ margin-top: 20px; } .btns { - color: white; font-weight: bolder; } #delete-btn { @@ -73,4 +84,101 @@ .accord-details { display: flex; flex-direction: column; -} \ No newline at end of file +} +.edit-dialog { + position: fixed; + top: 30%; + left: 50%; + transform: translate(-50%, -50%); + padding-bottom: 30px; + border-radius: 15px; + z-index: 10013; + width: 60%; + height: auto; + background-color: #e7e7e7; + margin-top: 10%; + margin-left: auto; + margin-right: auto; + box-shadow: 5px 5px 15px #888888, -5px -5px 15px #ffffff; +} +.blur-background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(5px); + z-index: 1000; + } + +.edit-dialog-heading { + text-align: center; + padding-top: 20px; +} + +.edit-form { + width: 85%; + margin: 0 auto; +} + +.edit-form label { + display: block; + margin-bottom: 5px; + color: #000; +} + +.edit-form input[type="text"], +.edit-form textarea { + width: 100%; + height: 50px; + border: 1px solid #bbbaba; + border-radius: 10px; + padding: 0 25px; + color: #777777; + margin-bottom: 10px; + background-color: #f1f1f1; + box-shadow: inset 2px 2px 5px #888888, inset -2px -2px 5px #ffffff; +} + +.edit-form textarea { + height: 180px; + resize: none; +} + +.edit-actions { + display: flex; + justify-content: space-between; + margin-top: 20px; +} + +.submit-btn { + display: flex; + justify-content: center; + width: 40%; + margin: 0 auto; + } + +.submit-btn-text { + display: flex; + justify-content: center; + align-items: center; +} + + +@media screen and (max-width: 750px) { + .edit-dialog { + width: 95%; + margin: 10% auto; + } + + .edit-form input, + .edit-form textarea { + height: 40px; + } + + .edit-actions button { + font-size: 14px; + } +} + \ No newline at end of file From 02c88c4bf81f9ee67175211bc38bd06945331c29 Mon Sep 17 00:00:00 2001 From: Shivam Gaur Date: Thu, 30 May 2024 02:16:11 +0530 Subject: [PATCH 2/2] fix updateFaq --- .../Components/Faq/ManageFaq/ManageFaq.jsx | 184 ++++++++++-------- .../Faq/ManageFaq/manage-faq.module.scss | 18 +- 2 files changed, 119 insertions(+), 83 deletions(-) diff --git a/frontend/src/pages/Admin/Components/Faq/ManageFaq/ManageFaq.jsx b/frontend/src/pages/Admin/Components/Faq/ManageFaq/ManageFaq.jsx index 3a8bd6d9..d63e5d37 100644 --- a/frontend/src/pages/Admin/Components/Faq/ManageFaq/ManageFaq.jsx +++ b/frontend/src/pages/Admin/Components/Faq/ManageFaq/ManageFaq.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useCallback } from "react"; import Accordion from "@material-ui/core/Accordion"; import AccordionDetails from "@material-ui/core/AccordionDetails"; import AccordionSummary from "@material-ui/core/AccordionSummary"; @@ -19,25 +19,23 @@ export function ManageFaq() { const [open, setOpenToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); const [severity, setSeverity] = useState("success"); - const [reload, setReload] = useState(true); - const [editedFaq, setEditedFaq] = useState(null); + const [reload, setReload] = useState(false); + const [faqObject, setFaqObject] = useState({}); const [openEditDialog, setOpenEditDialog] = useState(false); - const [editedQuestion, setEditedQuestion] = useState(""); - const [editedAnswer, setEditedAnswer] = useState(""); - const [editedIsActive, setEditedIsActive] = useState(true); - const [editedTags, setEditedTags] = useState([]); + const [formErrors, setFormErrors] = useState({}); - const handleCloseToast = () => { + const handleCloseToast = useCallback(() => { setTimeout(() => { setOpenToast(false); }, 500); - }; + }, []); const handleChange = (panel) => (event, isExpanded) => { setExpanded(isExpanded ? panel : false); }; - async function fetchAllFaq() { + const fetchAllFaq = useCallback(async () => { + setIsFetching(true); try { const response = await fetch(`${END_POINT}/faq/getFaq`); const data = await response.json(); @@ -47,7 +45,7 @@ export function ManageFaq() { console.log(err.message); setIsFetching(false); } - } + }, []); const deleteFaq = async (faqId) => { setIsFetching(true); @@ -88,7 +86,7 @@ export function ManageFaq() { "Content-Type": "application/json", authorization: `Bearer ${localStorage.getItem("token")}`, }, - body: JSON.stringify({ faqId: faqId, ...updatedFaqDetails }), + body: JSON.stringify({ faqId, ...updatedFaqDetails }), }); if (!response.ok) { @@ -99,7 +97,7 @@ export function ManageFaq() { setToastMessage(data.message); setOpenToast(true); setSeverity("success"); - setReload(!reload); + setReload((prev) => !prev); } catch (error) { console.error("Failed to update FAQ:", error.message); setToastMessage("Failed to update FAQ"); @@ -108,35 +106,42 @@ export function ManageFaq() { } }; - useEffect(() => { - fetchAllFaq(); - }, [reload]); - const handleEdit = (faqId) => { const editedFaq = faqs.find((faq) => faq._id === faqId); - setEditedFaq(editedFaq); - setEditedQuestion(editedFaq.question); - setEditedAnswer(editedFaq.answer); - setEditedIsActive(editedFaq.isActive); - setEditedTags(editedFaq.tags); + setFaqObject(editedFaq); setOpenEditDialog(true); }; const handleSaveEdit = () => { - const updatedFaq = { - question: editedQuestion, - answer: editedAnswer, - isActive: editedIsActive, - tags: editedTags, - }; - updateFaq(editedFaq._id, updatedFaq); - setOpenEditDialog(false); + if (validateForm()) { + updateFaq(faqObject._id, faqObject); + setOpenEditDialog(false); + } }; const handleCancelEdit = () => { - setOpenEditDialog(false); + setOpenEditDialog(false); + }; + + const validateForm = () => { + const errors = {}; + if (!faqObject.question) { + errors.question = "* Question is required"; + } + if (!faqObject.answer) { + errors.answer = "* Answer is required"; + } + if (!faqObject.tags.length || faqObject.tags[0] === "") { + errors.tags = "* At least one tag is required"; + } + setFormErrors(errors); + return Object.keys(errors).length === 0; }; + useEffect(() => { + fetchAllFaq(); + }, [fetchAllFaq, reload]); + return (

Manage FAQ

@@ -209,60 +214,80 @@ export function ManageFaq() { )}
- {editedFaq && openEditDialog && ( + {faqObject && openEditDialog && (
-
-

Edit FAQ

-
- - setEditedQuestion(e.target.value)} - className={style["faq-input"]} - /> - - setEditedAnswer(e.target.value)} - className={style["faq-input"]} - /> - - setEditedIsActive(e.target.checked)} - className={style["faq-input"]} - /> - - setEditedTags(e.target.value.split(","))} - className={style["faq-input"]} - /> -
- +

Edit FAQ

+
+ + + setFaqObject({ ...faqObject, question: e.target.value }) + } + className={style["faq-input"]} + /> + {formErrors.question && ( + {formErrors.question} + )} + + + setFaqObject({ ...faqObject, answer: e.target.value }) + } + className={style["faq-input"]} + /> + {formErrors.answer && ( + {formErrors.answer} + )} + + + setFaqObject({ ...faqObject, isActive: e.target.checked }) + } + className={style["checkbox"]} /> - Tags: + + setFaqObject({ + ...faqObject, + tags: e.target.value.split(",").map(tag => tag.trim()), + }) + } + className={style["faq-input"]} /> + {formErrors.tags && ( + {formErrors.tags} + )} +
+ + +
-
)} ); } - diff --git a/frontend/src/pages/Admin/Components/Faq/ManageFaq/manage-faq.module.scss b/frontend/src/pages/Admin/Components/Faq/ManageFaq/manage-faq.module.scss index 35ef20e2..50770df4 100644 --- a/frontend/src/pages/Admin/Components/Faq/ManageFaq/manage-faq.module.scss +++ b/frontend/src/pages/Admin/Components/Faq/ManageFaq/manage-faq.module.scss @@ -10,7 +10,7 @@ .faq-input { border-color: #1863ff; outline: none; - border: double 2px transparent; + border: double 1px transparent; border-radius: 10px; background-image: linear-gradient(white, white), linear-gradient(to right, rgba(255, 0, 90, 1), rgba(10, 24, 61, 1)); @@ -99,7 +99,6 @@ margin-top: 10%; margin-left: auto; margin-right: auto; - box-shadow: 5px 5px 15px #888888, -5px -5px 15px #ffffff; } .blur-background { position: fixed; @@ -165,11 +164,24 @@ align-items: center; } +.error { + color: rgb(219, 0, 0); + margin-top: 0; + padding-top: 0; + padding-bottom: 5px; + margin-bottom: 5px; +} + +.checkbox { + cursor: pointer; + margin-bottom: 10px; +} + @media screen and (max-width: 750px) { .edit-dialog { width: 95%; - margin: 10% auto; + margin: 20% auto; } .edit-form input,