From f7a67eafc9595a536d88cf9d5787b46c2d1417fa Mon Sep 17 00:00:00 2001 From: Enes Furkan Arslan Date: Wed, 20 Dec 2023 09:47:30 +0300 Subject: [PATCH 1/2] moderation pages backend connection Tag selection on moderation-not-jury page is connected with backend with 2 API requests (moderators/my-tags) GET and POST. GET request is executed at every render of the page and POST request is executed every change on tag list. If a user selects more than 5 tags or selects no tags, no request is sent and warning message is shown. The information of member being moderator or not is also taken from backend using profile/myProfile endpoint and necessary page is shown accordingly. On the moderation-not-moderator page when a member clicks apply button a POST request is sent to /moderators/request-promotion end point and a success message to user is shown. Necessary CSS updates of new components are also done. --- .../src/Components/TagSelection/index.jsx | 38 +++--- .../Pages/Moderation/Moderation.module.css | 24 ++++ .../frontend/src/Pages/Moderation/index.jsx | 112 +++++++++++++++++- 3 files changed, 154 insertions(+), 20 deletions(-) diff --git a/prediction-polls/frontend/src/Components/TagSelection/index.jsx b/prediction-polls/frontend/src/Components/TagSelection/index.jsx index 04818704..33620e76 100644 --- a/prediction-polls/frontend/src/Components/TagSelection/index.jsx +++ b/prediction-polls/frontend/src/Components/TagSelection/index.jsx @@ -1,30 +1,38 @@ -import React, { useState } from "react"; +// TagSelection.js +import React, { useState, useEffect } from "react"; import styles from './TagSelection.module.css'; -import { Checkbox } from "antd"; -const mockTags = ["sport", "NBA", "education"]; +const TagSelection = ({ initialTags, onTagChange }) => { + const [tags, setTags] = useState([]); + + useEffect(() => { + // Ensure initialTags is defined before setting the state + if (initialTags) { + setTags(initialTags); + } + }, [initialTags]); -const TagSelection = ({ selectedTags, onTagChange }) => { const handleTagChange = (tag) => { - const updatedTags = selectedTags.includes(tag) - ? selectedTags.filter((selectedTag) => selectedTag !== tag) - : [...selectedTags, tag]; + const updatedTags = tags.map((t) => + t.topic === tag.topic ? { ...t, isSelected: !t.isSelected } : t + ); + setTags(updatedTags); onTagChange(updatedTags); }; return (
-

Select Tags

+

Select Tags:

