From 9b01752d9934c9c25a966fb6dcb0a5dadb2a3a95 Mon Sep 17 00:00:00 2001 From: Shivam Gaur <128178418+shivamgaur99@users.noreply.github.com> Date: Mon, 5 Aug 2024 20:42:44 +0530 Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=81=20[Frontend]=20Improve=20the=20Man?= =?UTF-8?q?age=20Q&A=20UI=20(#1105)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Frontend] Improve the Manage Q&A UI * removing redundant code --- frontend/src/pages/Admin/Admin.jsx | 3 - .../Faq/Q&A/ManageQ&A/ManageQ&A.jsx | 199 ---------------- .../Components/Faq/Q&A/ManageQ&A/index.js | 1 - .../Faq/Q&A/ManageQ&A/manage.module.scss | 146 ------------ .../pages/Admin/Components/Faq/Q&A/QandA.jsx | 216 ++++++++++++++++-- .../Components/Faq/Q&A/qanda.module.scss | 146 ++++++++++-- 6 files changed, 317 insertions(+), 394 deletions(-) delete mode 100644 frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/ManageQ&A.jsx delete mode 100644 frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/index.js delete mode 100644 frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/manage.module.scss diff --git a/frontend/src/pages/Admin/Admin.jsx b/frontend/src/pages/Admin/Admin.jsx index 312af9f5..1c649dee 100644 --- a/frontend/src/pages/Admin/Admin.jsx +++ b/frontend/src/pages/Admin/Admin.jsx @@ -24,7 +24,6 @@ import { END_POINT } from "../../config/api"; import { useDispatch } from "react-redux"; import { ManageFaq } from "./Components/Faq/ManageFaq"; import { QandA } from "./Components/Faq/Q&A/QandA"; -import { Manageqa } from "./Components/Faq/Q&A/ManageQ&A/ManageQ&A"; import { Testimonial } from "./Components/Testimonial"; import { AddTestimonial } from "./Components/Testimonial/AddTestimonial"; import { ManageTestimonial } from "./Components/Testimonial/ManageTestimonial"; @@ -261,8 +260,6 @@ export const Admin = (props) => { ) : tab === 18 ? ( - ) : tab === 19 ? ( - ) : tab === 20 ? ( ) : tab === 21 ? ( diff --git a/frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/ManageQ&A.jsx b/frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/ManageQ&A.jsx deleted file mode 100644 index 4ba23be3..00000000 --- a/frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/ManageQ&A.jsx +++ /dev/null @@ -1,199 +0,0 @@ -import { useEffect, useState } from "react"; -import style from "./manage.module.scss"; -import { makeStyles } from "@material-ui/core/styles"; -import Modal from "@material-ui/core/Modal"; -import Button from "@material-ui/core/Button"; -import Typography from "@material-ui/core/Typography"; -import { AiOutlineArrowLeft } from "react-icons/ai"; -import { SimpleToast } from "../../../../../../components/util/Toast/Toast"; -import Loader from "../../../../../../components/util/Loader"; -import { getQuestionById, deleteAnswer, deleteQuestion, updateQuestionStatus, updateAnswerStatus, getAnswers } from "../../../../../../service/Faq"; -import { hideToast, showToast } from "../../../../../../service/toastService"; - -const useStyles = makeStyles((theme) => ({ - modal: { - display: "flex", - alignItems: "center", - justifyContent: "center", - }, - paper: { - backgroundColor: theme.palette.background.paper, - border: "2px solid #000", - boxShadow: theme.shadows[5], - padding: theme.spacing(2, 4, 3), - }, - buttons: { - display: "flex", - marginTop: "10px", - justifyContent: "center", - gap: "10px", - }, -})); - -export function Manageqa({ setTab, qId }) { - const [ans, setAns] = useState([]); - const [qns, setQns] = useState(); - const [toggle, setToggle] = useState(false); - const [isLoaded, setIsLoaded] = useState(false); - const [toast, setToast] = useState({ - toastStatus: false, - toastType: "", - toastMessage: "", - }); - const [confirmDelete, setConfirmDelete] = useState(false); - const [questionToDelete, setQuestionToDelete] = useState(null); - const classes = useStyles(); - - const getQuestion = async (id) => { - setIsLoaded(true); - const qRes = await getQuestionById(id, setToast); - setQns(qRes); - setIsLoaded(false); - }; - - const handleOpenConfirmModal = (id) => { - setConfirmDelete(true); - setQuestionToDelete(id); - }; - - const handleCloseConfirmModal = () => { - setConfirmDelete(false); - }; - - const handleDeleteAnswer = async (answerId) => { - await deleteAnswer(answerId, setToast); - setToggle(!toggle); - }; - - const handleDeleteQuestion = async () => { - await deleteQuestion(questionToDelete, setToast); - setConfirmDelete(false); - setTab(18); - setToggle(!toggle); - }; - - const updateQuestion = async (id, status) => { - await updateQuestionStatus(id, status, setToast); - setToggle(!toggle); - }; - - const getAnswer = async (questionId) => { - setIsLoaded(true); - const aRes = await getAnswers(questionId, setToast); - setAns(aRes); - setIsLoaded(false); - }; - - const updateAnswer = async (id, status) => { - await updateAnswerStatus(id, status, setToast); - setToggle(!toggle); - }; - - const handleCloseToast = (event, reason) => { - if (reason === "clickaway") { - return; - } - hideToast(setToast); - }; - - useEffect(() => { - getQuestion(qId); - getAnswer(qId); - }, [toggle]); - - return ( -
-

Manage Q&A

-
- setTab(18)} /> -
-
{isLoaded && }
- {!isLoaded && ( - <> -

Question

-
-
-
-

{qns?.title}

-
-

{qns?.description}

-
- - -
-
- {qns?.tags?.map((tag) => ( -

{tag}

- ))} -
-
-
-
-
- - )} -

