From 98e0e5113a73bacab931d11c2aaee68ba090a125 Mon Sep 17 00:00:00 2001 From: balaharisankar Date: Tue, 30 Jul 2024 23:00:43 +0530 Subject: [PATCH 1/2] Upvote and Downvotes fixed --- backend/app.js | 16 +- backend/app/models/question.js | 4 + .../routes/Q&A/question/downvoteQuestion.js | 26 +-- .../app/routes/Q&A/question/upvoteQuestion.js | 2 +- frontend/src/pages/Q&A/Q&A.jsx | 2 +- frontend/src/service/Faq.jsx | 210 +++++++++--------- 6 files changed, 131 insertions(+), 129 deletions(-) diff --git a/backend/app.js b/backend/app.js index 2c6a34c5..ea39cea0 100644 --- a/backend/app.js +++ b/backend/app.js @@ -15,8 +15,20 @@ app.use(express.static('uploads')); // Set security headers app.use(helmet()); +// cookie +app.use(cookieParser()); + // CORS -app.use(cors()); +// app.use(cors()); +app.use(cors({credentials:true,origin:process.env.FRONTEND_URL})); + +app.use(function(req, res, next) { + res.header('Access-Control-Allow-Credentials', true); + res.header('Access-Control-Allow-Origin', process.env.FRONTEND_URL); + res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,UPDATE,OPTIONS'); + res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept'); + next(); +}); // Body Parser app.use(express.json({ limit: '50mb' })); @@ -25,8 +37,6 @@ app.use(express.urlencoded({ limit: '50mb', extended: true })); // Response time app.use(responseTime({ suffix: false })); -// cookie -app.use(cookieParser()); // Use routes app.use('/', routes); diff --git a/backend/app/models/question.js b/backend/app/models/question.js index 43051a07..4fe46114 100644 --- a/backend/app/models/question.js +++ b/backend/app/models/question.js @@ -25,6 +25,10 @@ const questionSchema = new Schema( type: Number, default: 0, }, + downvotes:{ + type:Number, + default:0 + } }, { timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } } ); diff --git a/backend/app/routes/Q&A/question/downvoteQuestion.js b/backend/app/routes/Q&A/question/downvoteQuestion.js index d29b7869..3dcc23b9 100644 --- a/backend/app/routes/Q&A/question/downvoteQuestion.js +++ b/backend/app/routes/Q&A/question/downvoteQuestion.js @@ -6,25 +6,11 @@ const { getVoteCookieName } = require('../../../../helpers/middlewares/cookie'); module.exports = async (req, res, next) => { const { questionId } = req.body; - const [err] = await to( - question.updateOne({ _id: questionId }, [ - { - $set: { - upvotes: { - $cond: [ - { - $gt: ['$upvotes', 0], - }, - { - $subtract: ['$upvotes', 1], - }, - 0, - ], - }, - }, - }, - ]) - ); + const existingQues=await question.findById(questionId) + if(!existingQues.downvotes){ + const [err] = await to(question.updateOne({ _id: questionId },{$set:{downvotes:0}})); + } + const [err] = await to(question.updateOne({ _id: questionId }, { $inc: { downvotes: 1 } })); if (err) { console.log(err); const error = new ErrorHandler(constants.ERRORS.DATABASE, { @@ -36,7 +22,7 @@ module.exports = async (req, res, next) => { return next(error); } - res.cookie(getVoteCookieName('question', questionId), true, { maxAge: 20 * 365 * 24 * 60 * 60 * 1000 }); + res.cookie(getVoteCookieName('question', questionId), true, { maxAge: 20 * 365 * 24 * 60 * 60 * 1000, sameSite: "none", secure: true }); res.status(200).send({ message: 'Question has been down voted', }); diff --git a/backend/app/routes/Q&A/question/upvoteQuestion.js b/backend/app/routes/Q&A/question/upvoteQuestion.js index 77a1b2a2..671dca9d 100644 --- a/backend/app/routes/Q&A/question/upvoteQuestion.js +++ b/backend/app/routes/Q&A/question/upvoteQuestion.js @@ -16,7 +16,7 @@ module.exports = async (req, res, next) => { return next(error); } - res.cookie(getVoteCookieName('question', questionId), true, { maxAge: 20 * 365 * 24 * 60 * 60 * 1000 }); + res.cookie(getVoteCookieName('question', questionId), true, { maxAge: 20 * 365 * 24 * 60 * 60 * 1000,sameSite:"none",secure:true }); res.status(200).send({ message: 'Question has been upvoted', diff --git a/frontend/src/pages/Q&A/Q&A.jsx b/frontend/src/pages/Q&A/Q&A.jsx index d9d2e1bb..0423c21d 100644 --- a/frontend/src/pages/Q&A/Q&A.jsx +++ b/frontend/src/pages/Q&A/Q&A.jsx @@ -191,7 +191,7 @@ function Ques(props) { className="vote-btn" onClick={() => handleDownvote(item._id)} > - 👎 {item?.downvote} + 👎 {item?.downvotes} diff --git a/frontend/src/service/Faq.jsx b/frontend/src/service/Faq.jsx index f23e7c8b..d3ee876c 100644 --- a/frontend/src/service/Faq.jsx +++ b/frontend/src/service/Faq.jsx @@ -2,127 +2,127 @@ import { END_POINT } from "../config/api"; import { showToast } from "./toastService"; export async function postFaq(formData, setToast, toast) { - try { - const response = await fetch(`${END_POINT}/faq/postFaq`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - body: JSON.stringify(formData), + try { + const response = await fetch(`${END_POINT}/faq/postFaq`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${localStorage.getItem("token")}`, + }, + body: JSON.stringify(formData), + }); + + if (response.ok) { + setToast({ + ...toast, + toastMessage: "FAQ has been added", + toastStatus: true, + toastType: "success", }); - - if (response.ok) { - setToast({ - ...toast, - toastMessage: "FAQ has been added", - toastStatus: true, - toastType: "success", - }); - return { success: true }; - } else { - setToast({ - ...toast, - toastMessage: "Database Error", - toastStatus: true, - toastType: "error", - }); - return { success: false, error: "Database Error" }; - } - } catch (error) { + return { success: true }; + } else { setToast({ ...toast, - toastMessage: "Network Error", + toastMessage: "Database Error", toastStatus: true, toastType: "error", }); - return { success: false, error: "Network Error" }; + return { success: false, error: "Database Error" }; } + } catch (error) { + setToast({ + ...toast, + toastMessage: "Network Error", + toastStatus: true, + toastType: "error", + }); + return { success: false, error: "Network Error" }; } +} export async function getFaq() { - try { - const response = await fetch(`${END_POINT}/faq/getFaq`); - if (!response.ok) { - throw new Error("Failed to fetch FAQs"); - } - const data = await response.json(); - return data.Faq; - } catch (error) { - console.error("Failed to fetch FAQs:", error.message); + try { + const response = await fetch(`${END_POINT}/faq/getFaq`); + if (!response.ok) { throw new Error("Failed to fetch FAQs"); } + const data = await response.json(); + return data.Faq; + } catch (error) { + console.error("Failed to fetch FAQs:", error.message); + throw new Error("Failed to fetch FAQs"); + } } -export const deleteFaq = async (faqId, setToast, toast) => { - const url = `${END_POINT}/faq/deleteFaq`; - const body = { faqId: faqId }; - const headers = { - "Content-Type": "application/json", - authorization: `Bearer ${localStorage.getItem("token")}`, - }; - try { - const response = await fetch(url, { - method: "PUT", - headers: headers, - body: JSON.stringify(body), - }); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - const data = await response.json(); - setToast({ - ...toast, - toastMessage: data.message, - toastStatus: true, - toastType: "success", - }); - return data.message; - } catch (error) { - console.error("Failed to delete FAQ:", error.message); - setToast({ - ...toast, - toastMessage: "Failed to delete FAQ", - toastStatus: true, - toastType: "error", - }); - throw new Error("Failed to delete FAQ"); +export const deleteFaq = async (faqId, setToast, toast) => { + const url = `${END_POINT}/faq/deleteFaq`; + const body = { faqId: faqId }; + const headers = { + "Content-Type": "application/json", + authorization: `Bearer ${localStorage.getItem("token")}`, + }; + try { + const response = await fetch(url, { + method: "PUT", + headers: headers, + body: JSON.stringify(body), + }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); } + const data = await response.json(); + setToast({ + ...toast, + toastMessage: data.message, + toastStatus: true, + toastType: "success", + }); + return data.message; + } catch (error) { + console.error("Failed to delete FAQ:", error.message); + setToast({ + ...toast, + toastMessage: "Failed to delete FAQ", + toastStatus: true, + toastType: "error", + }); + throw new Error("Failed to delete FAQ"); + } }; export const updateFaq = async (faqId, updatedFaqDetails, setToast, toast) => { - 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, ...updatedFaqDetails }), - }); - - if (!response.ok) { - throw new Error("Failed to update FAQ"); - } + 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, ...updatedFaqDetails }), + }); - const data = await response.json(); - setToast({ - ...toast, - toastMessage: data.message, - toastStatus: true, - toastType: "success", - }); - return data.message; - } catch (error) { - console.error("Failed to update FAQ:", error.message); - setToast({ - ...toast, - toastMessage: "Failed to update FAQ", - toastStatus: true, - toastType: "error", - }); - throw new Error("Failed to update FAQ"); + if (!response.ok) { + throw new Error("Failed to update FAQ"); } + + const data = await response.json(); + setToast({ + ...toast, + toastMessage: data.message, + toastStatus: true, + toastType: "success", + }); + return data.message; + } catch (error) { + console.error("Failed to update FAQ:", error.message); + setToast({ + ...toast, + toastMessage: "Failed to update FAQ", + toastStatus: true, + toastType: "error", + }); + throw new Error("Failed to update FAQ"); + } }; export const getAllQuestions = async (setToast, toast) => { @@ -292,6 +292,7 @@ export const upvote = async (questionId, handleToast) => { headers: { "Content-Type": "application/json", }, + credentials: "include", body: JSON.stringify({ questionId }), }); if (!response.ok) { @@ -300,7 +301,7 @@ export const upvote = async (questionId, handleToast) => { showToast(handleToast, "Upvote Successfully"); return response.json(); } catch (error) { - showToast(handleToast, "Failed to upvote question", "error"); + showToast(handleToast, "You have already voted", "error"); throw new Error("Failed to upvote question"); } }; @@ -312,6 +313,7 @@ export const downvote = async (questionId, handleToast) => { headers: { "Content-Type": "application/json", }, + credentials: "include", body: JSON.stringify({ questionId }), }); if (!response.ok) { @@ -320,7 +322,7 @@ export const downvote = async (questionId, handleToast) => { showToast(handleToast, "Downvote Successfully"); return response.json(); } catch (error) { - showToast(handleToast, "Failed to downvote question", "error"); + showToast(handleToast, "You have already voted", "error"); throw new Error("Failed to downvote question"); } -}; \ No newline at end of file +}; From 6ba7b02e215d7ab78426eb8bbf25ab24b3fd4d8f Mon Sep 17 00:00:00 2001 From: balaharisankar Date: Wed, 7 Aug 2024 18:48:14 +0530 Subject: [PATCH 2/2] JoinUs functionality fixed --- .../pages/Home/components/JoinUsForm/Form.jsx | 697 +++++++++--------- .../components/JoinUsForm/form.module.scss | 69 +- frontend/src/service/JoinUs.jsx | 32 + 3 files changed, 447 insertions(+), 351 deletions(-) create mode 100644 frontend/src/service/JoinUs.jsx diff --git a/frontend/src/pages/Home/components/JoinUsForm/Form.jsx b/frontend/src/pages/Home/components/JoinUsForm/Form.jsx index a254277a..05fa566c 100644 --- a/frontend/src/pages/Home/components/JoinUsForm/Form.jsx +++ b/frontend/src/pages/Home/components/JoinUsForm/Form.jsx @@ -1,6 +1,8 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import Joi from "joi-browser"; import MultiSelect from "react-multi-select-component"; +import { SimpleToast } from "../../../../components/util/Toast"; +import { postJoinUs, PostJoinUs } from '../../../../service/JoinUs'; import styles from "./form.module.scss"; import { Button2 } from "../../../../components/util/Button/index"; @@ -9,16 +11,18 @@ export const JoinUsForm = (props) => { const [formdata, setFormData] = useState({ name: "", - phone: "", + contact: "", email: "", - link: "", - desc: "", - other: "", - dept: "", + linkdin: "", + description: "", + otherDomain: "", + department: "", year: null, college: "", }); + const [isSubmitted, setIsSubmitted] = useState(false) + const options = [ { label: "Machine Learning", value: "ml" }, { label: "Artificial Intelligence", value: "ai" }, @@ -36,16 +40,21 @@ export const JoinUsForm = (props) => { const [domainError, setDomainError] = useState(); const [formerrors, setFormErrors] = useState({}); + const [toast, setToast] = useState({ + toastStatus: false, + toastType: "", + toastMessage: "", + }); const schema = { name: Joi.string().required(), - phone: Joi.number().allow(""), - email: Joi.string().email().required(), - link: Joi.string().uri().allow(""), - desc: Joi.string().allow(""), - other: Joi.string().allow(""), - dept: Joi.string().allow(""), - year: Joi.number().required(), + contact: Joi.number().required(), + email: Joi.string().email().required().required(), + linkdin: Joi.string().uri().required(), + description: Joi.string().required(), + otherDomain: Joi.string(), + department: Joi.string().required(), + year: Joi.number().required().required(), college: Joi.string().required(), }; @@ -80,7 +89,7 @@ export const JoinUsForm = (props) => { setFormErrors(errors); }; - const handleSubmit = (e) => { + const handleSubmit = async (e) => { e.preventDefault(); const errors = validate(); console.log(errors); @@ -91,375 +100,401 @@ export const JoinUsForm = (props) => { } if (errors === null || Object.keys(errors).length === 0) { setFormErrors({}); - console.log("Submitted", formdata); + let data = { ...formdata, interestedDomain: domains.map((value)=>value.label) } + let res = await postJoinUs(data, setToast) + if (res == true) + setIsSubmitted(true) } else { setFormErrors(errors); + setToast({ toastStatus: true, toastMessage: "Fill all the fields", toastType: "error" }) } }; - console.log("form error: ", formerrors); - console.log("form data: ", formdata, domains); - return ( -
-
- -
+ <>
+
+ +
-