- {mockTags.map((tag) => ( -
diff --git a/prediction-polls/frontend/src/Pages/Moderation/Moderation.module.css b/prediction-polls/frontend/src/Pages/Moderation/Moderation.module.css index c5a1de3f..28e49db8 100644 --- a/prediction-polls/frontend/src/Pages/Moderation/Moderation.module.css +++ b/prediction-polls/frontend/src/Pages/Moderation/Moderation.module.css @@ -44,6 +44,30 @@ text-align: center; } + .message { + position: fixed; + top: 0; + left: 50%; + transform: translateX(-50%); + padding: 10px; + background-color: #4caf50; + color: #fff; + text-align: center; + z-index: 1000; + } + + .message2 { + position: fixed; + top: 0; + left: 50%; + transform: translateX(-50%); + padding: 10px; + background-color: var(--warning-400); + color: #fff; + text-align: center; + z-index: 1000; + } + .text { font-size: 20px; font-weight: 700; diff --git a/prediction-polls/frontend/src/Pages/Moderation/index.jsx b/prediction-polls/frontend/src/Pages/Moderation/index.jsx index 1b294a1e..3a06b146 100644 --- a/prediction-polls/frontend/src/Pages/Moderation/index.jsx +++ b/prediction-polls/frontend/src/Pages/Moderation/index.jsx @@ -3,7 +3,6 @@ import React, { useEffect } from "react"; import Menu from "../../Components/Menu"; import styles from "./Moderation.module.css"; import { Button } from "antd"; -import SearchBar from "../../Components/SearchBar"; import { useNavigate } from "react-router-dom"; import PollTag from "../../Components/PollTag"; import PointsButton from "../../Components/PointsButton"; @@ -16,9 +15,78 @@ function Moderation() { const [userData, setUserData] = useState({}); const [moderatorPosts, setModeratorPosts] = useState([]); const [selectedTags, setSelectedTags] = useState([]); + const [message, setMessage] = useState(null); + const [tags, setTags] = useState([]); - const handleTagChange = (tags) => { - setSelectedTags(tags); + const showMessage = (text) => { + setMessage(text); + + // Automatically hide the message after a certain duration (e.g., 3000 milliseconds) + setTimeout(() => { + setMessage(null); + }, 9000); + }; + + useEffect(() => { + // Make a GET request to fetch available tags from the backend + const fetchData = async () => { + try { + const response = await fetch(url + "/moderators/my-tags", { + method: "GET", + headers: { + Authorization: `Bearer ${localStorage.getItem("accessToken")}`, + "Content-Type": "application/json", + }, + }); + + const result = await response.json(); + setTags(result); + } catch (error) { + console.error('Error fetching tags:', error.message); + } + }; + + fetchData(); + }, []); + + const handleTagChange = (updatedTags) => { + // This function will be called whenever a tag is selected/unselected + // You can use the updatedTags array to track the current state of tags + + const selectedCount = updatedTags.reduce((count, tag) => (tag.isSelected ? count + 1 : count), 0); + + if (selectedCount === 0 || selectedCount > 5) { + showMessage('Please select at least one tag and at most 5 tags.'); + return; + } + + const updatedTagsWithNumbers = updatedTags.map((tag) => ({ + topic: tag.topic, + isSelected: tag.isSelected ? 1 : 0, + })); + + const postUpdatedTags = async () => { + try { + const response = await fetch(url + '/moderators/my-tags', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem("accessToken")}`, + }, + body: JSON.stringify(updatedTagsWithNumbers), + }); + + if (!response.ok) { + throw new Error('Network response was not ok'); + } + + // Handle success + } catch (error) { + console.error('Error updating tags:', error.message); + } + }; + + postUpdatedTags(); }; useEffect(() => { @@ -52,10 +120,33 @@ function Moderation() { fetchData(); }, []); - const isModerator = true; + const isModerator = userData?.isMod; const handleBecomeModerator = () => { console.log("User wants to become a moderator"); + const fetchData = async () => { + try { + const response = await fetch(url + "/moderators/request-promotion", { + method: "POST", + headers: { + Authorization: `Bearer ${localStorage.getItem("accessToken")}`, + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + throw new Error("Network response was not ok"); + } + + showMessage('Your application to become a moderator has been submitted successfully.'); + + } catch (error) { + console.error("Error fetching data:", error.message); + } + }; + + // Call the function to fetch data + fetchData(); }; const handleBecomeJury = () => { @@ -99,11 +190,16 @@ function Moderation() {
+ {message && ( +
+ {message} +
+ )} ) : ( @@ -119,7 +215,13 @@ function Moderation() { > Apply +
+ {message && ( +
+ {message} +
+ )} )} From 7d01981bfa6b3542923aef3b063532954eeee709 Mon Sep 17 00:00:00 2001 From: Enes Furkan Arslan Date: Wed, 20 Dec 2023 21:28:57 +0300 Subject: [PATCH 2/2] backend connection updates I was getting error message from the /moderators/my-tags POST endpoint because I was trying to send all of the tag information,however only the last changed one was expected. It is fixed in this commit. Also now I am rendering the posts with another GET request to endpoint /moderators/my-requests after a change occurs on the tags. --- .../frontend/src/Pages/Moderation/index.jsx | 110 ++++++++++++++---- 1 file changed, 90 insertions(+), 20 deletions(-) diff --git a/prediction-polls/frontend/src/Pages/Moderation/index.jsx b/prediction-polls/frontend/src/Pages/Moderation/index.jsx index 3a06b146..d6f94c7d 100644 --- a/prediction-polls/frontend/src/Pages/Moderation/index.jsx +++ b/prediction-polls/frontend/src/Pages/Moderation/index.jsx @@ -17,6 +17,7 @@ function Moderation() { const [selectedTags, setSelectedTags] = useState([]); const [message, setMessage] = useState(null); const [tags, setTags] = useState([]); + const [prevTags, setPrevTags] = useState(null); const showMessage = (text) => { setMessage(text); @@ -41,6 +42,7 @@ function Moderation() { const result = await response.json(); setTags(result); + setPrevTags(result); } catch (error) { console.error('Error fetching tags:', error.message); } @@ -65,28 +67,46 @@ function Moderation() { isSelected: tag.isSelected ? 1 : 0, })); - const postUpdatedTags = async () => { - try { - const response = await fetch(url + '/moderators/my-tags', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${localStorage.getItem("accessToken")}`, - }, - body: JSON.stringify(updatedTagsWithNumbers), - }); - - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - // Handle success - } catch (error) { - console.error('Error updating tags:', error.message); - } - }; + let lastUpdatedTag; + if (prevTags) { + lastUpdatedTag = updatedTags.find((tag) => { + const prevTag = prevTags.find((prev) => prev.topic === tag.topic); + return prevTag && prevTag.isSelected !== tag.isSelected; + }); + } + + + const postUpdatedTags = async () => { + try { + const response = await fetch(url + '/moderators/my-tags', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem("accessToken")}`, + }, + body: JSON.stringify({ + topic: lastUpdatedTag.topic, + isSelected: lastUpdatedTag.isSelected ? 1 : 0, + }), + }); + + if (!response.ok) { + throw new Error('Network response was not ok'); + } + + // Handle success + } catch (error) { + console.error('Error updating tags:', error.message); + } + }; postUpdatedTags(); + handlePostUpdate(); + + + + // Update the previous state with the current state + setPrevTags(updatedTags); }; useEffect(() => { @@ -120,6 +140,56 @@ function Moderation() { fetchData(); }, []); + const handlePostUpdate = () => { + + const fetchData = async () => { + try { + const response = await fetch(url + "/moderators/my-requests", { + method: "GET", + headers: { + Authorization: `Bearer ${localStorage.getItem("accessToken")}`, + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + throw new Error("Network response was not ok"); + } + + const result = await response.json(); + setModeratorPosts(result); + } catch (error) { + console.error("Error fetching data:", error.message); + } + }; + + // Call the function to fetch data + fetchData(); + + } + + const handleGetTagUpdate = () => { + const fetchData = async () => { + try { + const response = await fetch(url + "/moderators/my-tags", { + method: "GET", + headers: { + Authorization: `Bearer ${localStorage.getItem("accessToken")}`, + "Content-Type": "application/json", + }, + }); + + const result = await response.json(); + setTags(result); + setPrevTags(result); + } catch (error) { + console.error('Error fetching tags:', error.message); + } + }; + + fetchData(); + } + const isModerator = userData?.isMod; const handleBecomeModerator = () => {