Answers

- {ans?.length === 0 ? ( - No answers Found - ) : ( -
- {ans?.map((a) => ( -
-
-
-

{a.answer}

-
- - -
-
-
-
- ))} -
- )} - -
- Confirm Delete - Are you sure you want to delete this question and all its answers? -
- - -
-
-
- {toast.toastStatus && ( - - )} -
- ); -} diff --git a/frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/index.js b/frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/index.js deleted file mode 100644 index 747c2843..00000000 --- a/frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from "./ManageQ&A"; diff --git a/frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/manage.module.scss b/frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/manage.module.scss deleted file mode 100644 index 11c431d3..00000000 --- a/frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/manage.module.scss +++ /dev/null @@ -1,146 +0,0 @@ -.card-item { - text-align: center; - font-size: 1.5rem; - border-radius: 1em; - height: auto; - width: 90%; - margin: 10px auto 30px auto; - display: inline-block; - background-position: left center; - transition: all 0.5s ease-in; - background-color: #016795; - box-shadow: 0.5em 0.5em 0.5em rgb(54, 53, 53); - max-width: 400px; -} - -.card-title { - font-size: 1.8rem; - margin-bottom: 1.5rem; - line-height: 1.9rem; - font-weight: bold; - color: white; -} -.question{ - display: flex; - justify-content: center; -} -.card-question { - font-weight: bold; - text-align: left; - font-size: 1.3rem; - width: 100%; - margin: 2px; -} - -.card-answer { - font-weight: 600; - text-align: left; - font-size: 1.2rem; - width: 100%; - margin: 2px; -} - -.questionBox{ - display: grid; - justify-content: center; - margin: 5px; - gap: 10px; -} - -.answerBox{ - display: grid; - justify-content: center; - margin: 5px; - gap: 10px; -} - -.card-info { - color: white; - margin-top: 10px; - margin-bottom: 20px; - display: flex; - flex-direction: column; - padding: 14px; -} - -.head { - text-align: center; -} - -.button-group { - display: flex; - width: 100%; - align-items: center; - justify-content: center; - gap: 10px; - margin: 5px 2px 2px 2px; -} - -.button-approve { - padding: 10px; - border: none; - outline: none; - border-radius: 5px; - background-color: rgb(6, 158, 41); - margin: 5px; - color: #fff; - width: 120px; - font-size: medium; - font-weight: bold; - transition: background-color 200ms; -} - -.answer{ - display: grid; - grid-template-columns: auto auto; -} - -.button-edit:hover { - background-color: rgb(10, 205, 53); -} - -.button-delete { - padding: 10px; - border: none; - outline: none; - border-radius: 5px; - background-color: #fc0254; - margin: 5px; - color: #fff; - width: 120px; - font-size: medium; - font-weight: bold; - transition: background-color 200ms; - text-align: center; -} - -.button-delete:hover { - background-color: #fc3779; -} - -.tags{ - background-color: gray; - color: black; - padding: 0px 4px; - display: flex; - justify-content: center; - align-items: center; - margin: 4px; - height: 25px; - margin-top: 0; - border-radius: 10px; - font-size: small; -} -@media (max-width:"900px") { - .answerBox{ - display: block; - } - .questionBox{ - display: block; - } - .answer{ - display: flex; - justify-content: center; - flex-direction: column; - } -} \ No newline at end of file diff --git a/frontend/src/pages/Admin/Components/Faq/Q&A/QandA.jsx b/frontend/src/pages/Admin/Components/Faq/Q&A/QandA.jsx index 8df877bb..8d4eb61d 100644 --- a/frontend/src/pages/Admin/Components/Faq/Q&A/QandA.jsx +++ b/frontend/src/pages/Admin/Components/Faq/Q&A/QandA.jsx @@ -1,26 +1,140 @@ import React, { useEffect, useState } from "react"; import style from "./qanda.module.scss"; -import { getAllQuestions } from "../../../../../service/Faq"; +import { + getAllQuestions, + deleteAnswer, + deleteQuestion, + updateQuestionStatus, + updateAnswerStatus, + getAnswers, +} from "../../../../../service/Faq"; import { SimpleToast } from "../../../../../components/util/Toast/Toast"; import Loader from "../../../../../components/util/Loader"; -import { hideToast } from "../../../../../service/toastService"; +import { hideToast, showToast } from "../../../../../service/toastService"; +import Modal from "@material-ui/core/Modal"; +import Button from "@material-ui/core/Button"; +import Typography from "@material-ui/core/Typography"; +import { makeStyles } from "@material-ui/core/styles"; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import ExpandLessIcon from '@material-ui/icons/ExpandLess'; -export function QandA({ setTab, setQId, tab }) { +const useStyles = makeStyles((theme) => ({ + modal: { + display: "flex", + alignItems: "center", + justifyContent: "center", + }, + paper: { + backgroundColor: theme.palette.background.paper, + border: "2px solid #000", + boxShadow: theme.shadows[5], + padding: theme.spacing(2, 4, 3), + }, + buttons: { + display: "flex", + marginTop: "10px", + justifyContent: "center", + gap: "10px", + }, +})); + +export function QandA() { const [cards, setCards] = useState([]); + const [expandedCards, setExpandedCards] = useState({}); const [isLoaded, setIsLoaded] = useState(false); const [toast, setToast] = useState({ toastStatus: false, toastType: "", toastMessage: "", }); + const [confirmDelete, setConfirmDelete] = useState(false); + const [questionToDelete, setQuestionToDelete] = useState(null); + const classes = useStyles(); - const getdata = async () => { + const getQuestions = async () => { setIsLoaded(true); const data = await getAllQuestions(setToast, toast); setCards(data); setIsLoaded(false); }; + const handleOpenConfirmModal = (id) => { + setConfirmDelete(true); + setQuestionToDelete(id); + }; + + const handleCloseConfirmModal = () => { + setConfirmDelete(false); + setQuestionToDelete(null); + }; + + const handleDeleteQuestion = async () => { + await deleteQuestion(questionToDelete, setToast); + setCards(cards.filter(card => card._id !== questionToDelete)); + setConfirmDelete(false); + }; + + const updateQuestion = async (id, status) => { + setCards(cards.map(card => + card._id === id ? { ...card, isApproved: status } : card + )); + + await updateQuestionStatus(id, status, setToast); + }; + + const handleDeleteAnswer = async (answerId) => { + const questionId = Object.keys(expandedCards)[0]; + const prevAnswers = expandedCards[questionId]?.answers || []; + + setExpandedCards((prev) => ({ + ...prev, + [questionId]: { + ...prev[questionId], + answers: prevAnswers.filter(answer => answer._id !== answerId), + }, + })); + + await deleteAnswer(answerId, setToast); + }; + + const updateAnswer = async (id, status) => { + const questionId = Object.keys(expandedCards)[0]; + const prevAnswers = expandedCards[questionId]?.answers || []; + + setExpandedCards((prev) => ({ + ...prev, + [questionId]: { + ...prev[questionId], + answers: prevAnswers.map(answer => + answer._id === id ? { ...answer, isApproved: status } : answer + ), + }, + })); + + await updateAnswerStatus(id, status, setToast); + }; + + const handleToggleExpand = async (id) => { + if (expandedCards[id]) { + setExpandedCards((prev) => { + const newExpanded = { ...prev }; + delete newExpanded[id]; + return newExpanded; + }); + } else { + setIsLoaded(true); + const aRes = await getAnswers(id, setToast); + setExpandedCards((prev) => ({ + ...prev, + [id]: { + answers: aRes, + }, + })); + setIsLoaded(false); + } + }; + + const handleCloseToast = (event, reason) => { if (reason === "clickaway") { return; @@ -29,43 +143,97 @@ export function QandA({ setTab, setQId, tab }) { }; useEffect(() => { - getdata(); - }, [tab]); + getQuestions(); + }, []); return (

Manage Q&A

-
{isLoaded ? : null}
+
{isLoaded && }
- {cards?.map((d, index) => ( + {cards?.map((qns, index) => (
-

{d.title}

-
-

{d.description}

-
+

{qns.title}

-
Status
{`${d.isApproved ? "Approved" : "Not Approved"}`} +

{qns.description}

- {d.tags.map((tag, idx) => ( -

- {tag.value || tag} -

- ))} +
+ {qns?.tags?.map((tag) => ( +

{tag}

+ ))} +
+
+
+ +
+ {expandedCards[qns._id] && ( +
+ {expandedCards[qns._id].answers?.length === 0 ? ( + No answers Found + ) : ( + expandedCards[qns._id].answers.map((a) => ( +
+
+
+

{a.answer}

+
+ + +
+
+
+
+ )) + )} +
+ )}
))}
+ +
+ Confirm Delete + Are you sure you want to delete this question and all its answers? +
+ + +
+
+
{toast.toastStatus && ( div > .tags { - background-color: gray; +.manage { + width: 120px; + background-color: white; color: black; - padding: 0px 4px; - display: flex; + margin-bottom: 20px; + margin-top: 15px; + padding: 10px; + border: none; + border-radius: 5px; +} + +.card-item { + text-align: center; + font-size: 1.5rem; + border-radius: 1em; + height: auto; + width: 90%; + margin: 10px auto 30px auto; + display: block; + background-position: left center; + transition: all 0.5s ease-in; + background-color: #016795; + box-shadow: 0.5em 0.5em 0.5em rgb(54, 53, 53); + max-width: 400px; +} + +.card-answer { + font-weight: 600; + text-align: left; + font-size: 1.2rem; + width: 100%; + margin: 2px; +} + +.questionBox { + display: grid; + justify-content: center; + margin: 5px; + gap: 10px; +} + +.answerBox { + display: grid; justify-content: center; + margin: 5px; + gap: 10px; +} + +.card-info { + color: white; + margin-top: 10px; + margin-bottom: 20px; + display: flex; + flex-direction: column; + padding: 14px; +} + +.button-group { + display: flex; + width: 100%; align-items: center; - margin: 4px; - height: 18px; - margin-top: 0; - border-radius: 10px; - font-size: x-small; + justify-content: center; + gap: 10px; + margin: 5px 2px 2px 2px; } -.manage{ +.button-approve { + padding: 10px; + border: none; + outline: none; + border-radius: 5px; + background-color: rgb(6, 158, 41); + margin: 5px; + color: #fff; width: 120px; - background-color: white; - color: black; - margin-bottom: 20px; - margin-top: 15px; + font-size: medium; + font-weight: bold; + transition: background-color 200ms; +} + +.button-delete { padding: 10px; border: none; + outline: none; border-radius: 5px; + background-color: #fc0254; + margin: 5px; + color: #fff; + width: 120px; + font-size: medium; + font-weight: bold; + transition: background-color 200ms; + text-align: center; +} + +.button-delete:hover { + background-color: #fc3779; +} + +.tags { + background-color: gainsboro; + color: black; + padding: 0px 4px; + display: flex; + justify-content: center; + align-items: center; + margin: 4px; + height: 25px; + margin-top: 0; + border-radius: 4px; + font-size: small; +} + +@media (max-width: 900px) { + .answerBox { + display: block; + } + .questionBox { + display: block; + } + .answer { + display: flex; + justify-content: center; + flex-direction: column; + } }