- Join Us Form -

-
-
-
-
-
- - -
- {formerrors["name"] ? ( -
* {formerrors["name"]}
- ) : ( -
   
- )} -
-
-
-
-
- - -
- {formerrors["phone"] ? ( -
* {formerrors["phone"]}
- ) : ( -
   
- )} -
-
-
-
-
-
- - -
- {formerrors["email"] &&
* {formerrors["email"]}
} -
-
-
-
-
- - -
- {formerrors["link"] ? ( -
* {formerrors["link"]}
- ) : ( -
   
- )} + { + isSubmitted ? + +
+

Hello There !

+
+

+ Your request is in process! 😄
+ we will get back to you soon! +

-
+ + :
- - -
- {formerrors["desc"] ? ( -
* {formerrors["desc"]}
- ) : ( -
   
- )} -
-
-
- - - -
- {domainError ? ( -
{domainError}
- ) : ( -
   
- )} -
-
-
-
- - -
-
-
-
- - -
-
-
- -
-
-
- - + Join Us Form + + +
+
+
+
+ + +
+ {formerrors["name"] ? ( +
* {formerrors["name"]}
+ ) : ( +
   
+ )} +
+
+
+
+
+ + +
+ {formerrors["phone"] ? ( +
* {formerrors["phone"]}
+ ) : ( +
   
+ )} +
+
+
+
+
+
+ + +
+ {formerrors["email"] &&
* {formerrors["email"]}
} +
+
+
+
+
+ + +
+ {formerrors["link"] ? ( +
* {formerrors["link"]}
+ ) : ( +
   
+ )} +
+
- - + > + +
+ {formerrors["desc"] ? ( +
* {formerrors["desc"]}
+ ) : ( +
   
+ )} +
- + Interested Domains + + - + +
+ {domainError ? ( +
{domainError}
+ ) : ( +
   
+ )} +
+
+
+
+ + +
+
+
+
+ + +
- + Year of Study + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ {formerrors["year"] ? ( +
* {formerrors["year"]}
+ ) : ( +
   
+ )} +
+
+
+
+ + +
+ {formerrors["college"] ? ( +
* {formerrors["college"]}
+ ) : ( +
   
+ )} +
+
+
+
+ -
-
-
- {formerrors["year"] ? ( -
* {formerrors["year"]}
- ) : ( -
   
- )} -
-
-
-
- - -
- {formerrors["college"] ? ( -
* {formerrors["college"]}
- ) : ( -
   
- )} -
-
+
-
- -
-
- + }
-
+ {toast.toastStatus && ( + { setToast({ toastStatus: false, toastMessage: "", toastType: '' }) }} + severity={toast.toastType} + /> + )} + ); }; diff --git a/frontend/src/pages/Home/components/JoinUsForm/form.module.scss b/frontend/src/pages/Home/components/JoinUsForm/form.module.scss index 448c06de..435cf9ea 100644 --- a/frontend/src/pages/Home/components/JoinUsForm/form.module.scss +++ b/frontend/src/pages/Home/components/JoinUsForm/form.module.scss @@ -201,9 +201,12 @@ --rmsc-border: #ccc !important; --rmsc-gray: #aaa !important; --rmsc-bg: #333333 !important; - --rmsc-p: 10px !important; /* Spacing */ - --rmsc-radius: 4px !important; /* Radius */ - --rmsc-h: 38px !important; /* Height */ + --rmsc-p: 10px !important; + /* Spacing */ + --rmsc-radius: 4px !important; + /* Radius */ + --rmsc-h: 38px !important; + /* Height */ } .dropdown { @@ -211,9 +214,12 @@ --rmsc-selected: #474343 !important; --rmsc-border: #838383 !important; --rmsc-bg: #e7e7e7 !important; - --rmsc-p: 10px !important; /* Spacing */ - --rmsc-radius: 4px !important; /* Radius */ - --rmsc-h: 38px !important; /* Height */ + --rmsc-p: 10px !important; + /* Spacing */ + --rmsc-radius: 4px !important; + /* Radius */ + --rmsc-h: 38px !important; + /* Height */ } .valid { @@ -284,7 +290,7 @@ background-color: transparent; } -.checkbox-item input[type="checkbox"]:checked + label:after { +.checkbox-item input[type="checkbox"]:checked+label:after { width: 20px; height: 20px; position: absolute; @@ -292,11 +298,9 @@ left: 8px; content: " "; display: block; - background-image: linear-gradient( - to right, - rgba(255, 0, 90, 1), - rgba(10, 24, 61, 1) - ); + background-image: linear-gradient(to right, + rgba(255, 0, 90, 1), + rgba(10, 24, 61, 1)); border: 4px solid whitesmoke; } @@ -307,7 +311,7 @@ margin-bottom: 10px; } -.radio-item input[type="radio"]:checked + label:after { +.radio-item input[type="radio"]:checked+label:after { border-radius: 50%; width: 20px; height: 20px; @@ -319,12 +323,10 @@ border: 4px solid whitesmoke; } -.radio-item-light input[type="radio"]:checked + label:after { - background-image: linear-gradient( - to right, - rgba(255, 0, 90, 1), - rgba(10, 24, 61, 1) - ); +.radio-item-light input[type="radio"]:checked+label:after { + background-image: linear-gradient(to right, + rgba(255, 0, 90, 1), + rgba(10, 24, 61, 1)); } .validation { @@ -333,7 +335,7 @@ } // for dark theme -.radio-item-dark input[type="radio"]:checked + label:after { +.radio-item-dark input[type="radio"]:checked+label:after { background-image: linear-gradient(to right, #2b5876, #4e4376); } @@ -481,3 +483,30 @@ input[type="date"]:not(:valid):before { text-align: left; } } + +.goodbye-card { + width: 60%; + height: auto; + background-color: #e7e7e7; + margin: 20% auto; + border-radius: 10px; + box-shadow: 5px 5px 15px #888888, -5px -5px 15px #ffffff; +} + +.card-heading { + font-size: 1.6rem; + margin-bottom: 3px; + padding: 9px 0; + text-align: center; +} + +.inside-card { + width: 75%; + margin: 0 auto; + padding-bottom: 32px; +} + +.inside-card p { + padding: 0px; + font-size: 1.3rem; +} \ No newline at end of file diff --git a/frontend/src/service/JoinUs.jsx b/frontend/src/service/JoinUs.jsx new file mode 100644 index 00000000..6f770c32 --- /dev/null +++ b/frontend/src/service/JoinUs.jsx @@ -0,0 +1,32 @@ +import { END_POINT } from "../config/api"; +import { showToast } from "./toastService"; + +const postJoinUs = async (data, setToast) => { + try { + showToast(setToast,"Loading...","info") + const response = await fetch(`${END_POINT}/joinUs/`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + + if (response.ok) { + const _data = await response.json(); + showToast(setToast, "Form submitted successfully!", "success"); + return true; + } else { + const errorData = await response.json(); + console.error("Error:", errorData); + showToast(setToast, "Something went wrong", "error"); + return false; + } + } catch (err) { + console.error("Network Error:", err); + showToast(setToast, "Something went wrong", "error"); + return false; + } + }; + + export { postJoinUs }; \ No newline at end of file