Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Frontend] Add Q&A section in admin panel #901

Merged
merged 9 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/app/routes/Q&A/answers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const updateAnswerStatus = require('./updateAnswerStatus');
router.post('/', validation(answerValidationSchema), postAnswer);

// GET API FOR ANSWERS
router.get('/:questionId', validation(getAnswerValidationSchema), getAnswers);
router.get('/:questionId', getAnswers);
Kajol-Kumari marked this conversation as resolved.
Show resolved Hide resolved

// INCREASE UPVOTE FOR ANSWERS
router.patch('/upvote', upvoteAnswer);
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/pages/Admin/Admin.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import decode from "jwt-decode";

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";

export const Admin = (props) => {
const [tab, setTab] = useState(1);
Expand All @@ -31,7 +33,7 @@ export const Admin = (props) => {
const closeMobileMenu = () => setIsMenuOpen(false);
const dispatch = useDispatch();
const firstName = localStorage.getItem("firstName");

const [qId,setQId] = useState("")
useEffect(() => {
const token = localStorage.getItem("token");
try {
Expand Down Expand Up @@ -223,6 +225,10 @@ export const Admin = (props) => {
<ResetPassword />
) : tab === 16 ? (
<ManageBroadcasts />
) : tab === 18 ? (
<QandA setTab={setTab} setQId={setQId} tab={tab} />
) : tab === 19 ? (
<Manageqa setTab={setTab} qId={qId} />
) : null}
</div>
</div>
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/pages/Admin/Components/Faq/Faq.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ export function Faq(props) {
</div>
</div>
</div>
<div className={style["card-item"]}>
Hemu21 marked this conversation as resolved.
Show resolved Hide resolved
<div className={style["clickable-card"]}>
<div className={style["card-title"]}>
MANAGE Q&A
<AiFillEdit className={style["editt"]} />
</div>
<div className={style["card-content"]}>
<div onClick={()=>props.setTab(18)} className={style["main-btn"]}>
Manage here
</div>
</div>
</div>
</div>
</div>
</div>
);
Expand Down
131 changes: 131 additions & 0 deletions frontend/src/pages/Admin/Components/Faq/Q&A/ManageQ&A/ManageQ&A.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { useEffect, useState } from "react";
import { END_POINT } from "../../../../../../config/api";
import style from "./manage.module.scss";
import { AiOutlineArrowLeft } from "react-icons/ai";

export function Manageqa({ setTab, qId }) {
const [ans, setAns] = useState([]);
const [qns, setQns] = useState();
const [toogle, setToogle] = useState(false);
const getQuestion = async (id) => {
try {
const qUrl = `${END_POINT}/question/getQuestionById/${id}`;
const qResponse = await fetch(qUrl);
const qRes = await qResponse.json();
setQns(qRes);
} catch (error) {
console.log(error);
}
};
const updateQuestion = async (id, status) => {
try {
const qUrl = `${END_POINT}/question/updateStatus`;
const qResponse = await fetch(qUrl, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
body: JSON.stringify({ id: id, status: status }),
});
const qRes = await qResponse.json();
setToogle(!toogle);
} catch (error) {
console.log(error);
}
};
const getAnswer = async (questionId) => {
try {
const aUrl = `${END_POINT}/answers/${questionId}`;
const aResponse = await fetch(aUrl);
const aRes = await aResponse.json();
setAns(aRes.data);
} catch (error) {
console.log(error);
alert("Failed to fetch!");
}
};
const updateAnswer = async (id, status) => {
try {
const aUrl = `${END_POINT}/answers/updateStatus`;
const aResponse = await fetch(aUrl, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
body: JSON.stringify({ id: id, status: status }),
});
const aRes = await aResponse.json();
setToogle(!toogle);
} catch (error) {
console.log(error);
alert("Failed to fetch!");
}
};
useEffect(() => {
getQuestion(qId);
getAnswer(qId);
}, [toogle]);
return (
<div>
<div className={style["back"]}>
<AiOutlineArrowLeft size={"35px"} onClick={() => setTab(18)} />
</div>
<div className={style["card-item"]}>
<div className={style["card-info"]}>
<h1>{qns?.title}</h1>
<div className={style["questionBox"]}>
<h3 className={style["card-question"]}>{qns?.description}</h3>
<div className={style["button-group"]}>
<button
className={
qns?.isApproved
? style["button-delete"]
: style["button-approve"]
}
id={`${qns?._id}`}
onClick={(e) => {
updateQuestion(e.currentTarget.id, !qns?.isApproved);

}}
>
{qns?.isApproved ? "DisApprove" : "Approve"}
</button>
<button className={style["button-delete"]}>Delete</button>
</div>
</div>

{ans?.map((a) => (
<div className={style["answerBox"]}>
<h3 className={style["card-answer"]}>{a.answer}</h3>
<div className={style["button-group"]}>
<button
className={
a?.isApproved
? style["button-delete"]
: style["button-approve"]
}
id={`${a._id}`}
onClick={(e) => {
updateAnswer(e.currentTarget.id, !a?.isApproved);

}}
>
{a?.isApproved ? "DisApprove" : "Approve"}
</button>
<button className={style["button-delete"]}>Delete</button>
</div>
</div>
))}

<div style={{ display: "flex", padding: "10px 30px 10px 30px" }}>
{qns?.tags?.map((tag) => (
<p className={style["tags"]}>{tag}</p>
))}
</div>
</div>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./ManageQ&A";
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
.card-item {
text-align: center;
font-size: 1.5rem;
border-radius: 1em;
height: auto;
width: 90%;
margin: 10px auto 30px 4%;
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);
}

.card-title {
font-size: 1.8rem;
margin-bottom: 1.5rem;
line-height: 1.9rem;
font-weight: bold;
color: white;
}

.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: flex;
margin: 5px;
}

.answerBox{
display: flex;
margin: 5px;
}

.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;
justify-content: center;
gap: 10px;
margin: 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;
}

.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;
}
}
60 changes: 60 additions & 0 deletions frontend/src/pages/Admin/Components/Faq/Q&A/QandA.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useEffect, useState } from "react";
import style from "./qanda.module.scss";
import { END_POINT } from "../../../../../config/api";

export function QandA({ setTab, setQId, tab }) {
const [cards, setCards] = useState([]);
const getdata = async () => {
try {
const url = `${END_POINT}/question/getallquestions`;
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});
const res = await response.json();
setCards(res);
} catch (error) {
console.log(error);
alert("Failed to fetch!");
}
};
useEffect(() => {
getdata();
}, [tab]);
return (
<div>
<h1 className={style["head"]}>Question</h1>
<div className={style["manage-qas"]}>
{cards?.map((d, index) => (
<div className={style["crd"]} key={index}>
<h2 className="head1"> {d.title} </h2>
<div className={style["content"]}>
<h3 style={{ fontWeight: "bolder" }}>{d.description}</h3>
</div>
<div className={style["content"]}>
<h5>Status</h5> {`${d.isApproved ? "Approved" : "Not Approved"}`}
</div>
<div style={{ display: "flex" }}>
{d.tags.map((tag) => (
<p className={style["tags"]}>{tag}</p>
))}
</div>
<button
id={`${d._id}`}
onClick={(e) => {
setQId(e.currentTarget.id);
setTab(19);
}}
className={style["manage"]}
>
Manage Q&A
</button>
</div>
))}
</div>
</div>
);
}
1 change: 1 addition & 0 deletions frontend/src/pages/Admin/Components/Faq/Q&A/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./QandA";
Loading
Loading